Logo Search packages:      
Sourcecode: kazehakase version File versions

kz-gesture.c

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */

/*
 *  Copyright (C) 2003 Takuro Ashie
 *
 *  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, 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 General Public License for more details.
 *
 *  You should have received a copy of the GNU 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.
 */

#include "kz-gesture.h"

#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#include "gobject-utils.h"

enum {
   START_SIGNAL,
   CANCEL_SIGNAL,
   STACK_MOTION_SIGNAL,
   PERFORM_SIGNAL,
   LAST_SIGNAL
};

static void kz_gesture_class_init   (KzGestureClass *klass);
static void kz_gesture_init         (KzGesture *gesture);
static void kz_gesture_dispose      (GObject *object);

/* KzGesture class signals */
static void kz_gesture_stack_motion (KzGesture *gesture,
                             KzGestureMotion motion);
static void kz_gesture_real_perform (KzGesture *gesture);

static void kz_gesture_reset        (KzGesture *gesture);
static KzGestureItem *kz_gesture_search_matched_item (KzGesture *gesture);
static void kz_gesture_item_destroy (KzGestureItem *item);

static GObjectClass *parent_class = NULL;
static gint kz_gesture_signals[LAST_SIGNAL] = {0};

KZ_OBJECT_GET_TYPE(kz_gesture, "KzGesture", KzGesture,
               kz_gesture_class_init, kz_gesture_init,
               G_TYPE_OBJECT)

static void
kz_gesture_class_init (KzGestureClass *klass)
{
      GObjectClass *object_class;

      parent_class = g_type_class_peek_parent (klass);
      object_class = (GObjectClass *) klass;

      object_class->dispose = kz_gesture_dispose;

      klass->start        = NULL;
      klass->cancel       = NULL;
      klass->stack_motion = kz_gesture_stack_motion;
      klass->perform      = kz_gesture_real_perform;

      kz_gesture_signals[START_SIGNAL]
            = g_signal_new ("start",
                        G_TYPE_FROM_CLASS (klass),
                        G_SIGNAL_RUN_FIRST,
                        G_STRUCT_OFFSET (KzGestureClass, start),
                        NULL, NULL,
                        g_cclosure_marshal_VOID__VOID,
                        G_TYPE_NONE, 0);

      kz_gesture_signals[CANCEL_SIGNAL]
            = g_signal_new ("cancel",
                        G_TYPE_FROM_CLASS (klass),
                        G_SIGNAL_RUN_FIRST,
                        G_STRUCT_OFFSET (KzGestureClass, cancel),
                        NULL, NULL,
                        g_cclosure_marshal_VOID__VOID,
                        G_TYPE_NONE, 0);

      kz_gesture_signals[STACK_MOTION_SIGNAL]
            = g_signal_new ("stack-motion",
                        G_TYPE_FROM_CLASS (klass),
                        G_SIGNAL_RUN_FIRST,
                        G_STRUCT_OFFSET (KzGestureClass, stack_motion),
                        NULL, NULL,
                        g_cclosure_marshal_VOID__CHAR,
                        G_TYPE_NONE, 1, G_TYPE_CHAR);

      kz_gesture_signals[PERFORM_SIGNAL]
            = g_signal_new ("perform",
                        G_TYPE_FROM_CLASS (klass),
                        G_SIGNAL_RUN_LAST,
                        G_STRUCT_OFFSET (KzGestureClass, perform),
                        NULL, NULL,
                        g_cclosure_marshal_VOID__VOID,
                        G_TYPE_NONE, 0);
}

static void
kz_gesture_init (KzGesture *gesture)
{
      gesture->max_sequence_len
            = G_N_ELEMENTS(gesture->sequence) - 1;
      gesture->threshold = 16;

      kz_gesture_reset(gesture); /* init other members */

      gesture->items = NULL;
}

static void
kz_gesture_dispose (GObject *object)
{
      KzGesture *gesture;

      gesture = KZ_GESTURE(object);

      if (gesture->items)
            kz_gesture_items_unref(gesture->items);
      gesture->items = NULL;

      if (G_OBJECT_CLASS (parent_class)->dispose)
            G_OBJECT_CLASS (parent_class)->dispose(object);
}

KzGesture *
kz_gesture_new (void)
{
      KzGesture *gesture = g_object_new(KZ_TYPE_GESTURE, NULL);
      return gesture;
}

void
kz_gesture_set_items (KzGesture *gesture, KzGestureItems *items)
{
      g_return_if_fail(KZ_IS_GESTURE(gesture));

      if (gesture->items)
            kz_gesture_items_unref(gesture->items);
      if (items)
            gesture->items = kz_gesture_items_ref(items);
      else
            gesture->items = NULL;
}

static void
kz_gesture_real_perform (KzGesture *gesture)
{
      KzGestureItem *item;

      item = kz_gesture_search_matched_item(gesture);
      if (item)
            gtk_action_activate(item->action);

      kz_gesture_reset(gesture);
}

static void
kz_gesture_stack_motion (KzGesture *gesture, KzGestureMotion motion)
{
      gint len, max_len;
      KzGestureMotion *seq;

      g_return_if_fail(KZ_IS_GESTURE(gesture));
      /*
      g_return_if_fail(motion > KZ_GESTURE_MOTION_TERMINATOR &&
                   motion < KZ_GESTURE_N_MOTIONS);
      */

      len = gesture->sequence_len;
      max_len = gesture->max_sequence_len;
      seq = gesture->sequence;

      g_return_if_fail(len >= 0 && len < max_len);

      seq[len] = motion;
      len = ++gesture->sequence_len;
      seq[len] = '\0';
}

void
kz_gesture_start (KzGesture *gesture, gint mode, gint x, gint y)
{
      g_return_if_fail(KZ_IS_GESTURE(gesture));

      gesture->current_mode = mode;
      gesture->prev_x = gesture->x = x;
      gesture->prev_y = gesture->y = y;
      gesture->started = TRUE;

      g_signal_emit(gesture, kz_gesture_signals[START_SIGNAL], 0);
}

void
kz_gesture_perform (KzGesture *gesture)
{
      g_return_if_fail(KZ_IS_GESTURE(gesture));

      g_signal_emit(gesture, kz_gesture_signals[PERFORM_SIGNAL], 0);
}

void
kz_gesture_cancel (KzGesture *gesture)
{
      kz_gesture_reset (gesture);
      g_signal_emit(gesture, kz_gesture_signals[CANCEL_SIGNAL], 0);
}

static void
kz_gesture_reset (KzGesture *gesture)
{
      gesture->sequence[0]       = '\0';
      gesture->sequence_len      = 0;
      gesture->current_mode      = 0;
      gesture->prev_x            = -1;
      gesture->prev_y            = -1;
      gesture->x                 = -1;
      gesture->y                 = -1;
      gesture->started           = FALSE;
}

gboolean
kz_gesture_is_started (KzGesture *gesture)
{
      g_return_val_if_fail(KZ_IS_GESTURE(gesture), FALSE);
      return gesture->started;
}

void
kz_gesture_create_gesture_string(KzGesture *gesture, gchar buf[], gint len)
{
      gint i, j = 0;

      g_return_if_fail(KZ_IS_GESTURE(gesture));
      g_return_if_fail(buf);

      buf[0] = '\0';

      for (i = 0; i < gesture->sequence_len && j < len - 2; i++)
      {
            switch (toupper(gesture->sequence[i]))
            {
            case 'U':
                  buf[j++] = 'U';
                  break;
            case 'D':
                  buf[j++] = 'D';
                  break;
            case 'L':
                  buf[j++] = 'L';
                  break;
            case 'R':
                  buf[j++] = 'R';
                  break;
            default:
                  buf[j++] = '?';
                  break;
            }
            if (gesture->sequence[i] != '\0')
                  buf[j++] = ' ';
            buf[j] = '\0';
      }
}

void
kz_gesture_update_position (KzGesture *gesture, gint x, gint y)
{
      KzGestureMotion motion;
      gint mx, my;

      g_return_if_fail(KZ_IS_GESTURE(gesture));
      g_return_if_fail(kz_gesture_is_started(gesture));

      mx = x - gesture->prev_x; 
      my = y - gesture->prev_y;

      if (abs(mx) > gesture->threshold || abs(my) > gesture->threshold)
      {
            gint len = gesture->sequence_len;
            gint max_len = gesture->max_sequence_len;
            KzGestureMotion *seq = gesture->sequence;

            if (abs(mx) > abs(my)) {
                  if (mx < 0)
                        motion = 'L';
                  else
                        motion = 'R';
            }
            else
            {
                  if (my < 0)
                        motion = 'U';
                  else
                        motion = 'D';
            }

            gesture->prev_x = x;
            gesture->x = x;
            gesture->prev_y = y;
            gesture->y = y;

            if (len == 0 || (len > 0 && len < max_len && seq[len - 1] != motion))
            {
                  g_signal_emit (gesture,
                               kz_gesture_signals[STACK_MOTION_SIGNAL],
                               0, motion);
            }
      }
}

static KzGestureItem *
kz_gesture_search_matched_item (KzGesture *gesture)
{
      GSList *node;
      gint j;

      g_return_val_if_fail(KZ_IS_GESTURE(gesture), NULL);
      if (!kz_gesture_is_started(gesture)) return NULL;
      if (gesture->sequence[0] == 0) return NULL;
      if (!gesture->items) return NULL;

      for (node = gesture->items->list; node; node = g_slist_next(node))
      {
            KzGestureItem *item = node->data;

            if (!item) continue;
            if (item->sequence[0] == 0) continue;

            for (j = 0; gesture->sequence[j] == item->sequence[j]; j++)
            {
                  if (gesture->sequence[j + 1] == 0 &&
                      item->sequence[j + 1] == 0)
                  {
                        return item;
                  }
                  else if (gesture->sequence[j + 1] == 0 ||
                         item->sequence[j + 1] == 0)
                  {
                        break;
                  }
            }
      }

      return NULL;
}

gboolean
kz_gesture_is_matched (KzGesture *gesture)
{
      g_return_val_if_fail(KZ_IS_GESTURE(gesture), FALSE);

      if (kz_gesture_get_matched_label(gesture))
            return TRUE;
      else
            return FALSE;
}

const gchar *
kz_gesture_get_matched_label (KzGesture *gesture)
{
      KzGestureItem *item;

      g_return_val_if_fail(KZ_IS_GESTURE(gesture), NULL);

      item = kz_gesture_search_matched_item (gesture);
      if (!item) return NULL;
      g_return_val_if_fail(item->action, NULL);
      return gtk_action_get_name(item->action);
}

void
kz_gesture_get_current_position (KzGesture *gesture,
                         gint *x, gint *y)
{
      g_return_if_fail(KZ_IS_GESTURE(gesture));

      if (x)
            *x = gesture->x;
      if (y)
            *y = gesture->y;
}

const KzGestureMotion *
kz_gesture_get_current_sequence (KzGesture *gesture)
{
      g_return_val_if_fail(KZ_IS_GESTURE(gesture), NULL);

      return gesture->sequence;
}

gint
kz_gesture_get_mode (KzGesture *gesture)
{
      g_return_val_if_fail(KZ_IS_GESTURE(gesture), 0);

      return gesture->current_mode;
}

void
kz_gesture_set_mode (KzGesture *gesture, gint mode)
{
      g_return_if_fail(KZ_IS_GESTURE(gesture));

      gesture->current_mode = mode;
}

gint
kz_gesture_get_threshold (KzGesture *gesture)
{
      g_return_val_if_fail(KZ_IS_GESTURE(gesture), 0);

      return gesture->threshold;
}

void
kz_gesture_set_threshold (KzGesture *gesture, gint threshold)
{
      g_return_if_fail(KZ_IS_GESTURE(gesture));
      g_return_if_fail(threshold >= 0);

      gesture->threshold = threshold;
}



/*****************************************************************************
 *
 *   KzGestureItemTable
 *
 *****************************************************************************/
KzGestureItems *
kz_gesture_items_new ()
{
      KzGestureItems *items = g_new0(KzGestureItems, 1);
      items->list = NULL;
      items->ref_count = 1;
      return items;
}

KzGestureItems *
kz_gesture_items_ref (KzGestureItems *items)
{
      g_return_val_if_fail(items, NULL);
      items->ref_count++;
      return items;
}

void
kz_gesture_items_unref (KzGestureItems *items)
{
      g_return_if_fail(items);
      items->ref_count--;

      if (items->ref_count == 0) {
            GSList *node = items->list;

            for(; node; node = g_slist_next(node))
            {
                  KzGestureItem *item = node->data;

                  if (!item) continue;
                  kz_gesture_item_destroy(item);
            }

            g_slist_free(items->list);
            items->list = NULL;
            g_free(items);
      }
}


static gboolean
validate_gesture_sequence (const KzGestureMotion *sequence)
{
      gint i;

      if (!sequence || !*sequence) return FALSE;

      for (i = 0; sequence[i]; i++)
      {
            gint c = toupper(sequence[i]);
            if (c != 'U' && c != 'D' && c!= 'L' && c != 'R')
                  return FALSE;
      }

      return TRUE;
}


static gint
compare_gesture_item (gconstpointer data1, gconstpointer data2)
{
      const KzGestureItem *item = data1;
      const GtkAction *action = GTK_ACTION(data2);

      return item->action - action;
}


static void
kz_gesture_item_destroy (KzGestureItem *item)
{
      g_return_if_fail(item);

      g_object_unref(item->action);
      item->action = NULL;
      g_free(item->sequence);
      item->sequence = NULL;
      g_free(item);
}


void
kz_gesture_items_set_action (KzGestureItems *items,
                       GtkAction *action, gint mode,
                       const KzGestureMotion *sequence)
{
      KzGestureItem *item;
      GSList *node;

      g_return_if_fail(items);
      g_return_if_fail(GTK_IS_ACTION(action));
      g_return_if_fail(sequence && *sequence != '\0');

      if(!validate_gesture_sequence(sequence))
      {
            g_warning("Invalid gesture sequence: %s", sequence);
            return;
      }

      node = g_slist_find_custom(items->list, action,
                           compare_gesture_item);
      if (node)
      {
            item = node->data;
            g_free(item->sequence);
            item->sequence = NULL;
      }
      else
      {
            item = g_new0(KzGestureItem, 1);
            item->action   = g_object_ref(action);
      }

      item->mode     = mode;
      item->sequence = g_strdup(sequence);

      items->list = g_slist_append(items->list, item);
}


void
kz_gesture_items_unset_action (KzGestureItems *items,
                         GtkAction *action)
{
      KzGestureItem *item;
      GSList *node;

      g_return_if_fail(items);
      g_return_if_fail(GTK_IS_ACTION(action));

      node = g_slist_find_custom(items->list, action,
                           compare_gesture_item);
      if (!node) return;

      item = node->data;
      items->list = g_slist_remove(items->list, item);
      kz_gesture_item_destroy(item);
}

Generated by  Doxygen 1.6.0   Back to index