/*
 * 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 "resource-list.h"

#include "gtkbutton.h"
#include "gtklabel.h"
#include "gtksearchbar.h"
#include "gtksearchentry.h"
#include "gtkstack.h"
#include "gtktextbuffer.h"
#include "gtktreeselection.h"
#include "gtktreestore.h"
#include "gtkeventcontrollerkey.h"
#include "gtkpicture.h"
#include "gtkmediafile.h"
#include "gtkbinlayout.h"
#include "resource-holder.h"

#include <glib/gi18n-lib.h>

enum
{
  PROP_0,
  PROP_BUTTONS
};

struct _GtkInspectorResourceList
{
  GtkWidget parent_instance;

  GtkTextBuffer *buffer;
  GtkWidget *video;
  GtkWidget *image;
  GtkWidget *content;
  GtkWidget *name_label;
  GtkWidget *type;
  GtkWidget *type_label;
  GtkWidget *size_label;
  GtkWidget *info_grid;
  GtkWidget *stack;
  GtkWidget *buttons;
  GtkWidget *open_details_button;
  GtkWidget *close_details_button;
  GtkWidget *search_bar;
  GtkWidget *search_entry;

  GtkWidget *list;
  GtkColumnViewColumn *path;
  GtkColumnViewColumn *count;
  GtkColumnViewColumn *size;

  GtkTreeListModel *tree_model;
  GtkSingleSelection *selection;
};

typedef struct _GtkInspectorResourceListClass
{
  GtkWidgetClass parent;
} GtkInspectorResourceListClass;


G_DEFINE_TYPE (GtkInspectorResourceList, gtk_inspector_resource_list, GTK_TYPE_WIDGET)

static GListModel *
load_resources_recurse (const char *path,
                        int        *count_out,
                        gsize      *size_out)
{
  char **names;
  int i;
  GListStore *result;

  result = g_list_store_new (RESOURCE_TYPE_HOLDER);

  names = g_resources_enumerate_children (path, 0, NULL);
  for (i = 0; names[i]; i++)
    {
      int len;
      char *p;
      gboolean has_slash;
      int count;
      gsize size;
      GListModel *children;
      ResourceHolder *holder;

      p = g_strconcat (path, names[i], NULL);

      len = strlen (names[i]);
      has_slash = names[i][len - 1] == '/';

      if (has_slash)
        names[i][len - 1] = '\0';

      count = 0;
      size = 0;

      if (has_slash)
        {
          children = load_resources_recurse (p, &count, &size);

          *count_out += count;
          *size_out += size;
        }
      else
        {
          count = 0;
          if (g_resources_get_info (p, 0, &size, NULL, NULL))
            {
              *count_out += 1;
              *size_out += size;
            }
          children = NULL;
        }

      holder = resource_holder_new (names[i], p, count, size, children);
      g_clear_object (&children);
      g_list_store_append (result, holder);
      g_object_unref (holder);

      g_free (p);
    }

  g_strfreev (names);

  return G_LIST_MODEL (result);
}

static gboolean
populate_details (GtkInspectorResourceList *rl,
                  ResourceHolder           *holder)
{
  const char *path;
  const char *name;
  GBytes *bytes;
  char *type;
  gconstpointer data;
  gsize size;
  GError *error = NULL;
  char *markup;

  path = resource_holder_get_path (holder);
  name = resource_holder_get_name (holder);
  size = resource_holder_get_size (holder);

   if (g_str_has_suffix (path, "/"))
     return FALSE;

  markup = g_strconcat ("<span face='Monospace' size='small'>", path, "</span>", NULL);
  gtk_label_set_markup (GTK_LABEL (rl->name_label), markup);
  g_free (markup);

  bytes = g_resources_lookup_data (path, 0, &error);
  if (bytes == NULL)
    {
      gtk_text_buffer_set_text (rl->buffer, error->message, -1);
      g_error_free (error);
      gtk_stack_set_visible_child_name (GTK_STACK (rl->content), "text");
    }
  else
    {
      char *text;
      char *content_image;
      char *content_text;
      char *content_video;

      content_image = g_content_type_from_mime_type ("image/*");
      content_text = g_content_type_from_mime_type ("text/*");
      content_video = g_content_type_from_mime_type ("video/*");

      data = g_bytes_get_data (bytes, &size);
      type = g_content_type_guess (name, data, size, NULL);

      text = g_content_type_get_description (type);
      gtk_label_set_text (GTK_LABEL (rl->type_label), text);
      g_free (text);

      text = g_format_size (size);
      gtk_label_set_text (GTK_LABEL (rl->size_label), text);
      g_free (text);

      if (g_content_type_is_a (type, content_text))
        {
          gtk_text_buffer_set_text (rl->buffer, data, -1);
          gtk_stack_set_visible_child_name (GTK_STACK (rl->content), "text");
        }
      else if (g_content_type_is_a (type, content_image))
        {
          gtk_picture_set_resource (GTK_PICTURE (rl->image), path);
          gtk_stack_set_visible_child_name (GTK_STACK (rl->content), "image");
        }
      else if (g_content_type_is_a (type, content_video))
        {
          GtkMediaStream *stream;

          stream = gtk_media_file_new_for_resource (path);
          gtk_media_stream_set_loop (GTK_MEDIA_STREAM (stream), TRUE);
          gtk_picture_set_paintable (GTK_PICTURE (rl->image), GDK_PAINTABLE (stream));
          gtk_stack_set_visible_child_name (GTK_STACK (rl->content), "image");
          gtk_media_stream_play (GTK_MEDIA_STREAM (stream));
          g_object_unref (stream);
        }
      else
        {
          gtk_text_buffer_set_text (rl->buffer, "", 0);
          gtk_stack_set_visible_child_name (GTK_STACK (rl->content), "text");
        }

      g_free (type);
      g_bytes_unref (bytes);

      g_free (content_image);
      g_free (content_text);
    }

  return TRUE;
}

static void
on_row_activated (GtkColumnView            *view,
                  guint                     position,
                  GtkInspectorResourceList *rl)
{
  gpointer item;
  ResourceHolder *holder;

  item = g_list_model_get_item (G_LIST_MODEL (rl->selection), position);
  holder = gtk_tree_list_row_get_item (item);
  g_object_unref (item);
  if (populate_details (rl, holder))
    {
      gtk_stack_set_visible_child_name (GTK_STACK (rl->stack), "details");
      gtk_stack_set_visible_child_name (GTK_STACK (rl->buttons), "details");
    }
  g_object_unref (holder);
}

static gboolean
can_show_details (GtkInspectorResourceList *rl)
{
  gpointer item;
  ResourceHolder *holder;
  const char *path;

  item = gtk_single_selection_get_selected_item (rl->selection);
  holder = gtk_tree_list_row_get_item (item);
  if (holder == NULL)
    return FALSE;
  path = resource_holder_get_path (holder);
  g_object_unref (holder);
  return !g_str_has_suffix (path, "/");
}

static void
on_selection_changed (GtkSelectionModel        *selection,
                      guint                     position,
                      guint                     n_items,
                      GtkInspectorResourceList *rl)
{
  gtk_widget_set_sensitive (rl->open_details_button, can_show_details (rl));
}

static void
open_details (GtkWidget                *button,
              GtkInspectorResourceList *rl)
{
  gpointer item;
  ResourceHolder *holder;

  item = gtk_single_selection_get_selected_item (rl->selection);
  holder = gtk_tree_list_row_get_item (item);
  if (holder == NULL)
    return;
  if (populate_details (rl, holder))
    {
      gtk_stack_set_visible_child_name (GTK_STACK (rl->stack), "details");
      gtk_stack_set_visible_child_name (GTK_STACK (rl->buttons), "details");
    }
  g_object_unref (holder);
}

static void
close_details (GtkWidget                *button,
               GtkInspectorResourceList *rl)
{
  gtk_stack_set_visible_child_name (GTK_STACK (rl->stack), "list");
  gtk_stack_set_visible_child_name (GTK_STACK (rl->buttons), "list");
}

static GListModel *
load_resources (void)
{
  int count = 0;
  gsize size = 0;

  return load_resources_recurse ("/", &count, &size);
}

static void
on_map (GtkWidget *widget)
{
  GtkInspectorResourceList *rl = GTK_INSPECTOR_RESOURCE_LIST (widget);

  gtk_stack_set_visible_child_name (GTK_STACK (rl->stack), "list");
  gtk_widget_set_sensitive (rl->open_details_button, can_show_details (rl));
}

static gboolean search (GtkInspectorResourceList *rl,
                        gboolean                  forward,
                        gboolean                  force_progress);

static gboolean
key_pressed (GtkEventController       *controller,
             guint                     keyval,
             guint                     keycode,
             GdkModifierType           state,
             GtkInspectorResourceList *rl)
{
  if (gtk_widget_get_mapped (GTK_WIDGET (rl)))
    {
      GdkModifierType default_accel;
      gboolean search_started;

      search_started = gtk_search_bar_get_search_mode (GTK_SEARCH_BAR (rl->search_bar));
      default_accel = GDK_CONTROL_MASK;

      if (search_started &&
          (keyval == GDK_KEY_Return ||
           keyval == GDK_KEY_ISO_Enter ||
           keyval == GDK_KEY_KP_Enter))
        {
          gtk_widget_activate (GTK_WIDGET (rl->list));
          return GDK_EVENT_PROPAGATE;
        }
      else if (search_started &&
               (keyval == GDK_KEY_Escape))
        {
          gtk_search_bar_set_search_mode (GTK_SEARCH_BAR (rl->search_bar), FALSE);
          return GDK_EVENT_STOP;
        }
      else if (search_started &&
               ((state & (default_accel | GDK_SHIFT_MASK)) == (default_accel | GDK_SHIFT_MASK)) &&
               (keyval == GDK_KEY_g || keyval == GDK_KEY_G))
        {
          if (!search (rl, FALSE, TRUE))
            gtk_widget_error_bell (GTK_WIDGET (rl));
          return GDK_EVENT_STOP;
        }
      else if (search_started &&
               ((state & (default_accel | GDK_SHIFT_MASK)) == default_accel) &&
               (keyval == GDK_KEY_g || keyval == GDK_KEY_G))
        {
          if (!search (rl, TRUE, TRUE))
            gtk_widget_error_bell (GTK_WIDGET (rl));
          return GDK_EVENT_STOP;
        }
    }

  return GDK_EVENT_PROPAGATE;
}

static void
destroy_controller (GtkEventController *controller)
{
  gtk_widget_remove_controller (gtk_event_controller_get_widget (controller), controller);
}

static void
root (GtkWidget *widget)
{
  GtkInspectorResourceList *rl = GTK_INSPECTOR_RESOURCE_LIST (widget);
  GtkEventController *controller;
  GtkWidget *toplevel;

  GTK_WIDGET_CLASS (gtk_inspector_resource_list_parent_class)->root (widget);

  toplevel = GTK_WIDGET (gtk_widget_get_root (widget));

  controller = gtk_event_controller_key_new ();
  g_object_set_data_full (G_OBJECT (toplevel), "resource-controller", controller, (GDestroyNotify)destroy_controller);
  g_signal_connect (controller, "key-pressed", G_CALLBACK (key_pressed), widget);
  gtk_widget_add_controller (toplevel, controller);

  gtk_search_bar_set_key_capture_widget (GTK_SEARCH_BAR (rl->search_bar), toplevel);
}

static void
unroot (GtkWidget *widget)
{
  GtkWidget *toplevel;

  toplevel = GTK_WIDGET (gtk_widget_get_root (widget));
  g_object_set_data (G_OBJECT (toplevel), "resource-controller", NULL);

  GTK_WIDGET_CLASS (gtk_inspector_resource_list_parent_class)->unroot (widget);
}

static gboolean
match_string (const char *string,
              const char *text)
{
  char *lower;
  gboolean match = FALSE;

  if (string)
    {
      lower = g_ascii_strdown (string, -1);
      match = g_str_has_prefix (lower, text);
      g_free (lower);
    }

  return match;
}

static gboolean
match_object (GObject    *object,
              const char *text)
{
  const char *name = resource_holder_get_name (RESOURCE_HOLDER (object));

  if (match_string (name, text))
    return TRUE;

  return FALSE;
}

static GObject *
search_children (GObject    *object,
                 const char *text,
                 gboolean    forward)
{
  GListModel *children;
  GObject *child, *result;
  guint i, n;

  children = resource_holder_get_children (RESOURCE_HOLDER (object));
  if (children == NULL)
    return NULL;

  n = g_list_model_get_n_items (children);
  for (i = 0; i < n; i++)
    {
      child = g_list_model_get_item (children, forward ? i : n - i - 1);
      if (match_object (child, text))
        return child;

      result = search_children (child, text, forward);
      g_object_unref (child);
      if (result)
        return result;
    }

  return NULL;
}

static guint
model_get_item_index (GListModel *model,
                      gpointer    item)
{
  gpointer cmp;
  guint i;

  for (i = 0; (cmp = g_list_model_get_item (model, i)); i++)
    {
      if (cmp == item)
        {
          g_object_unref (cmp);
          return i;
        }
      g_object_unref (cmp);
    }

  return G_MAXUINT;
}

static GtkTreeListRow *
find_and_expand_object (GtkTreeListModel *model,
                        GObject          *object)
{
  GtkTreeListRow *result;
  GObject *parent;
  guint pos;

  parent = G_OBJECT (resource_holder_get_parent (RESOURCE_HOLDER (object)));
  if (parent)
    {
      GtkTreeListRow *parent_row = find_and_expand_object (model, parent);
      if (parent_row == NULL)
        return NULL;

      gtk_tree_list_row_set_expanded (parent_row, TRUE);
      pos = model_get_item_index (gtk_tree_list_row_get_children (parent_row), object);
      result = gtk_tree_list_row_get_child_row (parent_row, pos);
      g_object_unref (parent_row);
    }
  else
    {
      pos = model_get_item_index (gtk_tree_list_model_get_model (model), object);
      result = gtk_tree_list_model_get_child_row (model, pos);
    }

  return result;
}

static void
select_object (GtkInspectorResourceList *rl,
               GObject *object)
{
  GtkTreeListRow *row_item;

  row_item = find_and_expand_object (rl->tree_model, object);
  if (row_item == NULL)
    return;

  gtk_single_selection_set_selected (rl->selection,
                                     gtk_tree_list_row_get_position (row_item));
}

static gboolean
search (GtkInspectorResourceList *rl,
        gboolean                  forward,
        gboolean                  force_progress)
{
  GListModel *model = G_LIST_MODEL (rl->tree_model);
  GtkTreeListRow *row_item;
  GObject *child, *result;
  guint i, selected, n, row;
  const char *text;

  text = gtk_editable_get_text (GTK_EDITABLE (rl->search_entry));
  selected = gtk_single_selection_get_selected (rl->selection);
  n = g_list_model_get_n_items (model);
  if (selected >= n)
    selected = 0;

  for (i = 0; i < n; i++)
    {
      row = (selected + (forward ? i : n - i - 1)) % n;
      row_item = g_list_model_get_item (model, row);
      child = gtk_tree_list_row_get_item (row_item);
      if (i > 0 || !force_progress)
        {
          if (match_object (child, text))
            {
              gtk_single_selection_set_selected (rl->selection, row);
              g_object_unref (child);
              g_object_unref (row_item);
              return TRUE;
            }
        }

      if (!gtk_tree_list_row_get_expanded (row_item))
        {
          result = search_children (child, text, forward);
          if (result)
            {
              select_object (rl, result);
              g_object_unref (result);
              g_object_unref (child);
              g_object_unref (row_item);
              return TRUE;
            }
        }
      g_object_unref (child);
      g_object_unref (row_item);
    }

  return FALSE;
}

static void
on_search_changed (GtkSearchEntry           *entry,
                   GtkInspectorResourceList *rl)
{
  if (!search (rl, TRUE, FALSE))
    gtk_widget_error_bell (GTK_WIDGET (rl));
}

static void
next_match (GtkButton                *button,
            GtkInspectorResourceList *rl)
{
  if (gtk_search_bar_get_search_mode (GTK_SEARCH_BAR (rl->search_bar)))
    {
      if (!search (rl, TRUE, TRUE))
        gtk_widget_error_bell (GTK_WIDGET (rl));
    }
}

static void
previous_match (GtkButton                *button,
                GtkInspectorResourceList *rl)
{
  if (gtk_search_bar_get_search_mode (GTK_SEARCH_BAR (rl->search_bar)))
    {
      if (!search (rl, FALSE, TRUE))
        gtk_widget_error_bell (GTK_WIDGET (rl));
    }
}

static void
stop_search (GtkWidget                *entry,
             GtkInspectorResourceList *rl)
{
  gtk_editable_set_text (GTK_EDITABLE (rl->search_entry), "");
  gtk_search_bar_set_search_mode (GTK_SEARCH_BAR (rl->search_bar), FALSE);
}

static char *
holder_name (gpointer item)
{
  return g_strdup (resource_holder_get_name (RESOURCE_HOLDER (item)));
}

static int
holder_count (gpointer item)
{
  return resource_holder_get_count (RESOURCE_HOLDER (item));
}

static gsize
holder_size (gpointer item)
{
  return resource_holder_get_size (RESOURCE_HOLDER (item));
}

static void
gtk_inspector_resource_list_init (GtkInspectorResourceList *rl)
{
  GtkSorter *sorter;

  gtk_widget_init_template (GTK_WIDGET (rl));

  g_signal_connect (rl, "map", G_CALLBACK (on_map), NULL);

  gtk_search_bar_connect_entry (GTK_SEARCH_BAR (rl->search_bar),
                                GTK_EDITABLE (rl->search_entry));

  sorter = GTK_SORTER (gtk_string_sorter_new (gtk_cclosure_expression_new (G_TYPE_STRING, NULL,
                                                               0, NULL,
                                                               (GCallback)holder_name,
                                                               NULL, NULL)));

  gtk_column_view_column_set_sorter (rl->path, sorter);
  g_object_unref (sorter);

  sorter = GTK_SORTER (gtk_numeric_sorter_new (gtk_cclosure_expression_new (G_TYPE_INT, NULL,
                                                                0, NULL,
                                                                (GCallback)holder_count,
                                                                NULL, NULL)));

  gtk_column_view_column_set_sorter (rl->count, sorter);
  g_object_unref (sorter);

  sorter = GTK_SORTER (gtk_numeric_sorter_new (gtk_cclosure_expression_new (G_TYPE_UINT64, NULL,
                                                                0, NULL,
                                                                (GCallback)holder_size,
                                                                NULL, NULL)));

  gtk_column_view_column_set_sorter (rl->size, sorter);
  g_object_unref (sorter);
}

static GListModel *
create_model_for_object (gpointer item, gpointer data)
{
  GListModel *model = resource_holder_get_children (RESOURCE_HOLDER (item));

  if (model)
    return g_object_ref (model);

  return NULL;
}

static void
constructed (GObject *object)
{
  GtkInspectorResourceList *rl = GTK_INSPECTOR_RESOURCE_LIST (object);
  GListModel *sort_model;
  GtkSorter *column_sorter;
  GtkSorter *sorter;
 
  g_signal_connect (rl->open_details_button, "clicked",
                    G_CALLBACK (open_details), rl);
  g_signal_connect (rl->close_details_button, "clicked",
                    G_CALLBACK (close_details), rl);
 
  rl->tree_model = gtk_tree_list_model_new (load_resources (),
                                            FALSE,
                                            FALSE,
                                            create_model_for_object,
                                            NULL,
                                            NULL);

  column_sorter = gtk_column_view_get_sorter (GTK_COLUMN_VIEW (rl->list));
  sorter = GTK_SORTER (gtk_tree_list_row_sorter_new (g_object_ref (column_sorter)));
  sort_model = G_LIST_MODEL (gtk_sort_list_model_new (g_object_ref (G_LIST_MODEL (rl->tree_model)), sorter));
  rl->selection = gtk_single_selection_new (sort_model);

  gtk_column_view_set_model (GTK_COLUMN_VIEW (rl->list), GTK_SELECTION_MODEL (rl->selection));

  g_signal_connect (rl->selection, "selection-changed", G_CALLBACK (on_selection_changed), rl);
}

static void
get_property (GObject    *object,
              guint       param_id,
              GValue     *value,
              GParamSpec *pspec)
{
  GtkInspectorResourceList *rl = GTK_INSPECTOR_RESOURCE_LIST (object);

  switch (param_id)
    {
    case PROP_BUTTONS:
      g_value_take_object (value, rl->buttons);
      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)
{
  GtkInspectorResourceList *rl = GTK_INSPECTOR_RESOURCE_LIST (object);
  
  switch (param_id)
    {
    case PROP_BUTTONS:
      rl->buttons = g_value_get_object (value);
      rl->open_details_button = gtk_stack_get_child_by_name (GTK_STACK (rl->buttons), "list");
      rl->close_details_button = gtk_stack_get_child_by_name (GTK_STACK (rl->buttons), "details");
      break;

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

static void
dispose (GObject *object)
{
  GtkInspectorResourceList *rl = GTK_INSPECTOR_RESOURCE_LIST (object);

  g_clear_pointer (&rl->stack, gtk_widget_unparent);
  g_clear_object (&rl->selection);
  g_clear_object (&rl->tree_model);

  G_OBJECT_CLASS (gtk_inspector_resource_list_parent_class)->dispose (object);
}

static void
setup_name_cb (GtkSignalListItemFactory *factory,
               GtkListItem              *list_item)
{
  GtkWidget *expander;
  GtkWidget *label;

  expander = gtk_tree_expander_new ();
  gtk_list_item_set_child (list_item, expander);

  label = gtk_label_new (NULL);
  gtk_widget_set_margin_start (label, 5);
  gtk_widget_set_margin_end (label, 5);
  gtk_label_set_xalign (GTK_LABEL (label), 0.0);
  gtk_tree_expander_set_child (GTK_TREE_EXPANDER (expander), label);
}

static void
bind_name_cb (GtkSignalListItemFactory *factory,
              GtkListItem              *list_item)
{
  GtkTreeListRow *list_row;
  GtkWidget *expander;
  GtkWidget *label;
  gpointer item;

  list_row = gtk_list_item_get_item (list_item);
  expander = gtk_list_item_get_child (list_item);
  gtk_tree_expander_set_list_row (GTK_TREE_EXPANDER (expander), list_row);
  item = gtk_tree_list_row_get_item (list_row);
  label = gtk_tree_expander_get_child (GTK_TREE_EXPANDER (expander));

  gtk_label_set_label (GTK_LABEL (label), resource_holder_get_name (RESOURCE_HOLDER (item)));
  g_object_unref (item);
}

static void
setup_size_cb (GtkSignalListItemFactory *factory,
               GtkListItem              *list_item)
{
  GtkWidget *label;

  label = gtk_label_new (NULL);
  gtk_widget_set_margin_start (label, 5);
  gtk_widget_set_margin_end (label, 5);
  gtk_label_set_xalign (GTK_LABEL (label), 1.0);
  gtk_list_item_set_child (list_item, label);
}

static void
bind_size_cb (GtkSignalListItemFactory *factory,
              GtkListItem              *list_item)
{
  GObject *item;
  GtkWidget *label;
  gsize size;
  char *text;

  item = gtk_tree_list_row_get_item (gtk_list_item_get_item (list_item));
  label = gtk_list_item_get_child (list_item);

  size = resource_holder_get_size (RESOURCE_HOLDER (item));
  text = g_format_size (size);
  gtk_label_set_label (GTK_LABEL (label), text);
  g_free (text);

  g_object_unref (item);
}

static void
setup_count_cb (GtkSignalListItemFactory *factory,
                GtkListItem              *list_item)
{
  GtkWidget *label;

  label = gtk_label_new (NULL);
  gtk_widget_set_margin_start (label, 5);
  gtk_widget_set_margin_end (label, 5);
  gtk_label_set_xalign (GTK_LABEL (label), 1.0);
  gtk_list_item_set_child (list_item, label);
}

static void
bind_count_cb (GtkSignalListItemFactory *factory,
               GtkListItem              *list_item)
{
  GObject *item;
  GtkWidget *label;
  int count;
  char *text;

  item = gtk_tree_list_row_get_item (gtk_list_item_get_item (list_item));
  label = gtk_list_item_get_child (list_item);

  count = resource_holder_get_count (RESOURCE_HOLDER (item));
  if (count > 0)
    {
      text = g_strdup_printf ("%d", count);
      gtk_label_set_label (GTK_LABEL (label), text);
      g_free (text);
    }
  else
    gtk_label_set_label (GTK_LABEL (label), "");
  g_object_unref (item);
}

static void
gtk_inspector_resource_list_class_init (GtkInspectorResourceListClass *klass)
{
  GObjectClass *object_class = G_OBJECT_CLASS (klass);
  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);

  object_class->get_property = get_property;
  object_class->set_property = set_property;
  object_class->constructed = constructed;
  object_class->dispose = dispose;

  widget_class->root = root;
  widget_class->unroot = unroot;

  g_object_class_install_property (object_class, PROP_BUTTONS,
      g_param_spec_object ("buttons", NULL, NULL,
                           GTK_TYPE_WIDGET, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));

  gtk_widget_class_set_template_from_resource (widget_class, "/org/gtk/libgtk/inspector/resource-list.ui");
  gtk_widget_class_bind_template_child (widget_class, GtkInspectorResourceList, buffer);
  gtk_widget_class_bind_template_child (widget_class, GtkInspectorResourceList, content);
  gtk_widget_class_bind_template_child (widget_class, GtkInspectorResourceList, image);
  gtk_widget_class_bind_template_child (widget_class, GtkInspectorResourceList, name_label);
  gtk_widget_class_bind_template_child (widget_class, GtkInspectorResourceList, type_label);
  gtk_widget_class_bind_template_child (widget_class, GtkInspectorResourceList, type);
  gtk_widget_class_bind_template_child (widget_class, GtkInspectorResourceList, size_label);
  gtk_widget_class_bind_template_child (widget_class, GtkInspectorResourceList, info_grid);
  gtk_widget_class_bind_template_child (widget_class, GtkInspectorResourceList, stack);
  gtk_widget_class_bind_template_child (widget_class, GtkInspectorResourceList, search_bar);
  gtk_widget_class_bind_template_child (widget_class, GtkInspectorResourceList, search_entry);
  gtk_widget_class_bind_template_child (widget_class, GtkInspectorResourceList, list);
  gtk_widget_class_bind_template_child (widget_class, GtkInspectorResourceList, path);
  gtk_widget_class_bind_template_child (widget_class, GtkInspectorResourceList, count);
  gtk_widget_class_bind_template_child (widget_class, GtkInspectorResourceList, size);

  gtk_widget_class_bind_template_callback (widget_class, on_search_changed);
  gtk_widget_class_bind_template_callback (widget_class, on_row_activated);
  gtk_widget_class_bind_template_callback (widget_class, next_match);
  gtk_widget_class_bind_template_callback (widget_class, previous_match);
  gtk_widget_class_bind_template_callback (widget_class, stop_search);
  gtk_widget_class_bind_template_callback (widget_class, setup_name_cb);
  gtk_widget_class_bind_template_callback (widget_class, bind_name_cb);
  gtk_widget_class_bind_template_callback (widget_class, setup_count_cb);
  gtk_widget_class_bind_template_callback (widget_class, bind_count_cb);
  gtk_widget_class_bind_template_callback (widget_class, setup_size_cb);
  gtk_widget_class_bind_template_callback (widget_class, bind_size_cb);

  gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT);
}

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