## gok-keyboard

gok-keyboard

## Synopsis

    enum                KeyboardLayouts;
    enum                KeyboardTypes;
    GokKeyboard*        gok_keyboard_new                    (void);
    GokKeyboard*        gok_keyboard_read                   (const gchar *Filename);
    void                gok_keyboard_delete                 (GokKeyboard *pKeyboard,
                                                             gboolean bForce);
    void                gok_keyboard_delete_key             (GokKey *pKey,
                                                             GokKeyboard *pKeyboard);
    void                gok_keyboard_count_rows_columns     (GokKeyboard *pKeyboard);
    gint                gok_keyboard_get_number_rows        (GokKeyboard *pKeyboard);
    gint                gok_keyboard_get_number_columns     (GokKeyboard *pKeyboard);
    gboolean            gok_keyboard_add_keys               (GokKeyboard *pKeyboard,
                                                             xmlDoc *pDoc);
    gchar*              gok_keyboard_get_name               (GokKeyboard *pKeyboard);
    void                gok_keyboard_set_name               (GokKeyboard *pKeyboard,
                                                             gchar *Name);
    gboolean            gok_keyboard_get_supports_wordcomplete
                                                            (GokKeyboard *pKeyboard);
    void                gok_keyboard_set_wordcomplete_keys_added
                                                            (GokKeyboard *pKeyboard,
                                                             gboolean bTrueFalse);
    gboolean            gok_keyboard_get_wordcomplete_keys_added
                                                            (GokKeyboard *pKeyboard);
    void                gok_keyboard_set_commandpredict_keys_added
                                                            (GokKeyboard *pKeyboard,
                                                             gboolean bTrueFalse);
    gboolean            gok_keyboard_get_commandpredict_keys_added
                                                            (GokKeyboard *pKeyboard);
    gboolean            gok_keyboard_display                (GokKeyboard *pKeyboard,
                                                             GokKeyboard *pKeyboardCurrent,
                                                             GtkWidget *pWindowMain,
                                                             gboolean CallbackScanner);
    xmlNode*            gok_keyboard_find_node              (xmlNode *pNode,
                                                             gchar *NameNode);
    void                gok_keyboard_position_keys          (GokKeyboard *pKeyboard,
                                                             GtkWidget *pWindow);
    void                gok_keyboard_initialize             (void);
    void                gok_key_delete                      (GokKey *pKey,
                                                             GokKeyboard *pKeyboard,
                                                             gboolean bDeleteButton);
    GokKey*             gok_key_new                         (GokKey *pKeyPrevious,
                                                             GokKey *pKeyNext,
                                                             GokKeyboard *pKeyboard);
    gboolean            gok_keyboard_layout                 (GokKeyboard *pKeyboard,
                                                             KeyboardLayouts layout,
                                                             KeyboardShape shape,
                                                             gboolean force);
    gboolean            gok_keyboard_branch_gui             (AccessibleNode *pNodeAccessible,
                                                             GokSpySearchType type);
    gboolean            gok_keyboard_branch_gui_actions     (GokKeyboard *keyboard,
                                                             AccessibleNode *pNodeAccessible,
                                                             gint action_ndx);
    gboolean            gok_keyboard_branch_edittext        (void);
    gboolean            gok_chunker_chunk                   (GokKeyboard *pKeyboard);
    void                gok_chunker_initialize              (void);
    gboolean            gok_chunker_chunk_rows_ttb          (GokKeyboard *pKeyboard,
                                                             gint ChunkOrder);
    gboolean            gok_chunker_chunk_rows_btt          (GokKeyboard *pKeyboard,
                                                             gint ChunkOrder);
    gboolean            gok_chunker_chunk_cols_ltr          (GokKeyboard *pKeyboard,
                                                             gint ChunkOrder);
    gboolean            gok_chunker_chunk_cols_rtl          (GokKeyboard *pKeyboard,
                                                             gint ChunkOrder);
    gboolean            gok_chunker_chunk_recursive         (GokKeyboard *pKeyboard,
                                                             gint ChunkOrder,
                                                             gint Groups);
    GokKey*             gok_chunker_find_center             (GokKeyboard *pKeyboard,
                                                             gint centerRow,
                                                             gint centerColumn,
                                                             gint *pRowsDistant,
                                                             gint *pColumnsDistant);
    GokKey*             gok_keyboard_output_selectedkey     (void);
    gboolean            gok_keyboard_validate_dynamic_keys  (Accessible *pAccessibleForeground);
    void                gok_keyboard_fill_row               (GokKeyboard *pKeyboard,
                                                             gint RowNumber);
    void                gok_keyboard_insert_array           (GokKey *pKey);
    void                gok_keyboard_on_window_resize       (void);
    gint                gok_keyboard_get_keywidth_for_window
                                                            (gint WidthWindow,
                                                             GokKeyboard *pKeyboard);
    gint                gok_keyboard_get_keyheight_for_window
                                                            (gint HeightWindow,
                                                             GokKeyboard *pKeyboard);
    void                gok_keyboard_set_ignore_resize      (gboolean bFlag);
    Accessible*         gok_keyboard_get_accessible         (GokKeyboard *pKeyboard);
    void                gok_keyboard_set_accessible         (GokKeyboard *pKeyboard,
                                                             Accessible *pAccessible);
    void                gok_keyboard_calculate_font_size    (GokKeyboard *pKeyboard);
    void                gok_keyboard_calculate_font_size_group
                                                            (GokKeyboard *pKeyboard,
                                                             gint GroupNumber,
                                                             gboolean bOverride);
    gboolean            gok_keyboard_update_dynamic         (GokKeyboard *pKeyboard);
    void                gok_keyboard_update_labels          (void);

## Description

## Details

### enum KeyboardLayouts

    typedef enum {
    KEYBOARD_LAYOUT_NORMAL,
    KEYBOARD_LAYOUT_QWERTY,
    KEYBOARD_LAYOUT_CENTER,
    KEYBOARD_LAYOUT_UPPERL,
    KEYBOARD_LAYOUT_UNSPECIFIED
    } KeyboardLayouts;

### enum KeyboardTypes

    typedef enum {
    KEYBOARD_TYPE_PLAIN,
    KEYBOARD_TYPE_MAIN,
    KEYBOARD_TYPE_APPLICATIONS,
    KEYBOARD_TYPE_MENUS,
    KEYBOARD_TYPE_MENUITEMS,
    KEYBOARD_TYPE_ACTIONS,
    KEYBOARD_TYPE_ALLTOOLBARS,
    KEYBOARD_TYPE_TOOLBAR,
    KEYBOARD_TYPE_GUI,
    KEYBOARD_TYPE_EDITTEXT,
    KEYBOARD_TYPE_WINDOWS,
    KEYBOARD_TYPE_UNSPECIFIED,
    KEYBOARD_TYPE_MODAL
    } KeyboardTypes;

### gok_keyboard_new ()

    GokKeyboard*        gok_keyboard_new                    (void);

Allocates memory for a new keyboard and initializes the GokKeyboard structure. Call gok_keyboard_delete on this when done with it.

Returns : | A pointer to the new keyboard, NULL if it can't be created.

### gok_keyboard_read ()

    GokKeyboard*        gok_keyboard_read                   (const gchar *Filename);

Reads in the given keyboard file. Note: Call 'gok_keyboard_delete' on this keyboard when done with it.

`Filename` : | Name of the keyboard file.
Returns : | A pointer to the new keyboard, NULL if not created.

### gok_keyboard_delete ()

    void                gok_keyboard_delete                 (GokKeyboard *pKeyboard,
                                                             gboolean bForce);

Deletes the given keyboard. This must be called on every keyboard that has been created. Don't use the given keyboard after calling this.

`pKeyboard` : | Pointer to the keyboard that's getting deleted.
`bForce` : | TRUE if the keyboard should be deleted even if it is in the stack.

### gok_keyboard_delete_key ()

    void                gok_keyboard_delete_key             (GokKey *pKey,
                                                             GokKeyboard *pKeyboard);

`pKey` : |
`pKeyboard` : |

### gok_keyboard_count_rows_columns ()

    void                gok_keyboard_count_rows_columns     (GokKeyboard *pKeyboard);

Counts the number of rows and columns in the keyboard and updates members of the GokKeyboard structure.

`pKeyboard` : | Pointer to the keyboard that we want to get the rows and columns for.

### gok_keyboard_get_number_rows ()

    gint                gok_keyboard_get_number_rows        (GokKeyboard *pKeyboard);

`pKeyboard` : | Pointer to the keyboard that you're concerned about.
Returns : | The number of rows in the given keyboard.

### gok_keyboard_get_number_columns ()

    gint                gok_keyboard_get_number_columns     (GokKeyboard *pKeyboard);

`pKeyboard` : | Pointer to the keyboard you want to know about.
Returns : | The number of columns in the given keyboard.

### gok_keyboard_add_keys ()

    gboolean            gok_keyboard_add_keys               (GokKeyboard *pKeyboard,
                                                             xmlDoc *pDoc);

Adds the keys from the given DOM to this keyboard. The keys will all be deleted when gok_keyboard_delete is called.

`pKeyboard` : | Pointer to the keyboard that will contain the keys.
`pDoc` : | Pointer to the XML document that describes the keys.
Returns : | TRUE if the keys were added, FALSE if not.

### gok_keyboard_get_name ()

    gchar*              gok_keyboard_get_name               (GokKeyboard *pKeyboard);

`pKeyboard` : | Pointer to the keyboard to get the name from.
Returns : | gchar* name of keyboard

### gok_keyboard_set_name ()

    void                gok_keyboard_set_name               (GokKeyboard *pKeyboard,
                                                             gchar *Name);

`pKeyboard` : | Pointer to the keyboard that's getting named.
`Name` : | Name for the keyboard.

### gok_keyboard_get_supports_wordcomplete ()

    gboolean            gok_keyboard_get_supports_wordcomplete
                                                            (GokKeyboard *pKeyboard);

`pKeyboard` : | Pointer to the keyboard that we're testing.
Returns : | TRUE if the given keyboard supports word completion. Only alphabetic keyboards should support word completion.

### gok_keyboard_set_wordcomplete_keys_added ()

    void                gok_keyboard_set_wordcomplete_keys_added
                                                            (GokKeyboard *pKeyboard,
                                                             gboolean bTrueFalse);

`pKeyboard` : | Pointer to the keyboard that is changed.
`bTrueFalse` : | TRUE if you want the predictor keys added, FALSE if not.

### gok_keyboard_get_wordcomplete_keys_added ()

    gboolean            gok_keyboard_get_wordcomplete_keys_added
                                                            (GokKeyboard *pKeyboard);

`pKeyboard` : | Pointer to the keyboard that we're testing.
Returns : | TRUE if the given keyboard has the word completion keys added, FALSE if not.

### gok_keyboard_set_commandpredict_keys_added ()

    void                gok_keyboard_set_commandpredict_keys_added
                                                            (GokKeyboard *pKeyboard,
                                                             gboolean bTrueFalse);

`pKeyboard` : | Pointer to the keyboard that is changed.
`bTrueFalse` : | TRUE if you want the prediction keys added, FALSE if not.

### gok_keyboard_get_commandpredict_keys_added ()

    gboolean            gok_keyboard_get_commandpredict_keys_added
                                                            (GokKeyboard *pKeyboard);

`pKeyboard` : | Pointer to the keyboard that we're testing.
Returns : | TRUE if the given keyboard has the word completion keys added, FALSE if not.

### gok_keyboard_display ()

    gboolean            gok_keyboard_display                (GokKeyboard *pKeyboard,
                                                             GokKeyboard *pKeyboardCurrent,
                                                             GtkWidget *pWindowMain,
                                                             gboolean CallbackScanner);

Displays the given keyboard in the GOK window.

`pKeyboard` : | Pointer to the keyboard that gets displayed.
`pKeyboardCurrent` : | Pointer to the current keyboard.
`pWindowMain` : | Pointer to the main window that holds the keyboards.
`CallbackScanner` : | If TRUE then the keyboard is used by the GOK. If FALSE then the keyboard is used by the editor.
Returns : | TRUE if the keyboard was displayed, FALSE if not.

### gok_keyboard_find_node ()

    xmlNode*            gok_keyboard_find_node              (xmlNode *pNode,
                                                             gchar *NameNode);

`pNode` : | Pointer to the XML node that may contain the node you're looking for.
`NameNode` : | Name of the node you're looking for.
Returns : | A pointer to the first node that has the given name, NULL if it can't be found. Note: This is recursive.

### gok_keyboard_position_keys ()

    void                gok_keyboard_position_keys          (GokKeyboard *pKeyboard,
                                                             GtkWidget *pWindow);

Positions the keys on the keyboard. The key cell coordinates are converted into window locations.

`pKeyboard` : | Pointer to the keyboard that contains the keys.
`pWindow` : | Pointer to the window that displays the keys.

### gok_keyboard_initialize ()

    void                gok_keyboard_initialize             (void);

Initializes global data for all keyboards. Call this once at the beginning of the program.

Returns : | void

### gok_key_delete ()

    void                gok_key_delete                      (GokKey *pKey,
                                                             GokKeyboard *pKeyboard,
                                                             gboolean bDeleteButton);

Deletes the given key. This must be called on every key that has been created. Don't use the given key after calling this. This unhooks the key from the next/previous list of keys.

`pKey` : | Pointer to the key that gets deleted.
`pKeyboard` : | Pointer to the keyboard that contains the key (can be NULL). If pKeyboard is not NULL then the key is unhooked from the keyboard.
`bDeleteButton` : | Flag that determines if the GTK button associated with the key should also be deleted. This should be set to TRUE if the key is deleted while the program is running. At the end of the program, when the GOK window is destroyed and the GTK buttons are destroyed, this should be set to FALSE.

### gok_key_new ()

    GokKey*             gok_key_new                         (GokKey *pKeyPrevious,
                                                             GokKey *pKeyNext,
                                                             GokKeyboard *pKeyboard);

Allocates memory for a new key and initializes the GokKey structure. Returns a pointer to the new key, NULL if it can't be created. Call gok_key_delete on this when done with it.

`pKeyPrevious` : | Pointer to the previous key in the list of keys.
`pKeyNext` : | Pointer to the next key in the list
`pKeyboard` : | Pointer to the keyboard.
Returns : | A pointer to the new key, NULL if it wasn't created.

### gok_keyboard_layout ()

    gboolean            gok_keyboard_layout                 (GokKeyboard *pKeyboard,
                                                             KeyboardLayouts layout,
                                                             KeyboardShape shape,
                                                             gboolean force);

Arranges the keys on the keyboard. Predefined keyboards are already laid out. Runtime keyboards require this.

`pKeyboard` : | Pointer to the keyboard that is getting laid out.
`layout` : | Can be used to specify a layout for the keys
`shape` : | Can be used to specify a shape of the keyboard window
`force` : | If TRUE, perform a layout even if performed previously
Returns : | TRUE if the keyboard was laid out, FALSE if not.

### gok_keyboard_branch_gui ()

    gboolean            gok_keyboard_branch_gui             (AccessibleNode *pNodeAccessible,
                                                             GokSpySearchType type);

Displays the generic gui keyboard - currently used for widgets inside windowish things

`pNodeAccessible` : | Pointer to the accessible node (parent object).
`type` : | Type of dynamic keyboard to branch to (the role of the children).
Returns : | TRUE if the keyboard was displayed, FALSE if not.

### gok_keyboard_branch_gui_actions ()

    gboolean            gok_keyboard_branch_gui_actions     (GokKeyboard *keyboard,
                                                             AccessibleNode *pNodeAccessible,
                                                             gint action_ndx);

Widgets can have multiple actions - build a keyboard of them.

`keyboard` : |
`pNodeAccessible` : | the node which represents the gui widget
`action_ndx` : |
Returns : | TRUE if the keyboard was displayed, FALSE if not.

### gok_keyboard_branch_edittext ()

    gboolean            gok_keyboard_branch_edittext        (void);

Returns : |

### gok_chunker_chunk ()

    gboolean            gok_chunker_chunk                   (GokKeyboard *pKeyboard);

Breaks the keyboard ginto chunks (rows, columns etc.). The list of chunks is stored on the keyboard

`pKeyboard` : | Pointer to the keyboard that will be broken ginto chunks.
Returns : | TRUE if keyboard could be broken into chunks, FALSE if not.

### gok_chunker_initialize ()

    void                gok_chunker_initialize              (void);

Initializes the chunker. This must be called before using the chunker.

### gok_chunker_chunk_rows_ttb ()

    gboolean            gok_chunker_chunk_rows_ttb          (GokKeyboard *pKeyboard,
                                                             gint ChunkOrder);

Keys are placed in the row in which they appear - starting at the top row and working towards the bottom row. If a key spans more than one row it will be placed in the topmost row it occupies. The given keyboard should not have any chunks (call gok_chunker_delete_chunks before calling this).

`pKeyboard` : | Pointer to the keyboard that will be broken ginto chunks of rows.
`ChunkOrder` : | Defines the row order (left to right or right to left). See enum ChunkOrder.
Returns : | TRUE if the keyboard was broken ginto rows, FALSE if not

### gok_chunker_chunk_rows_btt ()

    gboolean            gok_chunker_chunk_rows_btt          (GokKeyboard *pKeyboard,
                                                             gint ChunkOrder);

Keys are placed in the row in which they appear - starting at the bottom row and working towards the top row. If a key spans more than one row it will be placed in the lowest row it occupies. The given keyboard should not have any chunks (call gok_chunker_delete_chunks before calling this).

`pKeyboard` : | Pointer to the keyboard that will be broken ginto chunks of rows.
`ChunkOrder` : | Defines the row order (left to right or right to left). See enum ChunkOrder.
Returns : | TRUE if the keyboard was broken ginto rows, FALSE if not

### gok_chunker_chunk_cols_ltr ()

    gboolean            gok_chunker_chunk_cols_ltr          (GokKeyboard *pKeyboard,
                                                             gint ChunkOrder);

Keys are placed in the column in which they appear - starting at the left column and working towards the right row. If a key spans more than one column it will be placed in the leftmost columns it occupies. The given keyboard should not have any chunks (call gok_chunker_delete_chunks before calling this).

`pKeyboard` : | Pointer to the keyboard that will be broken ginto chunks of rows.
`ChunkOrder` : | Defines the column order (top to bottom or bottom to top). See enum ChunkOrder.
Returns : | TRUE if the keyboard was broken ginto columns, FALSE if not

### gok_chunker_chunk_cols_rtl ()

    gboolean            gok_chunker_chunk_cols_rtl          (GokKeyboard *pKeyboard,
                                                             gint ChunkOrder);

Keys are placed in the column in which they appear - starting at the right row and working towards the left column. If a key spans more than one row it will be placed in the rightmost row it occupies. The given keyboard should not have any chunks (call gok_chunker_delete_chunks before calling this).

`pKeyboard` : | Pointer to the keyboard that will be broken ginto chunks of columns.
`ChunkOrder` : | Defines the column order (top to bottom or bottom to top). See enum ChunkOrder.
Returns : | TRUE if the keyboard was broken ginto columns, FALSE if not

### gok_chunker_chunk_recursive ()

    gboolean            gok_chunker_chunk_recursive         (GokKeyboard *pKeyboard,
                                                             gint ChunkOrder,
                                                             gint Groups);

Breaks the keyboard into recursive groups until a group consists of only one key.

`pKeyboard` : | Pointer to the keyboard that will be broken into chunks.
`ChunkOrder` : | Defines the column order (top to bottom or bottom to top). See enum ChunkOrder.
`Groups` : | Number of groups the chunk should be broken into.
Returns : | TRUE if the keyboard was broken ginto chunks, FALSE if not.

### gok_chunker_find_center ()

    GokKey*             gok_chunker_find_center             (GokKeyboard *pKeyboard,
                                                             gint centerRow,
                                                             gint centerColumn,
                                                             gint *pRowsDistant,
                                                             gint *pColumnsDistant);

Check pRowsDistant and pColumnsDistant to find out how close this key is to the center. If both pRowsDistant and pColumnsDistant are 0 then the key returned is directly at the center.

`pKeyboard` : | Keyboard that you want to find the center of.
`centerRow` : | Center row of the keyboard.
`centerColumn` : | Center column of the keyboard.
`pRowsDistant` : | (out) Key returned is this number of rows away from the center.
`pColumnsDistant` : | (out) Key returned is this number of columns away from the center.
Returns : | Pointer to the key that is closest to the center of the keyboard.

### gok_keyboard_output_selectedkey ()

    GokKey*             gok_keyboard_output_selectedkey     (void);

Performs the events associated with the currently selected key

Returns : | Always 0.

### gok_keyboard_validate_dynamic_keys ()

    gboolean            gok_keyboard_validate_dynamic_keys  (Accessible *pAccessibleForeground);

Enables or disables the keys that branch to the dynamic keyboards keyboards.

`pAccessibleForeground` : | Pointer to the foreground accessible pointer.
Returns : | TRUE if any of the keys have changed their state (disable/active). Returns FALSE if none of the keys change state.

### gok_keyboard_fill_row ()

    void                gok_keyboard_fill_row               (GokKeyboard *pKeyboard,
                                                             gint RowNumber);

This function resizes the keys in the given row so they fill the entire row. This should be used only on keyboards that are synamically created (not predefined).

`pKeyboard` : | Pointer to the keyboard that contains the row.
`RowNumber` : | Number of the row you want filled.

### gok_keyboard_insert_array ()

    void                gok_keyboard_insert_array           (GokKey *pKey);

Adds the given key to our array in order of the rightmost key location.

`pKey` : | Pointer to the key you want added to the array.

### gok_keyboard_on_window_resize ()

    void                gok_keyboard_on_window_resize       (void);

This will be called when the window has been resized. Change the key size, update the gok_data and settings dialog with the new key size. If we resize the window (by branching) then the m_bIgnoreResizeEvent flag will be set so we ignore the resize. This flag is needed because we can't get a message from the system letting us know that it was the user that resized the window.

### gok_keyboard_get_keywidth_for_window ()

    gint                gok_keyboard_get_keywidth_for_window
                                                            (gint WidthWindow,
                                                             GokKeyboard *pKeyboard);

Calculates a key width for the current keyboard given the window width.

`WidthWindow` : | Width of the target window.
`pKeyboard` : | Pointer to the keyboard that will be displayed.
Returns : | The key width.

### gok_keyboard_get_keyheight_for_window ()

    gint                gok_keyboard_get_keyheight_for_window
                                                            (gint HeightWindow,
                                                             GokKeyboard *pKeyboard);

Calculates a key height for the current keyboard given the window height.

`HeightWindow` : | Height of the target window.
`pKeyboard` : | Pointer to the keyboard that will be displayed.
Returns : | The key height.

### gok_keyboard_set_ignore_resize ()

    void                gok_keyboard_set_ignore_resize      (gboolean bFlag);

Sets/clears a flag so that the next resize event will be ignored.

`bFlag` : | State of the resize flag.

### gok_keyboard_get_accessible ()

    Accessible*         gok_keyboard_get_accessible         (GokKeyboard *pKeyboard);

`pKeyboard` : | Pointer to the keyboard that we're using
Returns : | pointer to the accessible (probably shared by keys on this keyboard)

### gok_keyboard_set_accessible ()

    void                gok_keyboard_set_accessible         (GokKeyboard *pKeyboard,
                                                             Accessible *pAccessible);

`pKeyboard` : | Pointer to the keyboard that is to be changed.
`pAccessible` : | Pointer to the new accessible interface.

### gok_keyboard_calculate_font_size ()

    void                gok_keyboard_calculate_font_size    (GokKeyboard *pKeyboard);

Sets the font size for each key on the given keyboard. Each key may be assigned to a a font size group (FSG). If the FSG is not specified then the key belongs to group FONT_SIZE_GROUP_UNDEFINED. If the FSG is FONT_SIZE_GROUP_UNIQUE then the key does not belong to any group and calculate a font size for that key.

`pKeyboard` : | Pointer to the keyboard that gets the new font size.

### gok_keyboard_calculate_font_size_group ()

    void                gok_keyboard_calculate_font_size_group
                                                            (GokKeyboard *pKeyboard,
                                                             gint GroupNumber,
                                                             gboolean bOverride);

Sets the font size for each key that belongs to the given group on the given keyboard.

`pKeyboard` : | Pointer to the keyboard that gets the new font size.
`GroupNumber` : | Number of the font size group.
`bOverride` : | If TRUE then the font size is set for the key even if it already has a font size set. If FALSE then the font size is not set for the key if it is already set.

### gok_keyboard_update_dynamic ()

    gboolean            gok_keyboard_update_dynamic         (GokKeyboard *pKeyboard);

Creates all the keys for the given dynamic keyboard.

`pKeyboard` : | Pointer to the keyboard that gets updated.
Returns : | TRUE if the keyboard was updated, FALSE if not.

### gok_keyboard_update_labels ()

    void                gok_keyboard_update_labels          (void);

Redraws the labels on all the keys. This should be called whenever a modifier key changes state.