/*
 *  $Id: menu-recent-files.c 29971 2026-05-21 15:22:15Z yeti-dn $
 *  Copyright (C) 2003-2025 David Necas (Yeti), Petr Klapetek.
 *  E-mail: yeti@gwyddion.net, klapetek@gwyddion.net.
 *
 *  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 of the License, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#include "config.h"
#include <string.h>
#include <glib/gi18n-lib.h>
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>

#include "libgwyddion/macros.h"
#include "libgwyddion/math.h"
#include "libgwyddion/utils.h"
#include "libgwyui/gwyui.h"

#include "libgwyapp/gwyapp.h"
#include "libgwyapp/internal.h"
#include "libgwyapp/sanity.h"

static void   open_recent_file           (GObject *item);
static gchar* fix_recent_file_underscores(gchar *s);

static GtkWidget *recent_files_menu = NULL;

static void
recent_file_activated(void)
{
    static GtkWidget *recent_file_list = NULL;

    if (recent_file_list) {
        gtk_window_present(GTK_WINDOW(recent_file_list));
        return;
    }

    recent_file_list = gwy_app_recent_file_list_new();
    gtk_window_add_accel_group(GTK_WINDOW(recent_file_list), gwy_app_main_accel_group());
    g_object_add_weak_pointer(G_OBJECT(recent_file_list), (gpointer*)&recent_file_list);
    gtk_widget_show(recent_file_list);
}

static void
set_thumbnail(GtkWidget *item, const gchar *filename_utf8)
{
    GdkPixbuf *thumbnail, *menuicon;
    gint width, height, w, h;
    gdouble hscale, wscale;
    guint32 pixel;

    gtk_icon_size_lookup(GTK_ICON_SIZE_MENU, &width, &height);
    width = 3*width/2;
    height = 3*height/2;

    thumbnail = gwy_app_recent_file_get_thumbnail(filename_utf8);
    w = gdk_pixbuf_get_width(thumbnail);
    h = gdk_pixbuf_get_height(thumbnail);

    wscale = (gdouble)width/w;
    hscale = (gdouble)height/h;
    if (wscale <= hscale) {
        height = CLAMP(ceil(wscale*h), 2, height);
        hscale = (gdouble)height/h;
    }
    else {
        width = CLAMP(ceil(hscale*w), 2, width);
        wscale = (gdouble)width/w;
    }

    menuicon = gdk_pixbuf_new(GDK_COLORSPACE_RGB, gdk_pixbuf_get_has_alpha(thumbnail), 8, width + 2, height + 2);
    /* FIXME: Getting the right color as RGB is ugly. Do it later...
    gc = item->style->text_gc[GTK_STATE_NORMAL];
    */
    pixel = 0x777777ff;
    gdk_pixbuf_fill(menuicon, pixel);
    gdk_pixbuf_scale(thumbnail, menuicon, 1, 1, width, height, 1, 1, wscale, hscale, GDK_INTERP_HYPER);
    g_object_unref(thumbnail);

    gwy_set_image_menu_item_pixbuf(GTK_MENU_ITEM(item), menuicon);
    g_object_unref(menuicon);
}

/**
 * gwy_app_menu_recent_files_update:
 * @recent_files: A list of recent file names, in UTF-8.
 *
 * Updates recent file menu.
 *
 * If the list of files is longer than the maximum number of recent file menu
 * items, only the maximum number is shown.
 **/
void
gwy_app_menu_recent_files_update(GList *recent_files)
{
    static const guint accel_keys[] = {
        GDK_KEY_1, GDK_KEY_2, GDK_KEY_3, GDK_KEY_4, GDK_KEY_5, GDK_KEY_6, GDK_KEY_7, GDK_KEY_8, GDK_KEY_9, GDK_KEY_0
    };

    GtkWidget *item;
    GQuark quark;
    GList *l, *child, *children;
    gchar *s, *label, *filename;
    gint i, nrecent;

    if (!recent_files_menu)
        return;

    child = children = gtk_container_get_children(GTK_CONTAINER(recent_files_menu));
    quark = g_quark_from_string("filename");
    nrecent = _gwy_app_get_n_recent_files();
    for (i = 0, l = recent_files; l && i < nrecent; l = g_list_next(l), i++) {
        filename = (gchar*)l->data;
        s = fix_recent_file_underscores(g_path_get_basename(filename));
        label = g_strdup_printf("%s%d. %s", i < 10 ? "_" : "", i, s);
        if (child) {
            item = gtk_bin_get_child(GTK_BIN(child->data));
            gwy_debug("reusing item %p for <%s> [#%d]", item, s, i);
            gtk_label_set_text_with_mnemonic(GTK_LABEL(item), label);
            g_object_set_qdata_full(G_OBJECT(child->data), quark, g_strdup(filename), g_free);
            item = GTK_WIDGET(child->data);
            child = g_list_next(child);
        }
        else {
            item = gwy_create_image_menu_item(label, NULL, TRUE);
            gwy_debug("creating item %p for <%s> [#%d]", item, s, i);
            g_object_set_qdata_full(G_OBJECT(item), quark, g_strdup(filename), g_free);
            if (i < G_N_ELEMENTS(accel_keys)) {
                gchar accel_path[64];

                g_snprintf(accel_path, sizeof(accel_path), "<file>/Open Recent/%d", i+1);
                gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
                gtk_accel_map_add_entry(accel_path, accel_keys[i], GDK_CONTROL_MASK);
            }
            gtk_menu_shell_append(GTK_MENU_SHELL(recent_files_menu), item);
            g_signal_connect(item, "activate", G_CALLBACK(open_recent_file), NULL);
        }
        g_free(label);
        g_free(s);

        gtk_widget_show(item);
        set_thumbnail(item, filename);
    }

    /* keep a konstant number of entries, otherwise it's just too hard to manage the separated stuff at the end */
    while (i < nrecent) {
        if (child) {
            item = gtk_bin_get_child(GTK_BIN(child->data));
            gwy_debug("hiding item %p [#%d]", item, i);
            gtk_widget_hide(child->data);
            child = g_list_next(child);
        }
        else {
            item = gwy_create_image_menu_item("Hidden", NULL, TRUE);
            gwy_debug("adding hidden item %p [#%d]", item, i);
            if (i < G_N_ELEMENTS(accel_keys)) {
                gchar accel_path[64];

                g_snprintf(accel_path, sizeof(accel_path), "<file>/Open Recent/%d", i+1);
                gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
                gtk_accel_map_add_entry(accel_path, accel_keys[i], GDK_CONTROL_MASK);
            }
            gtk_menu_shell_append(GTK_MENU_SHELL(recent_files_menu), item);
            g_signal_connect(item, "activate", G_CALLBACK(open_recent_file), NULL);
        }
        i++;
    }

    /* if there are still some entries, the separated entires already exist, so we are done */
    if (child) {
        g_return_if_fail(GTK_IS_SEPARATOR_MENU_ITEM(child->data));
        g_list_free(children);
        return;
    }
    /* separator */
    item = gtk_separator_menu_item_new();
    gtk_menu_shell_append(GTK_MENU_SHELL(recent_files_menu), item);
    gtk_widget_show(item);
    /* doc history */
    item = gwy_create_image_menu_item(_("_Document History..."), GWY_ICON_GTK_OPEN, FALSE);
    gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), "<file>/Open Recent/Document History");
    gtk_accel_map_add_entry("<file>/Open Recent/Document History", GDK_KEY_H, GDK_CONTROL_MASK);
    gtk_menu_shell_append(GTK_MENU_SHELL(recent_files_menu), item);
    g_signal_connect(item, "activate", G_CALLBACK(recent_file_activated), NULL);
    gtk_widget_show(item);

    g_list_free(children);
}

static gchar*
fix_recent_file_underscores(gchar *s)
{
    gchar *s2;

    s2 = gwy_strreplace(s, "_", "__", (gsize)-1);
    g_free(s);

    return s2;
}

/**
 * gwy_app_menu_recent_files_get:
 *
 * Gets the application recent files menu.
 *
 * The menu is initially empty and can be updated with gwy_app_menu_recent_files_update().  This function is
 * essentially useful only for toolbox construction.
 *
 * Returns: The recent files menu (a #GtkMenu).
 **/
GtkWidget*
gwy_app_menu_recent_files_get(void)
{
    if (!recent_files_menu) {
        recent_files_menu = gtk_menu_new();
        gtk_menu_set_accel_group(GTK_MENU(recent_files_menu), gwy_app_main_accel_group());
        g_object_add_weak_pointer(G_OBJECT(recent_files_menu), (gpointer*)&recent_files_menu);
        gwy_app_menu_recent_files_update(NULL);
    }

    return recent_files_menu;
}

static void
open_recent_file(GObject *item)
{
    const gchar *filename_utf8;  /* in UTF-8 */

    filename_utf8 = g_object_get_data(G_OBJECT(item), "filename");
    g_return_if_fail(filename_utf8);
    gwy_app_file_load(filename_utf8, NULL, NULL);
}

/**
 * SECTION: menu-recent-files
 * @title: Recent files menu
 * @short_description: Menu with recently opened files
 **/

/* vim: set cin columns=120 tw=118 et ts=4 sw=4 cino=>1s,e0,n0,f0,{0,}0,^0,\:1s,=0,g1s,h0,t0,+1s,c3,(0,u0 : */
