/*
 * 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * See the COPYING file for license information.
 *
 * Guillaume Chazarain <guichaz@gmail.com>
 */

/***************
 * The menubar *
 ***************/

#include <gdk/gdkkeysyms.h>     /* GDK_* */

#include "gliv.h"
#include "menus.h"
#include "options.h"
#include "rendering.h"
#include "rcfile.h"
#include "mnemonics.h"
#include "params.h"
#include "next_image.h"
#include "windows.h"
#include "matrix.h"
#include "math_floats.h"
#include "files_list.h"
#include "messages.h"
#include "open_dialog.h"
#include "main.h"
#include "history.h"
#include "scrollbars.h"
#include "cursors.h"
#include "help.h"
#include "images_menus.h"
#include "collection.h"
#include "ipc.h"
#include "actions.h"
#include "tree.h"
#include "tree_browser.h"
#include "gliv-image.h"

extern options_struct *options;
extern GlivImage *current_image;
extern GtkMenuBar *menu_bar;

void set_menu_label(GtkMenuItem * item, const gchar * text, gboolean mnemonic)
{
    GList *children;
    GtkLabel *label;

    /* Find the label to change. */
    children = gtk_container_get_children(GTK_CONTAINER(item));
    while (children != NULL && GTK_IS_ACCEL_LABEL(children->data) == FALSE)
        children = children->next;

    if (children == NULL)
        return;

    label = children->data;

    if (mnemonic) {
        push_mnemonics();
        gtk_label_set_text_with_mnemonic(GTK_LABEL(label), add_mnemonic(text));
        pop_mnemonics();
    } else
        gtk_label_set_text(GTK_LABEL(label), text);
}

static GtkMenuShell *add_menu(const gchar * name)
{
    GtkMenuShell *menu;
    GtkMenuItem *item;
    static gboolean first_time = TRUE;

    if (first_time) {
        reset_mnemonics();
        first_time = FALSE;
    } else
        pop_mnemonics();

    name = add_mnemonic(_(name));
    item = GTK_MENU_ITEM(gtk_menu_item_new_with_mnemonic(name));
    gtk_menu_shell_append(GTK_MENU_SHELL(menu_bar), GTK_WIDGET(item));

    menu = GTK_MENU_SHELL(gtk_menu_new());
    gtk_menu_shell_append(menu, gtk_tearoff_menu_item_new());
    gtk_menu_item_set_submenu(item, GTK_WIDGET(menu));

    push_mnemonics();
    return menu;
}

/* Should not collide with a keysym */
#define CTRL (1 << 31)

static GtkMenuItem *add_menu_item(GtkMenuShell * menu,
                                  const gchar * menu_name, const gchar * name,
                                  GCallback func, guint key, gpointer data)
{
    GtkMenuItem *menu_item;
    GdkModifierType mod;
    gchar *accel_path;
    const gchar *menu_item_name;

    menu_item_name = add_mnemonic(_(name));
    menu_item = GTK_MENU_ITEM(gtk_menu_item_new_with_mnemonic(menu_item_name));

    gtk_menu_shell_append(menu, GTK_WIDGET(menu_item));
    if (!func)
        return menu_item;

    g_signal_connect_swapped(menu_item, "activate", func, data);

    if (key >= GDK_A && key <= GDK_Z)
        mod = GDK_SHIFT_MASK;
    else if (key & CTRL) {
        key &= ~CTRL;
        mod = GDK_CONTROL_MASK;
    } else
        mod = 0;

    accel_path = g_strconcat("<GLiv>/", menu_name, "/", name, NULL);
    gtk_accel_map_add_entry(accel_path, key, mod);
    gtk_menu_item_set_accel_path(menu_item, accel_path);
    g_free(accel_path);

    return menu_item;
}

static gboolean toggle_alpha_checks(void)
{
    options->alpha_checks ^= TRUE;
    refresh(REFRESH_IMAGE);

    return FALSE;
}

static gboolean menu_fullscreen(void)
{
    toggle_fullscreen(!options->fullscreen);
    return FALSE;
}

#define MAXIMIZE   (1 << 0)
#define SCALE_DOWN (1 << 1)

static gboolean menu_maximize(gint what)
{
    gboolean old_maximize, old_scale_down;

    if (current_image == NULL)
        return FALSE;

    old_maximize = options->maximize;
    old_scale_down = options->scaledown;

    options->maximize = ((what & MAXIMIZE) != 0);
    options->scaledown = ((what & SCALE_DOWN) != 0);

    matrix_set_max_zoom(-1, -1, TRUE);

    options->maximize = old_maximize;
    options->scaledown = old_scale_down;

    refresh(REFRESH_IMAGE | REFRESH_STATUS | APPEND_HISTORY);
    return FALSE;
}

enum transfo {
    /* Must be kept in sync with 'enum image_position' */
    TRANSFO_CENTER = 0,
    TRANSFO_TOP_LEFT,
    TRANSFO_TOP_RIGHT,
    TRANSFO_BOTTOM_LEFT,
    TRANSFO_BOTTOM_RIGHT,
    /* --- */
    TRANSFO_MOVE_LEFT,
    TRANSFO_MOVE_RIGHT,
    TRANSFO_MOVE_UP,
    TRANSFO_MOVE_DOWN,
    /* --- */
    TRANSFO_ROTATE_BIG_PLUS,
    TRANSFO_ROTATE_BIG_MINUS,
    TRANSFO_ROTATE_SMALL_PLUS,
    TRANSFO_ROTATE_SMALL_MINUS,
    /* --- */
    TRANSFO_ZOOM_IN,
    TRANSFO_ZOOM_OUT,
    TRANSFO_FIT_WIDTH,
    TRANSFO_FIT_HEIGHT,
    TRANSFO_FLIP_H,
    TRANSFO_FLIP_V,
    TRANSFO_RESET,
    /* --- */
};

static gboolean menu_transfo(enum transfo transfo)
{
    if (current_image == NULL)
        return FALSE;

    switch (transfo) {
    case TRANSFO_CENTER:
    case TRANSFO_TOP_LEFT:
    case TRANSFO_TOP_RIGHT:
    case TRANSFO_BOTTOM_LEFT:
    case TRANSFO_BOTTOM_RIGHT:
        {
            enum image_position old_pos = options->initial_pos;
            options->initial_pos = transfo;
            matrix_set_initial_position();
            options->initial_pos = old_pos;
            break;
        }

        /* --- */

    case TRANSFO_MOVE_LEFT:
        matrix_move(MOVE_OFFSET, 0.0);
        break;

    case TRANSFO_MOVE_RIGHT:
        matrix_move(-MOVE_OFFSET, 0.0);
        break;

    case TRANSFO_MOVE_UP:
        matrix_move(0.0, MOVE_OFFSET);
        break;

    case TRANSFO_MOVE_DOWN:
        matrix_move(0.0, -MOVE_OFFSET);
        break;

        /* --- */

    case TRANSFO_ROTATE_BIG_PLUS:
        matrix_rotate(BIG_ROTATION);
        break;

    case TRANSFO_ROTATE_BIG_MINUS:
        matrix_rotate(-BIG_ROTATION);
        break;

    case TRANSFO_ROTATE_SMALL_PLUS:
        matrix_rotate(SMALL_ROTATION);
        break;

    case TRANSFO_ROTATE_SMALL_MINUS:
        matrix_rotate(-SMALL_ROTATION);
        break;

        /* --- */

    case TRANSFO_ZOOM_IN:
    case TRANSFO_ZOOM_OUT:
        {
            gboolean zoom_pointer = options->zoom_pointer;
            options->zoom_pointer = FALSE;

            if (transfo == TRANSFO_ZOOM_IN)
                zoom_in(ZOOM_FACTOR);
            else
                zoom_in(1.0 / ZOOM_FACTOR);

            options->zoom_pointer = zoom_pointer;
            break;
        }

    case TRANSFO_FIT_WIDTH:
        matrix_fit(TRUE, FALSE);
        break;

    case TRANSFO_FIT_HEIGHT:
        matrix_fit(FALSE, TRUE);
        break;

    case TRANSFO_FLIP_H:
        matrix_flip_h();
        break;

    case TRANSFO_FLIP_V:
        matrix_flip_v();
        break;

    case TRANSFO_RESET:
        matrix_reset();
        break;

    default:
        g_printerr("Unknown transformation: %d\n", transfo);
        return FALSE;
    }

    refresh(REFRESH_IMAGE | REFRESH_STATUS | APPEND_HISTORY);
    return FALSE;
}

#define SEPARATOR gtk_menu_shell_append(menu, gtk_separator_menu_item_new())

#define ADD_MENU_ITEM(str, signal_func, key, val)                     \
    add_menu_item(menu, menu_name, str, G_CALLBACK(signal_func), key, \
                  GINT_TO_POINTER(val))

#define ADD_MENU(str)                                          \
    do {                                                       \
        menu = add_menu(str);                                  \
        menu_name = str;                                       \
        gtk_menu_set_accel_group(GTK_MENU(menu), accel_group); \
    } while (0)

/* Used to add an images menu. */
static GtkMenuItem *add_special_menu(const gchar * name, GCallback rebuild_menu)
{
    GList *children;
    GtkMenuShell *menu;
    GtkMenuItem *item;
    gchar *menu_name = NULL;

    menu = add_menu(name);

    children = gtk_container_get_children(GTK_CONTAINER(menu));
    item = GTK_MENU_ITEM(gtk_menu_get_attach_widget(GTK_MENU(menu)));
    g_list_free(children);

    ADD_MENU_ITEM(_("Rebuild this menu"), rebuild_menu, 0, 0);
    ADD_MENU_ITEM(_("Open thumbnails browser..."), show_tree_browser, 0, 0);

    return item;
}

#define ADD_SPECIAL_MENU(str, cb) cb(add_special_menu(str, G_CALLBACK(cb)))

static GtkMenuShell *add_submenu(GtkMenuShell * menu, const gchar * name)
{
    GtkMenuItem *item;
    GtkMenu *submenu;

    item = add_menu_item(menu, NULL, name, NULL, 0, NULL);
    submenu = GTK_MENU(gtk_menu_new());

    gtk_menu_item_set_submenu(item, GTK_WIDGET(submenu));
    gtk_menu_shell_append(GTK_MENU_SHELL(submenu), gtk_tearoff_menu_item_new());
    push_mnemonics();

    return GTK_MENU_SHELL(submenu);
}


#define ADD_SUBMENU(name)                                         \
    do {                                                          \
        submenu = add_submenu(menu, name);                        \
        gtk_menu_set_accel_group(GTK_MENU(submenu), accel_group); \
        submenu_name = g_strconcat(menu_name, "/", name, NULL);   \
    } while (0)

#define END_SUBMENU()         \
    do {                      \
        pop_mnemonics();      \
        g_free(submenu_name); \
    } while (0)

#define ADD_SUBMENU_ITEM(name, action, key)                                   \
    add_menu_item(submenu, submenu_name, name, G_CALLBACK(menu_transfo), key, \
                  GINT_TO_POINTER(action))

/* GtkAction are not supported in gtk+-2 < 2.3 */
extern const guint gtk_minor_version;


GtkAccelGroup *create_menus(void)
{
    GtkMenuShell *menu, *submenu;
    GtkAccelGroup *accel_group;
    GtkMenuItem *item, *current_image_item, *every_image_item;
    gchar *menu_name, *submenu_name;

    menu_bar = GTK_MENU_BAR(gtk_menu_bar_new());

    /* This will go away when we support actions. */
    if (gtk_minor_version >= 3)
        g_signal_connect(menu_bar, "can-activate-accel", G_CALLBACK(gtk_true),
                         NULL);

    accel_group = gtk_accel_group_new();

    gtk_settings_set_long_property(gtk_settings_get_default(),
                                   "gtk-can-change-accels", 1, "GLiv");

    /**************/
    /* File menu. */
    ADD_MENU(N_("File"));
    ADD_MENU_ITEM(N_("Open..."), menu_open, GDK_o, FALSE);
    ADD_MENU_ITEM(N_("Open directory..."), menu_open, GDK_O, TRUE);
    SEPARATOR;                  /* ------------------------------------ */
    ADD_MENU_ITEM(N_("Load collection..."), deserialize_collection, 0, 0);
    ADD_MENU_ITEM(N_("Save collection..."), serialize_collection_gui, 0, 0);
    SEPARATOR;                  /* ------------------------------------ */
    ADD_MENU_ITEM(N_("Quit"), gui_quit, GDK_q, 0);

    /******************/
    /* Commands menu. */
    ADD_MENU(N_("Commands"));
    ADD_MENU_ITEM(N_("Load previous image"), load_direction, GDK_p, -1);
    ADD_MENU_ITEM(N_("Load next image"), load_direction, GDK_n, 1);
    ADD_MENU_ITEM(N_("Load the Nth image..."), menu_image_nr, GDK_g, 0);
    ADD_MENU_ITEM(N_("Load first image"), load_1st_image, GDK_comma, 0);
    ADD_MENU_ITEM(N_("Load last image"), load_last_image, GDK_period, 0);
    ADD_MENU_ITEM(N_("Load random image"), load_random_image, GDK_x, 0);
    ADD_MENU_ITEM(N_("Reload the current image"), reload_current_image, 0, 0);
    SEPARATOR;                  /* ------------------------------------ */
    ADD_MENU_ITEM(N_("Sort images list"), reorder_files, 0, FALSE);
    ADD_MENU_ITEM(N_("Shuffle images list"), reorder_files, 0, TRUE);
    SEPARATOR;                  /* ------------------------------------ */
    ADD_MENU_ITEM(N_("Rebuild images menus"), rebuild_images_menus, 0, 0);
    item = ADD_MENU_ITEM(N_("Stop rebuilding the images menu"),
                         cancel_using_tree, 0, 0);
    set_stop_rebuilding_menu(item);
    SEPARATOR;                  /* ------------------------------------ */
    ADD_MENU_ITEM(N_("Toggle floating windows"), toggle_floating_windows,
                  GDK_w, 0);
    item = ADD_MENU_ITEM(N_("Start the slide show"), toggle_slide_show,
                         GDK_Pause, 0);
    set_slide_show_menu(item);

    ADD_MENU_ITEM(N_("Remove the current file"), confirm_remove_current,
                  GDK_Delete, 0);
    SEPARATOR;                  /* ------------------------------------ */
    ADD_MENU_ITEM(N_("Edit actions..."), edit_actions, 0, 0);
    current_image_item = ADD_MENU_ITEM(_("Action on the current image"),
                                       NULL, 0, 0);
    every_image_item = ADD_MENU_ITEM(_("Action on every image"), NULL, 0, 0);

    /*************************/
    /* Transformations menu. */
    ADD_MENU(N_("Transformations"));
    ADD_MENU_ITEM(N_("Undo"), undo, GDK_u, 0);
    ADD_MENU_ITEM(N_("Redo"), redo, GDK_y, 0);
    ADD_MENU_ITEM(N_("Clear history"), clear_history, GDK_c, 0);
    SEPARATOR;                  /* ------------------------------------ */
    ADD_SUBMENU(N_("Move"));
    ADD_SUBMENU_ITEM(N_("Move left"), TRANSFO_MOVE_LEFT, GDK_Left);
    ADD_SUBMENU_ITEM(N_("Move right"), TRANSFO_MOVE_RIGHT, GDK_Right);
    ADD_SUBMENU_ITEM(N_("Move up"), TRANSFO_MOVE_UP, GDK_Up);
    ADD_SUBMENU_ITEM(N_("Move down"), TRANSFO_MOVE_DOWN, GDK_Down);
    END_SUBMENU();

    ADD_SUBMENU(N_("Set position"));
    ADD_SUBMENU_ITEM(N_("Center"), TRANSFO_CENTER, 0);
    ADD_SUBMENU_ITEM(N_("Top left"), TRANSFO_TOP_LEFT, GDK_Home);
    ADD_SUBMENU_ITEM(N_("Top right"), TRANSFO_TOP_RIGHT, 0);
    ADD_SUBMENU_ITEM(N_("Bottom left"), TRANSFO_BOTTOM_LEFT, 0);
    ADD_SUBMENU_ITEM(N_("Bottom right"), TRANSFO_BOTTOM_RIGHT, 0);
    END_SUBMENU();

    ADD_SUBMENU(N_("Rotation"));
    ADD_SUBMENU_ITEM(N_("Rotate +90 deg"), TRANSFO_ROTATE_BIG_PLUS,
                     CTRL | GDK_Up);
    ADD_SUBMENU_ITEM(N_("Rotate -90 deg"), TRANSFO_ROTATE_BIG_MINUS,
                     CTRL | GDK_Down);
    ADD_SUBMENU_ITEM(N_("Rotate +0.1 deg"), TRANSFO_ROTATE_SMALL_PLUS,
                     CTRL | GDK_Left);
    ADD_SUBMENU_ITEM(N_("Rotate -0.1 deg"), TRANSFO_ROTATE_SMALL_MINUS,
                     CTRL | GDK_Right);
    END_SUBMENU();

    SEPARATOR;                  /* ------------------------------------ */

    ADD_MENU_ITEM(N_("Horizontal flip"), menu_transfo, GDK_z, TRANSFO_FLIP_H);

    ADD_MENU_ITEM(N_("Vertical flip"), menu_transfo, GDK_e, TRANSFO_FLIP_V);

    SEPARATOR;                  /* ------------------------------------ */

    ADD_MENU_ITEM(N_("Maximize small image"), menu_maximize, GDK_M, MAXIMIZE);
    ADD_MENU_ITEM(N_("Scale down large image"), menu_maximize, GDK_l,
                  SCALE_DOWN);
    ADD_MENU_ITEM(N_("Image fit window"), menu_maximize, GDK_m,
                  MAXIMIZE | SCALE_DOWN);

    ADD_MENU_ITEM(N_("Zoom in"), menu_transfo, GDK_plus, TRANSFO_ZOOM_IN);
    ADD_MENU_ITEM(N_("Zoom out"), menu_transfo, GDK_minus, TRANSFO_ZOOM_OUT);
    ADD_MENU_ITEM(N_("Fit width"), menu_transfo, 0, TRANSFO_FIT_WIDTH);
    ADD_MENU_ITEM(N_("Fit height"), menu_transfo, 0, TRANSFO_FIT_HEIGHT);

    SEPARATOR;                  /* ------------------------------------ */

    ADD_MENU_ITEM(N_("Reset"), menu_transfo, GDK_r, TRANSFO_RESET);

    /*****************/
    /* Options menu. */
    ADD_MENU(N_("Options"));
    ADD_MENU_ITEM(N_("Toggle Fullscreen mode"), menu_fullscreen, GDK_f, 0);
    ADD_MENU_ITEM(N_("Toggle Menu Bar"), toggle_menu_bar, GDK_b, 0);
    ADD_MENU_ITEM(N_("Toggle Status Bar"), toggle_status_bar, GDK_i, 0);
    ADD_MENU_ITEM(N_("Toggle Scrollbars"), toggle_scrollbars, GDK_s, 0);
    ADD_MENU_ITEM(N_("Toggle Alpha checks"), toggle_alpha_checks, GDK_a, 0);
    SEPARATOR;                  /* ------------------------------------ */
    ADD_MENU_ITEM(N_("Hide the cursor"), hide_cursor, GDK_d, 0);
    ADD_MENU_ITEM(N_("Set this window as server"), start_server, 0, 0);
    SEPARATOR;                  /* ------------------------------------ */
    ADD_MENU_ITEM(N_("Options..."), show_options, GDK_t, 0);

    /*********************/
    /* Directories menu. */
    ADD_SPECIAL_MENU(N_("Directories"), rebuild_directories);

    /****************/
    /* Images menu. */
    ADD_SPECIAL_MENU(N_("Images"), rebuild_images);

    /*******************************/
    /* Menus rebuilding indicator. */
    item = GTK_MENU_ITEM(gtk_menu_item_new_with_label(""));
    set_rebuilding_entry(item);
    gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE);
    gtk_menu_shell_append(GTK_MENU_SHELL(menu_bar), GTK_WIDGET(item));

    /**************/
    /* Help menu. */
    ADD_MENU(N_("Help"));
    ADD_MENU_ITEM(N_("About..."), show_about_box, 0, 0);
    ADD_MENU_ITEM(N_("Controls..."), toggle_help, GDK_h, 0);

    load_accelerators();

    load_actions();
    init_actions(accel_group, current_image_item, every_image_item);

    return accel_group;
}
