/*  Inti: Integrated Foundation Classes
 *  Copyright (C) 2002-2003 The Inti Development Team.
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Library General Public License for more details.
 *
 *  You should have received a copy of the GNU Library General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */
 
//! @file inti/gdk/keys.h
//! @brief A GdkKeymap C++ wrapper interface.
//!
//! Provides Keymap and KeymapKey, two objects that can be used for manipulating keyboard codes.

#ifndef INTI_GDK_KEYS_H
#define INTI_GDK_KEYS_H

#ifndef INTI_G_OBJECT_H
#include <inti/glib/object.h>
#endif

#ifndef INTI_GDK_TYPES_H
#include <inti/gdk/types.h>
#endif

#ifndef INTI_PANGO_TYPES_H
#include <inti/pango/types.h>
#endif

#ifndef _CPP_VECTOR
#include <vector>
#endif

namespace Inti {

namespace Gdk {

class Display;
class KeymapClass;
class Keyval;

//! @class KeymapKey keys.h inti/gdk/keys.h
//! @brief A GdkKeymapKey C++ wrapper class.
//!
//! KeymapKey represents a hardware key that can be mapped to a keyval.

class KeymapKey
{
	GdkKeymapKey key_;

public:
//! @name Constructors
//! @{

	KeymapKey();
	//!< Construct an empty KeymapKey (see Gdk::KeymapKey::set() for details).

	KeymapKey(unsigned int keycode, int group, int level);
	//!< Construct a new KeymapKey (see Gdk::KeymapKey::set() for details).
	//!< @param keycode The hardware keycode; this is an identifying number for a physical key.
	//!< @param group Indicates movement in a horizontal direction.
	//!< @param level Indicates which symbol on the key will be used, in a vertical direction.

//! @}
//! @name Accessors
//! @{

	GdkKeymapKey* gdk_keymap_key() const { return const_cast<GdkKeymapKey*>(&key_); }
	//!< Get a pointer to the GdkKeymapKey structure.
	
	unsigned int keycode() const;
	//!< Returns the hardware key code. The keycode is an identifying number for a physical key.

	int group() const;
	//!< Returns the group. The group indicates movement in a horizontal direction.

	int level() const;
	//!< Returns the level. The level indicates which symbol on the key will be used,
	//!< in a vertical direction.

//! @name Methods
//! @{

	void set(unsigned int keycode, int group, int level);
	//!< Sets the keycode, group and level for the key.
	//!< @param keycode The hardware keycode; this is an identifying number for a physical key.
	//!< @param group Indicates movement in a horizontal direction.
	//!< @param level Indicates which symbol on the key will be used, in a vertical direction.
	//!<
	//!< <BR>On a standard US keyboard, the key with the number "1" on it also has the
	//!< exclamation point ("!") character on it. The level indicates whether to use the "1"
	//!< or the "!" symbol. The letter keys are considered to have a lowercase letter at level 0,
	//!< and an uppercase letter at level 1, though only the uppercase letter is printed.
	//!<
	//!< Usually groups are used for two different languages. In group 0, a key might have
	//!< two English characters, and in group 1 it might have two Hebrew characters. The Hebrew
	//!< characters will be printed on the key next to the English characters.

//! @}
};

//! @class Keymap keys.h inti/gdk/keys.h
//! @brief A GdkKeymap C++ wrapper class.
//!
//! Key values are the codes which are sent whenever a key is pressed or released. They appear in
//! the keyval field of the GdkEventKey structure. They are also returned by the keyval() accessor
//! in the Gdk::EventKey object, which is passed to the virutal signal handlers for the "key-press"
//! and "key-release" event signals. Examples of key values are GDK_a, GDK_Enter, GDK_F1, etc.
//! The complete list of key values can be found in the <gdk/gdkkeysyms.h> header file. This file
//! is not included by Inti, it must be included independently, because the file is quite large.
//!
//! The Keyval object represents a GDK key value. A key value can be converted into a 
//! string representation with Gdk::Keyval::name(). The reverse, a Keyval can be constructed
//! from string. The case of key values can be determined using Gdk::Keyval::is_upper() and
//! Gdk::Keyval::is_lower(). Key values can be converted to upper or lower case using 
//! Gdk::Keyval::to_upper() and Gdk::Keyval::to_lower(). When it makes sense, key values can
//! be converted to and from Unicode characters with Gdk::Keyval::to_unicode() and 
//! Gdk::Keyval::from_unicode().
//!
//! One Gdk::Keymap object exists for each user display. Gdk::Keymap::get() returns the keymap
//! for the specified display, or the default display if no display is specified. A keymap is a
//! mapping from Gdk::KeymapKey to key values. You can think of a Gdk::KeymapKey as a 
//! representation of a symbol printed on a physical keyboard key. That is, it contains three
//! pieces of information. First, it contains the hardware keycode; this is an identifying number
//! for a physical key. Second, it contains the level of the key. The level indicates which
//! symbol on the key will be used, in a vertical direction. So on a standard US keyboard, the
//! key with the number "1" on it also has the exclamation point ("!") character on it. The level
//! indicates whether to use the "1" or the "!" symbol. The letter keys are considered to have a
//! lowercase letter at level 0, and an uppercase letter at level 1, though only the uppercase 
//! letter is printed. Third, the Gdk::KeymapKey contains a group; groups are not used on standard
//! US keyboards, but are used in many other countries. On a keyboard with groups, there can be 
//! 3 or 4 symbols printed on a single key. The group indicates movement in a horizontal direction.
//! Usually groups are used for two different languages. In group 0, a key might have two English
//! characters, and in group 1 it might have two Hebrew characters. The Hebrew characters will be
//! printed on the key next to the English characters.
//!
//! In order to use a keymap to interpret a key event, it's necessary to first convert the keyboard
//! state into an effective group and level. This is done via a set of rules that varies widely 
//! according to type of keyboard and user configuration. The method translate_keyboard_state()
//! accepts a keyboard state -- consisting of hardware keycode pressed, active modifiers, and 
//! active group -- applies the appropriate rules, and returns the group/level to be used to index
//! the keymap, along with the modifiers which did not affect the group and level. i.e. it returns
//! "unconsumed modifiers." The keyboard group may differ from the effective group used for keymap
//! lookups because some keys don't have multiple groups - e.g. the Enter key is always in group 0
//! regardless of keyboard state.
//!
//! Note that translate_keyboard_state() also returns the keyval, i.e. it goes ahead and performs
//! the keymap lookup in addition to telling you which effective group/level values were used for
//! the lookup. Gdk::EventKey already contains this keyval, however, so you don't normally need
//! to call translate_keyboard_state() just to get the keyval.

class Keymap : public G::Object
{
	friend class G::Object;
	friend class KeymapClass;

	Keymap(const Keymap&);
	Keymap& operator=(const Keymap&);
	
protected:
//! @name Constructors
//! @{

	explicit Keymap(GdkKeymap *keymap, bool reference = true);
	//!< Construct a new Keymap from an existing GdkKeymap.
	//!< @param keymap A pointer to a GdkKeymap.
	//!< @param reference Set false if the initial reference count is floating, set true if it's not.
	//!<
	//!< <BR>The <EM>keymap</EM> can be a newly created GdkKeymap or an existing
	//!< GdkKeymap. (see G::Object::Object).

//! @}
//! @name Signal Handlers
//! @{

	virtual void on_direction_changed();
	//!< Called when the direction of the keymap changes;

	virtual void on_keys_changed();
	//!< Called when the keyboard mapping changes.

//! @}
//  Signals

	typedef G::Signal0<void> DirectionChangedSignalType;
	typedef G::SignalProxy<TypeInstance, DirectionChangedSignalType> DirectionChangedSignalProxy;
	static const DirectionChangedSignalType direction_changed_signal;

	typedef G::Signal0<void> KeysChangedSignalType;
	typedef G::SignalProxy<TypeInstance, KeysChangedSignalType> KeysChangedSignalProxy;
	static const KeysChangedSignalType keys_changed_signal;

public:
//! @name Constructors
//! @{

	virtual ~Keymap();
	//!< Destructor.
	
//! @}

	static Keymap* get(const Display *display = 0);
	//!< Gets the GdkKeymap attached to <EM>display</EM>.
	//!< @param display The display to use, or null for the default display.
	//!< @return The keymap attached to <EM>display</EM>.

//! @name Accessors
//! @{
	
	GdkKeymap* gdk_keymap() const { return (GdkKeymap*)instance; }
	//!< Get a pointer to the GdkKeymap structure.

	GdkKeymapClass* gdk_keymap_class() const;
	//!< Get a pointer to the GdkKeymapClass structure.

	operator GdkKeymap* () const;
	//!< Conversion operator; safely converts a Keymap to a GdkKeymap pointer.

	Pango::Direction get_direction() const;
	//!< Returns the direction of the keymap, either Pango::DIRECTION_LTR or Pango::DIRECTION_RTL.
	
//! @}
//! @name Methods
//! @{

	Keyval lookup_key(const KeymapKey& key);
	//!< Looks up the keyval mapped to <EM>key</EM> (keycode/group/level triplet). 
	//!< @param key A KeymapKey with keycode, group, and level initialized.
	//!< @returns A Keyval, or 0 if none was mapped to the given key.
	//!<
	//!< <BR>If no keyval is bound to key, returns 0. For normal user input, you want
	//!< to use translate_keyboard_state() instead of this method, since the effective
	//!< group/level may not be the same as the current keyboard state.

	bool translate_keyboard_state
	(
		unsigned int hardware_keycode,
		ModifierTypeField state,
		int group,
		Keyval& keyval,
		int *effective_group,
		int *level,
		ModifierTypeField *consumed_modifiers
	);
	//!< Translates the contents of an EventKey into a keyval, effective group, and level.
	//!< @param hardware_keycode A keycode.
	//!< @param state A modifier state.
	//!< @param group The active keyboard group.
	//!< @param keyval The return location for the keyval.
	//!< @param effective_group The return location for the effective group.
	//!< @param level The return location for the level.
	//!< @param consumed_modifiers The return location for modifiers that were
	//!<                           used to determine the group or level.
	//!< @return <EM>true</EM> if there was a keyval bound to the keycode/state/group.
	//!<
	//!< <BR>Modifiers that affected the translation and are thus unavailable for application
	//!< use are returned in consumed_modifiers. See Gdk::Keyval::get_keys() for an explanation
	//!< of groups and levels. The <EM>effective_group</EM> is the group that was actually used
	//!< for the translation; some keys such as Enter are not affected by the active keyboard
	//!< group. The <EM>level</EM> is derived from state. For convenience, EventKey already
	//!< contains the translated keyval, so this function isn't as useful as you might think.

	bool get_entries_for_keyval(const Keyval& keyval, std::vector<KeymapKey>& keys);
	//!< Obtains a list of KeymapKey (keycode/group/level combinations) that
	//!< will generate the specified keyval.
	//!< @param keyval A keyval, such as GDK_a, GDK_Up, GDK_Return, etc.
	//!< @param keys A reference to a vector of KeymapKey to hold the list of keys.
	//!< @return <EM>true</EM> if keys were found and returned.
	//!<
	//!< <BR>Groups and levels are two kinds of keyboard mode; in general, the level
	//!< determines whether the top or bottom symbol on a key is used, and the group
	//!< determines whether the left or right symbol is used. On US keyboards, the 
	//!< shift key changes the keyboard level, and there are no groups. A group switch
	//!< key might convert a keyboard between Hebrew to English modes, for example. 
	//!< EventKey contains a group field that indicates the active keyboard group. The
	//!< level is computed from the modifier mask.

	bool get_entries_for_keycode(unsigned int hardware_keycode, std::vector<KeymapKey> *keys, std::vector<Keyval> *keyvals);
	//!< Returns the keyvals bound to <EM>hardware_keycode</EM>.
	//!< @param hardware_keycode A keycode.
	//!< @param keys A reference to a vector of KeymapKey to hold the keys, or null.
	//!< @param keyvals A reference to a vector of Keyval to hold the key values, or null.
	//!< @return <EM>true</EM> if there were any entries returned in keys and/or keyvals.
	//!<
	//!< <BR> When a keycode is pressed by the user, the keyval from this list of entries
	//!< is selected by considering the effective keyboard group and level. See
	//!< translate_keyboard_state().

//! @}
//! @name Signal Proxies
//! @{

	const DirectionChangedSignalProxy sig_direction_changed()
	{
		return DirectionChangedSignalProxy(this, &direction_changed_signal);
	}
	//!< Connect to the direction_changed_signal; emitted when the direction of the keymap changes;

	const KeysChangedSignalProxy sig_keys_changed()
	{
		return KeysChangedSignalProxy(this, &keys_changed_signal);
	}
	//!< Connect to the keys_changed_signal; emitted when the keyboard mapping changes.
	
//! @}
};

} // namespace Gdk

} // namespace Inti

#endif // INTI_GDK_KEYS_H

