/*
 * 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@yahoo.fr>
 */

/***************
 * 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"

extern options_struct *options;
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;
}

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);

    mod = (key >= GDK_A && key <= GDK_Z) ? GDK_SHIFT_MASK : 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;

    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 {
    TRANSFORMATION_MOVE_LEFT,
    TRANSFORMATION_MOVE_RIGHT,
    TRANSFORMATION_MOVE_UP,
    TRANSFORMATION_MOVE_DOWN,
    /* --- */
    TRANSFORMATION_ROTATE_BIG_PLUS,
    TRANSFORMATION_ROTATE_BIG_MINUS,
    TRANSFORMATION_ROTATE_SMALL_PLUS,
    TRANSFORMATION_ROTATE_SMALL_MINUS,
    /* --- */
    TRANSFORMATION_ZOOM_IN,
    TRANSFORMATION_ZOOM_OUT,
    TRANSFORMATION_FLIP_H,
    TRANSFORMATION_FLIP_V,
    TRANSFORMATION_RESET
};

static guchar menu_move(guchar action)
{
    switch (action) {
    case TRANSFORMATION_MOVE_LEFT:
        matrix_move(MOVE_OFFSET, 0.0);
        break;

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

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

        /* case TRANSFORMATION_MOVE_DOWN: */
    default:
        matrix_move(0.0, -MOVE_OFFSET);
    }

    return REFRESH_IMAGE | APPEND_HISTORY;
}

static guchar menu_rotate(guchar action)
{
    switch (action) {
    case TRANSFORMATION_ROTATE_BIG_PLUS:
        matrix_rotate(BIG_ROTATION);
        break;

    case TRANSFORMATION_ROTATE_BIG_MINUS:
        matrix_rotate(-BIG_ROTATION);
        break;

    case TRANSFORMATION_ROTATE_SMALL_PLUS:
        matrix_rotate(SMALL_ROTATION);
        break;

        /* case TRANSFORMATION_ROTATE_SMALL_MINUS: */
    default:
        matrix_rotate(-SMALL_ROTATION);
    }

    return REFRESH_IMAGE | REFRESH_STATUS | APPEND_HISTORY;
}

static gboolean menu_set_top_left(void)
{
    matrix_set_top_left();
    refresh(REFRESH_IMAGE | APPEND_HISTORY);
    return FALSE;
}

static void menu_zoom(guchar action)
{
    gboolean zoom_pointer;

    zoom_pointer = options->zoom_pointer;
    options->zoom_pointer = FALSE;

    if (action == TRANSFORMATION_ZOOM_IN)
        zoom_in(ZOOM_FACTOR);
    else
        zoom_in(1.0 / ZOOM_FACTOR);

    options->zoom_pointer = zoom_pointer;
}

static gboolean menu_transformation(guchar action)
{
    gint what = 0;

    switch (action) {
    case TRANSFORMATION_MOVE_LEFT:
    case TRANSFORMATION_MOVE_RIGHT:
    case TRANSFORMATION_MOVE_UP:
    case TRANSFORMATION_MOVE_DOWN:
        what = menu_move(action);
        break;

    case TRANSFORMATION_ROTATE_BIG_PLUS:
    case TRANSFORMATION_ROTATE_BIG_MINUS:
    case TRANSFORMATION_ROTATE_SMALL_PLUS:
    case TRANSFORMATION_ROTATE_SMALL_MINUS:
        what = menu_rotate(action);
        break;

    case TRANSFORMATION_ZOOM_IN:
    case TRANSFORMATION_ZOOM_OUT:
        menu_zoom(action);
        break;

    case TRANSFORMATION_FLIP_H:
        matrix_flip_h();
        what = REFRESH_IMAGE | REFRESH_STATUS | APPEND_HISTORY;
        break;

    case TRANSFORMATION_FLIP_V:
        matrix_flip_v();
        what = REFRESH_IMAGE | REFRESH_STATUS | APPEND_HISTORY;
        break;

        /* case TRANSFORMATION_RESET: */
    default:
        matrix_reset();
        what = REFRESH_IMAGE | REFRESH_STATUS | APPEND_HISTORY;
    }

    refresh(what);
    return FALSE;
}

#define SEPARATOR gtk_menu_shell_append(submenu, gtk_separator_menu_item_new())

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

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

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

    submenu = add_menu(name);

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

    ADD_MENU_ITEM(_("Rebuild this menu"), rebuild_menu, 0, 0);

    return item;
}

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

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


GtkAccelGroup *create_menus(void)
{
    GtkMenuShell *submenu;
    GtkAccelGroup *accel_group;
    GtkMenuItem *item, *current_image_item, *every_image_item;
    gchar *menu_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, 0);
    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_("Move top left"), menu_set_top_left, GDK_Home, 0);
    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);
    SEPARATOR;                  /* ------------------------------------ */
    ADD_MENU_ITEM(N_("Horizontal flip"), menu_transformation, GDK_z,
                  TRANSFORMATION_FLIP_H);

    ADD_MENU_ITEM(N_("Vertical flip"), menu_transformation, GDK_e,
                  TRANSFORMATION_FLIP_V);
    SEPARATOR;                  /* ------------------------------------ */
    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);
    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;                  /* ------------------------------------ */
    item = ADD_MENU_ITEM(N_("Start the slide show"), toggle_slide_show, 0,
                         FALSE);
    set_slide_show_menu(item);

    ADD_MENU_ITEM(N_("Remove the current file"), confirm_remove_current, GDK_x,
                  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_MENU_ITEM(N_("Move left"), menu_transformation, 0,
                  TRANSFORMATION_MOVE_LEFT);

    ADD_MENU_ITEM(N_("Move right"), menu_transformation, 0,
                  TRANSFORMATION_MOVE_RIGHT);

    ADD_MENU_ITEM(N_("Move up"), menu_transformation, 0,
                  TRANSFORMATION_MOVE_UP);

    ADD_MENU_ITEM(N_("Move down"), menu_transformation, 0,
                  TRANSFORMATION_MOVE_DOWN);

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

    ADD_MENU_ITEM(N_("Rotate +90 deg"), menu_transformation, 0,
                  TRANSFORMATION_ROTATE_BIG_PLUS);

    ADD_MENU_ITEM(N_("Rotate -90 deg"), menu_transformation, 0,
                  TRANSFORMATION_ROTATE_BIG_MINUS);

    ADD_MENU_ITEM(N_("Rotate +0.1 deg"), menu_transformation, 0,
                  TRANSFORMATION_ROTATE_SMALL_PLUS);

    ADD_MENU_ITEM(N_("Rotate -0.1 deg"), menu_transformation, 0,
                  TRANSFORMATION_ROTATE_SMALL_MINUS);

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

    ADD_MENU_ITEM(N_("Zoom in"), menu_transformation, 0,
                  TRANSFORMATION_ZOOM_IN);

    ADD_MENU_ITEM(N_("Zoom out"), menu_transformation, 0,
                  TRANSFORMATION_ZOOM_OUT);

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

    ADD_MENU_ITEM(N_("Reset"), menu_transformation, GDK_r,
                  TRANSFORMATION_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;
}
