/*
 * Copyright (c) 2014 Red Hat, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
 */

#include "config.h"
#include <glib/gi18n-lib.h>

#include "gestures.h"
#include "object-tree.h"

#include "gtksizegroup.h"
#include "gtkcomboboxtext.h"
#include "gtklistbox.h"
#include "gtkgesture.h"
#include "gtklabel.h"
#include "gtkframe.h"
#include "gtkwidgetprivate.h"

enum
{
  PROP_0,
  PROP_OBJECT_TREE
};

struct _GtkInspectorGesturesPrivate
{
  GtkSizeGroup *sizegroup;
  GObject *object;
  GtkInspectorObjectTree *object_tree;
};

G_DEFINE_TYPE_WITH_PRIVATE (GtkInspectorGestures, gtk_inspector_gestures, GTK_TYPE_BOX)

static void
gtk_inspector_gestures_init (GtkInspectorGestures *sl)
{
  sl->priv = gtk_inspector_gestures_get_instance_private (sl);
  sl->priv->sizegroup = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
  g_object_set (sl,
                "orientation", GTK_ORIENTATION_VERTICAL,
                "margin-start", 60,
                "margin-end", 60,
                "margin-top", 60,
                "margin-bottom", 30,
                "spacing", 10,
                NULL);
}

static void
clear_all (GtkInspectorGestures *sl)
{
  GList *children, *l;
  GtkWidget *child;

  children = gtk_container_get_children (GTK_CONTAINER (sl));
  for (l = children; l; l = l->next)
    {
      child = l->data;
      gtk_container_remove (GTK_CONTAINER (sl), child);
    }
  g_list_free (children);
}

static void
phase_changed_cb (GtkComboBox *combo, GtkInspectorGestures *sl)
{
  GtkWidget *row;
  GtkPropagationPhase phase;
  GtkGesture *gesture;

  phase = gtk_combo_box_get_active (combo);
  row = gtk_widget_get_ancestor (GTK_WIDGET (combo), GTK_TYPE_LIST_BOX_ROW);
  gesture = GTK_GESTURE (g_object_get_data (G_OBJECT (row), "gesture"));
  gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (gesture), phase);
}

static void
row_activated (GtkListBox           *box,
               GtkListBoxRow        *row,
               GtkInspectorGestures *sl)
{
  GObject *gesture;
  
  gesture = G_OBJECT (g_object_get_data (G_OBJECT (row), "gesture"));
  gtk_inspector_object_tree_select_object (sl->priv->object_tree, gesture);
}

static void
add_gesture (GtkInspectorGestures *sl,
             GObject              *object,
             GtkWidget            *listbox,
             GtkGesture           *gesture,
             GtkPropagationPhase   phase)
{
  GtkWidget *row;
  GtkWidget *box;
  GtkWidget *label;
  GtkWidget *combo;

  row = gtk_list_box_row_new ();
  gtk_container_add (GTK_CONTAINER (listbox), row);
  gtk_widget_show (row);
  box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 40);
  gtk_container_add (GTK_CONTAINER (row), box);
  g_object_set (box, "margin", 10, NULL);
  gtk_widget_show (box);
  label = gtk_label_new (g_type_name_from_instance ((GTypeInstance*)gesture));
  g_object_set (label, "xalign", 0.0, NULL);
  gtk_container_add (GTK_CONTAINER (box), label);
  gtk_size_group_add_widget (sl->priv->sizegroup, label);
  gtk_widget_show (label);
  gtk_widget_set_halign (label, GTK_ALIGN_START);
  gtk_widget_set_valign (label, GTK_ALIGN_BASELINE);

  combo = gtk_combo_box_text_new ();
  gtk_combo_box_text_insert_text (GTK_COMBO_BOX_TEXT (combo), GTK_PHASE_NONE, C_("event phase", "None"));
  gtk_combo_box_text_insert_text (GTK_COMBO_BOX_TEXT (combo), GTK_PHASE_CAPTURE, C_("event phase", "Capture"));
  gtk_combo_box_text_insert_text (GTK_COMBO_BOX_TEXT (combo), GTK_PHASE_BUBBLE, C_("event phase", "Bubble"));
  gtk_combo_box_text_insert_text (GTK_COMBO_BOX_TEXT (combo), GTK_PHASE_TARGET, C_("event phase", "Target"));
  gtk_combo_box_set_active (GTK_COMBO_BOX (combo), phase);
  gtk_container_add (GTK_CONTAINER (box), combo);
  gtk_widget_show (combo);
  gtk_widget_set_halign (label, GTK_ALIGN_END);
  gtk_widget_set_valign (label, GTK_ALIGN_BASELINE);

  g_object_set_data (G_OBJECT (row), "gesture", gesture);
  g_signal_connect (combo, "changed", G_CALLBACK (phase_changed_cb), sl);
}

static void
add_gesture_group (GtkInspectorGestures *sl,
                   GObject              *object,
                   GtkGesture           *gesture,
                   GHashTable           *hash)
{
  GtkWidget *frame;
  GtkWidget *listbox;
  GList *list, *l;
  GtkGesture *g;
  GtkPropagationPhase phase;

  frame = gtk_frame_new (NULL);
  gtk_widget_show (frame);
  gtk_widget_set_halign (frame, GTK_ALIGN_CENTER);

  listbox = gtk_list_box_new ();
  g_signal_connect (listbox, "row-activated", G_CALLBACK (row_activated), sl);
  gtk_container_add (GTK_CONTAINER (frame), listbox);
  gtk_widget_show (listbox);
  gtk_list_box_set_selection_mode (GTK_LIST_BOX (listbox), GTK_SELECTION_NONE);

  list = gtk_gesture_get_group (gesture);
  for (l = list; l; l = l->next)
    {
      g = l->data;
      phase = GPOINTER_TO_INT (g_hash_table_lookup (hash, g));
      add_gesture (sl, object, listbox, g, phase);
      g_hash_table_remove (hash, g);
    }
  g_list_free (list);

  gtk_container_add (GTK_CONTAINER (sl), frame);
}

void
gtk_inspector_gestures_set_object (GtkInspectorGestures *sl,
                                   GObject              *object)
{
  GHashTable *hash;
  GHashTableIter iter;
  GList *list, *l;
  gint phase;

  clear_all (sl);
  gtk_widget_hide (GTK_WIDGET (sl));

  if (!GTK_IS_WIDGET (object))
    return;

  hash = g_hash_table_new (g_direct_hash, g_direct_equal);
  for (phase = GTK_PHASE_NONE; phase <= GTK_PHASE_TARGET; phase++)
    {
      list = _gtk_widget_list_controllers (GTK_WIDGET (object), phase);
      for (l = list; l; l = l->next)
        {
          GtkEventController *controller = l->data;

          if (GTK_IS_GESTURE (controller))
            g_hash_table_insert (hash, controller, GINT_TO_POINTER (phase));
        }
      g_list_free (list);
    }

  if (g_hash_table_size (hash))
    gtk_widget_show (GTK_WIDGET (sl));

  while (g_hash_table_size (hash) > 0)
    {
      gpointer key, value;
      GtkGesture *gesture;
      g_hash_table_iter_init (&iter, hash);
      (void)g_hash_table_iter_next (&iter, &key, &value);
      gesture = key;
      add_gesture_group (sl, object, gesture, hash);
    }

  g_hash_table_unref (hash);
}

static void
get_property (GObject    *object,
              guint       param_id,
              GValue     *value,
              GParamSpec *pspec)
{
  GtkInspectorGestures *sl = GTK_INSPECTOR_GESTURES (object);

  switch (param_id)
    {
      case PROP_OBJECT_TREE:
        g_value_take_object (value, sl->priv->object_tree);
        break;

      default:
        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
        break;
    }
}

static void
set_property (GObject      *object,
              guint         param_id,
              const GValue *value,
              GParamSpec   *pspec)
{
  GtkInspectorGestures *sl = GTK_INSPECTOR_GESTURES (object);

  switch (param_id)
    {
      case PROP_OBJECT_TREE:
        sl->priv->object_tree = g_value_get_object (value);
        break;

      default:
        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
        break;
    }
}

static void
gtk_inspector_gestures_class_init (GtkInspectorGesturesClass *klass)
{
  GObjectClass *object_class = G_OBJECT_CLASS (klass);

  object_class->get_property = get_property;
  object_class->set_property = set_property;

  g_object_class_install_property (object_class, PROP_OBJECT_TREE,
      g_param_spec_object ("object-tree", "Widget Tree", "Widget tree",
                           GTK_TYPE_WIDGET, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
}

// vim: set et sw=2 ts=2:
