/*
 * This file is part of YAD.
 *
 * YAD 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 3 of the License, or
 * (at your option) any later version.
 *
 * YAD 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 YAD. If not, see <http://www.gnu.org/licenses/>.
 *
 * Copyright (C) 2010-2025, Victor Ananjevsky <victor@sanana.kiev.ua>
 */

#include <config.h>
#include <locale.h>
#include <gtk/gtk.h>
#include <glib/gi18n.h>
#include <gdk/gdkkeysyms.h>

typedef struct {
  GtkWidget *win;
  GtkWidget *image;
  GtkWidget *lname;
  GtkWidget *lsize;
  GtkWidget *lfile;
  GtkWidget *cat_list;
  GtkWidget *icon_list;

  GtkIconTheme *theme;

  GHashTable *icons;
} IconBrowserData;

static gboolean show_regular = TRUE;
static gboolean show_symbolic = FALSE;

static gboolean
key_press_cb (GtkWidget * w, GdkEventKey * ev, gpointer data)
{
  if (ev->keyval == GDK_KEY_Escape)
    {
      gtk_main_quit ();
      return TRUE;
    }
  return FALSE;
}

static GtkListStore *
load_icon_cat (IconBrowserData * data, gchar * cat)
{
  GtkListStore *store;
  GList *i, *icons;
  gint size, w, h;

  gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, &w, &h);
  size = MIN (w, h);

  store = gtk_list_store_new (2, GDK_TYPE_PIXBUF, G_TYPE_STRING);

  icons = gtk_icon_theme_list_icons (data->theme, cat);
  for (i = icons; i; i = i->next)
    {
      GtkTreeIter iter;
      GdkPixbuf *pb, *spb;
      GtkIconInfo *info;

      info = gtk_icon_theme_lookup_icon (data->theme, i->data, size, 0);
      if (gtk_icon_info_is_symbolic (info))
        {
          if (!show_symbolic)
            continue;
        }
      else
        {
          if (!show_regular)
            continue;
        }
      g_object_unref (info);

      spb = pb = gtk_icon_theme_load_icon (data->theme, i->data, size, 0, NULL);

      if (pb)
        {
          /* scale pixbuf if needed */
          w = gdk_pixbuf_get_width (pb);
          h = gdk_pixbuf_get_height (pb);
          if (w > size || h > size)
            {
              pb = gdk_pixbuf_scale_simple (spb, size, size, GDK_INTERP_BILINEAR);
              g_object_unref (spb);
            }
        }

      gtk_list_store_append (store, &iter);
      gtk_list_store_set (store, &iter, 0, pb, 1, i->data, -1);

      if (pb)
        g_object_unref (pb);
    }
  g_list_free_full (icons, g_free);

  gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store), 1, GTK_SORT_ASCENDING);

  return store;
}

static void
print_icon (GtkTreeView *tv, GtkTreePath *path, GtkTreeViewColumn *col, IconBrowserData *data)
{
  GtkTreeModel *model;
  GtkTreeIter iter;
  gchar *icon;

  model = gtk_tree_view_get_model (tv);
  gtk_tree_model_get_iter (model, &iter, path);
  gtk_tree_model_get (model, &iter, 1, &icon, -1);

  g_print ("%s\n", icon);

  gtk_main_quit ();
}

static void
select_icon (GtkTreeSelection * sel, IconBrowserData * data)
{
  GtkTreeModel *model;
  GtkTreeIter iter;
  GtkIconInfo *info;
  gint *sz, i;
  gchar *icon, *file;
  GString *sizes;

  if (!gtk_tree_selection_get_selected (sel, &model, &iter))
    return;

  gtk_tree_model_get (model, &iter, 1, &icon, -1);

  gtk_image_set_from_icon_name (GTK_IMAGE (data->image), icon, GTK_ICON_SIZE_DIALOG);

  sz = gtk_icon_theme_get_icon_sizes (data->theme, icon);
  info = gtk_icon_theme_lookup_icon (data->theme, icon, sz[0], 0);

  if (info)
    file = (gchar *) gtk_icon_info_get_filename (info);
  else
    file = NULL;

  /* create sizes string */
  i = 0;
  sizes = g_string_new ("");
  while (sz[i])
    {
      if (sz[i] == -1)
        g_string_append (sizes, _("scalable "));
      else
        g_string_append_printf (sizes, "%dx%d ", sz[i], sz[i]);
      i++;
    }
  /* free memory */
  g_free (sz);

  gtk_label_set_text (GTK_LABEL (data->lname), icon);
  gtk_label_set_text (GTK_LABEL (data->lsize), sizes->str);
  gtk_label_set_text (GTK_LABEL (data->lfile), file ? file : _("built-in"));

  g_string_free (sizes, TRUE);

  if (info)
    g_object_unref (info);
}

static void
select_cat (GtkTreeSelection * sel, IconBrowserData * data)
{
  GtkTreeModel *model;
  GtkListStore *store;
  GtkTreeIter iter;
  gchar *cat;

  if (!gtk_tree_selection_get_selected (sel, &model, &iter))
    return;

  gtk_tree_model_get (model, &iter, 0, &cat, -1);

  store = g_hash_table_lookup (data->icons, cat);
  if (!store)
    {
      store = load_icon_cat (data, cat);
      g_hash_table_insert (data->icons, cat, store);
    }
  gtk_tree_view_set_model (GTK_TREE_VIEW (data->icon_list), GTK_TREE_MODEL (store));
}

gint
main (gint argc, gchar * argv[])
{
  IconBrowserData *data;
  gchar **themes = NULL;
  GList *ic, *icat;
  GtkListStore *store;
  GtkTreeSelection *sel;
  GtkTreeViewColumn *col;
  GtkCellRenderer *r;
  GtkWidget *w, *p, *box, *t;
  gboolean all = FALSE, symbolic = FALSE, interactive = FALSE;

  GError *err = NULL;
  GOptionContext *ctx;

  GOptionEntry entrs[] = {
    { "all", 'a', 0, G_OPTION_ARG_NONE, &all, _("Show all icons"), NULL },
    { "symbolic", 's', 0, G_OPTION_ARG_NONE, &symbolic, _("Show only symbolic icons"), NULL },
    { "interactive", 'i', 0, G_OPTION_ARG_NONE, &interactive, _("Select and print icon on double-click"), NULL },
    { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_STRING_ARRAY, &themes, NULL, N_("THEME") },
    { NULL }
  };

  setlocale (LC_ALL, "");

#ifdef ENABLE_NLS
  bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
  bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
  textdomain (GETTEXT_PACKAGE);
#endif

  /* Parse the command line arguments */
  ctx = g_option_context_new (_("- Icon browser"));
  g_option_context_set_ignore_unknown_options (ctx, TRUE);
  g_option_context_add_main_entries (ctx, entrs, GETTEXT_PACKAGE);
  g_option_context_add_group (ctx, gtk_get_option_group (TRUE));
  if (!g_option_context_parse (ctx, &argc, &argv, &err))
    {
      g_printerr ("Option parsing failed: %s\n", err->message);
      return 1;
    }

  data = g_new0 (IconBrowserData, 1);

  if (all)
    {
      show_regular = TRUE;
      show_symbolic = TRUE;
    }
  else if (symbolic)
    {
      show_regular = FALSE;
      show_symbolic = TRUE;
    }

  /* load icon theme */
  data->theme = gtk_icon_theme_get_default ();
  if (themes && themes[0])
    {
      data->theme = gtk_icon_theme_new ();
      gtk_icon_theme_set_custom_theme (data->theme, themes[0]);
    }

  /* create interface */
  data->win = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  gtk_window_set_title (GTK_WINDOW (data->win), _("Icon browser"));
  gtk_window_set_icon_name (GTK_WINDOW (data->win), "gtk-info");
  gtk_window_set_default_size (GTK_WINDOW (data->win), 500, 400);
  g_signal_connect (G_OBJECT (data->win), "delete-event", G_CALLBACK (gtk_main_quit), NULL);
  g_signal_connect (G_OBJECT (data->win), "key-press-event", G_CALLBACK (key_press_cb), NULL);

  box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 5);
  gtk_container_add (GTK_CONTAINER (data->win), box);
  gtk_container_set_border_width (GTK_CONTAINER (data->win), 5);

  /* create icon info box */
  t = gtk_grid_new ();
  gtk_grid_set_row_spacing (GTK_GRID (t), 5);
  gtk_grid_set_column_spacing (GTK_GRID (t), 5);
  gtk_box_pack_start (GTK_BOX (box), t, FALSE, FALSE, 2);

  data->image = gtk_image_new_from_icon_name ("image-missing", GTK_ICON_SIZE_DIALOG);
  gtk_grid_attach (GTK_GRID (t), data->image, 0, 0, 1, 3);

  w = gtk_label_new (NULL);
  gtk_label_set_markup (GTK_LABEL (w), _("<b>Name:</b>"));
  gtk_label_set_xalign (GTK_LABEL (w), 0.0);
  gtk_grid_attach (GTK_GRID (t), w, 1, 0, 1, 1);

  data->lname = gtk_label_new (NULL);
  gtk_label_set_selectable (GTK_LABEL (data->lname), TRUE);
  gtk_label_set_xalign (GTK_LABEL (data->lname), 0.0);
  gtk_grid_attach (GTK_GRID (t), data->lname, 2, 0, 1, 1);
  gtk_widget_set_hexpand (data->lname, TRUE);

  w = gtk_label_new (NULL);
  gtk_label_set_markup (GTK_LABEL (w), _("<b>Sizes:</b>"));
  gtk_label_set_xalign (GTK_LABEL (w), 0.0);
  gtk_grid_attach (GTK_GRID (t), w, 1, 1, 1, 1);

  data->lsize = gtk_label_new (NULL);
  gtk_label_set_selectable (GTK_LABEL (data->lsize), TRUE);
  gtk_label_set_xalign (GTK_LABEL (data->lsize), 0.0);
  gtk_grid_attach (GTK_GRID (t), data->lsize, 2, 1, 1, 1);
  gtk_widget_set_hexpand (data->lsize, TRUE);

  w = gtk_label_new (NULL);
  gtk_label_set_markup (GTK_LABEL (w), _("<b>Filename:</b>"));
  gtk_label_set_xalign (GTK_LABEL (w), 0.0);
  gtk_grid_attach (GTK_GRID (t), w, 1, 2, 1, 1);

  data->lfile = gtk_label_new (NULL);
  gtk_label_set_selectable (GTK_LABEL (data->lfile), TRUE);
  gtk_label_set_xalign (GTK_LABEL (data->lfile), 0.0);
  gtk_grid_attach (GTK_GRID (t), data->lfile, 2, 2, 1, 1);
  gtk_widget_set_hexpand (data->lfile, TRUE);

  /* create icon browser */
  p = gtk_paned_new (GTK_ORIENTATION_HORIZONTAL);
  gtk_paned_set_position (GTK_PANED (p), 150);
  gtk_box_pack_start (GTK_BOX (box), p, TRUE, TRUE, 2);

  /* create category list */
  w = gtk_scrolled_window_new (NULL, NULL);
  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (w), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
  gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (w), GTK_SHADOW_ETCHED_IN);
  gtk_paned_add1 (GTK_PANED (p), w);

  store = gtk_list_store_new (1, G_TYPE_STRING);

  data->cat_list = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));
  gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (data->cat_list), TRUE);
  gtk_container_add (GTK_CONTAINER (w), data->cat_list);

  sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (data->cat_list));
  g_signal_connect (G_OBJECT (sel), "changed", G_CALLBACK (select_cat), data);

  r = gtk_cell_renderer_text_new ();
  col = gtk_tree_view_column_new_with_attributes (_("Category"), r, "text", 0, NULL);
  gtk_tree_view_column_set_expand (col, TRUE);
  gtk_tree_view_append_column (GTK_TREE_VIEW (data->cat_list), col);

  /* load icons category */
  data->icons = g_hash_table_new (g_direct_hash, g_direct_equal);
  icat = gtk_icon_theme_list_contexts (data->theme);
  for (ic = icat; ic; ic = ic->next)
    {
      GtkTreeIter iter;

      gtk_list_store_append (store, &iter);
      gtk_list_store_set (store, &iter, 0, ic->data, -1);
      g_free (ic->data);
    }
  g_list_free (icat);

  /* create icons list */
  w = gtk_scrolled_window_new (NULL, NULL);
  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (w), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
  gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (w), GTK_SHADOW_ETCHED_IN);
  gtk_paned_add2 (GTK_PANED (p), w);

  data->icon_list = gtk_tree_view_new ();
  gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (data->icon_list), TRUE);
  gtk_container_add (GTK_CONTAINER (w), data->icon_list);

  sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (data->icon_list));
  g_signal_connect (G_OBJECT (sel), "changed", G_CALLBACK (select_icon), data);

  if (interactive)
    g_signal_connect (G_OBJECT (data->icon_list), "row-activated", G_CALLBACK (print_icon), data);

  col = gtk_tree_view_column_new ();
  gtk_tree_view_column_set_title (col, _("Icons"));
  gtk_tree_view_column_set_sort_column_id (col, 1);
  r = gtk_cell_renderer_pixbuf_new ();
  gtk_tree_view_column_pack_start (col, r, FALSE);
  gtk_tree_view_column_set_attributes (col, r, "pixbuf", 0, NULL);
  r = gtk_cell_renderer_text_new ();
  gtk_tree_view_column_pack_start (col, r, FALSE);
  gtk_tree_view_column_set_attributes (col, r, "text", 1, NULL);
  gtk_tree_view_column_set_expand (col, TRUE);
  gtk_tree_view_append_column (GTK_TREE_VIEW (data->icon_list), col);

  gtk_widget_show_all (data->win);

  /* run it */
  gtk_main ();

  return 0;
}
