/*
 * Copyright 2009 Canonical Ltd.
 *
 * This program is free software: you can redistribute it and/or modify it
 * under the terms of either or both of the following licenses:
 *
 * 1) the GNU Lesser General Public License version 3, as published by the
 * Free Software Foundation; and/or
 * 2) the GNU Lesser General Public License version 2.1, as published by
 * the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranties of
 * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
 * PURPOSE.  See the applicable version of the GNU Lesser General Public
 * License for more details.
 *
 * You should have received a copy of both the GNU Lesser General Public
 * License version 3 and version 2.1 along with this program.  If not, see
 * <http://www.gnu.org/licenses/>
 *
 * Authored by: Neil Jagdish Patel <neil.patel@canonical.com>
 *
 */

/** 
 * SECTION:ctk-focusable
 * @short_description: #CtkFocusable is an interface designed to allow objects to become focused
 * @include: ctk-focusable.h
 *
 * #CtkFocusable is an interface layered ontop of other Ctk widgets in order to allow them to become focused in some way
 * it also provides methods in order to manipulate and return focused status
 */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <glib-object.h>

#include "ctk-focusable.h"

static void
ctk_focusable_base_init (gpointer g_iface)
{
  static gboolean initialised = FALSE;

  if (!initialised)
    {
      initialised = TRUE;
    }
}

GType
ctk_focusable_get_type (void)
{
  static GType focusable_type = 0;

  if (G_UNLIKELY (!focusable_type))
    {
      GTypeInfo focusable_info =
      {
        sizeof (CtkFocusableIface),
        ctk_focusable_base_init,
        NULL, /* iface_finalize */
      };

      focusable_type = g_type_register_static (G_TYPE_INTERFACE,
                       "CtkFocusable",
                       &focusable_info, 0);
    }

  return focusable_type;
}

/*
 * Public methods
 */

/**
 * ctk_focusable_set_focused:
 * @focusable: a #CtkFocusable object
 * @is_focused: a boolean value
 *
 * Sets @focusable to be focused or not
 */
void
ctk_focusable_set_focused (CtkFocusable      *focusable,
                           gboolean           is_focused)
{
  g_return_if_fail (CTK_IS_FOCUSABLE (focusable));

  CTK_FOCUSABLE_GET_IFACE (focusable)->set_focused (focusable, is_focused);
}

/**
 * ctk_focusable_get_focused:
 * @focusable: a #CtkFocusable object
 * 
 * Retrives the status of @focusable
 *
 * Returns: A boolean value, false if @focusable is not a valid #CtkFocusable
 */
gboolean
ctk_focusable_get_focused (CtkFocusable      *focusable)
{
  g_return_val_if_fail (CTK_IS_FOCUSABLE (focusable), FALSE);

  return CTK_FOCUSABLE_GET_IFACE (focusable)->get_focused (focusable);
}


/**
 * ctk_focusable_can_focus:
 * @focusable: a #CtkFocusable object
 * 
 * Retrives data on wheather @focusable can be focused or not
 *
 * Returns: A boolean value, false if @focusable is not a valid #CtkFocusable
 */
gboolean
ctk_focusable_can_focus   (CtkFocusable      *focusable)
{
  g_return_val_if_fail (CTK_IS_FOCUSABLE (focusable), FALSE);

  return CTK_FOCUSABLE_GET_IFACE (focusable)->can_focus (focusable);
}

/**
 * ctk_focusable_activate:
 * @focusable: A #CtkFocusable object
 *
 * Activates @focusable (makes it focused?)
 */
void
ctk_focusable_activate    (CtkFocusable      *focusable)
{
  g_return_if_fail (CTK_IS_FOCUSABLE (focusable));

  CTK_FOCUSABLE_GET_IFACE (focusable)->activate (focusable);
}

CtkFocusEventType
ctk_focusable_get_event_type (ClutterKeyEvent   *event,
                              CtkFocusDirection *direction)
{
  CtkFocusEventType type = CTK_FOCUS_EVENT_NONE;
  CtkFocusDirection dir = CTK_FOCUS_DIR_NONE;
  gint              keyval;

  g_return_val_if_fail (event, CTK_FOCUS_EVENT_NONE);

  keyval = clutter_event_get_key_symbol ((ClutterEvent *)event);

  switch (keyval)
    {
    case CLUTTER_Return:
    case CLUTTER_KP_Enter:
    case CLUTTER_ISO_Enter:
    case CLUTTER_KP_Space:
    case CLUTTER_space:
      type = CTK_FOCUS_EVENT_ACTIVATE;
      break;
    case CLUTTER_BackSpace:
    case CLUTTER_Escape:
      break;
    case CLUTTER_Up:
    case CLUTTER_KP_Up:
      type = CTK_FOCUS_EVENT_DIRECTION;
      dir = CTK_FOCUS_DIR_UP;
      break;
    case CLUTTER_Down:
    case CLUTTER_KP_Down:
      type = CTK_FOCUS_EVENT_DIRECTION;
      dir = CTK_FOCUS_DIR_DOWN;
      break;
    case CLUTTER_Left:
    case CLUTTER_KP_Left:
      type = CTK_FOCUS_EVENT_DIRECTION;
      dir = CTK_FOCUS_DIR_LEFT;
      break;
    case CLUTTER_Right:
    case CLUTTER_KP_Right:
      type = CTK_FOCUS_EVENT_DIRECTION;
      dir = CTK_FOCUS_DIR_RIGHT;
      break;
#if 0
    case CLUTTER_Page_Up:
    case CLUTTER_KP_Page_Up:
      type = TYPE_DIRECTION;
      dir = CLUTTER_DIRECTION_PAGE_UP;
      break;
    case CLUTTER_Page_Down:
    case CLUTTER_KP_Page_Down:
      type = TYPE_DIRECTION;
      dir = CLUTTER_DIRECTION_PAGE_DOWN;
      break;
#endif
    case CLUTTER_Tab:
    case CLUTTER_KP_Tab:
      type = CTK_FOCUS_EVENT_DIRECTION;
      dir = CTK_FOCUS_DIR_NEXT;
      if (event->modifier_state & CLUTTER_SHIFT_MASK)
        dir = CTK_FOCUS_DIR_PREV;
      break;
    case CLUTTER_ISO_Left_Tab:
      type = CTK_FOCUS_EVENT_DIRECTION;
      dir = CTK_FOCUS_DIR_PREV;
      break;
    default:
      type = CTK_FOCUS_EVENT_NONE;
      dir = CTK_FOCUS_DIR_NONE;
      break;
    }

  *direction = dir;
  return type;
}

