/* window action menu (ops on a single window) */

/*
 * Copyright (C) 2001 Havoc Pennington
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <libxfcegui4/netk-window-action-menu.h>
#include <libxfcegui4/xfce-internals.h>

typedef enum
{
    CLOSE,
    MINIMIZE,
    MAXIMIZE,
    SHADE,
    PIN,
    SENDTO
} WindowAction;

typedef struct _ActionMenuData ActionMenuData;

struct _ActionMenuData
{
    NetkWindow *window;
    GtkWidget *menu;
    GtkWidget *minimize_item;
    GtkWidget *maximize_item;
    GtkWidget *shade_item;
    GtkWidget *sendto_item;
    GtkWidget *close_item;
    GtkWidget *pin_item;
    guint idle_handler;
};

static void object_weak_notify (gpointer data,
                                GObject *obj);
static void window_weak_notify (gpointer data,
                                GObject *window);

static void
window_weak_notify (gpointer data,
                    GObject *window)
{
    g_object_set_data (G_OBJECT (data), "netk-action-data", NULL);
    g_object_weak_unref (G_OBJECT (data),
                         object_weak_notify,
                         window);
}


static void
object_weak_notify (gpointer data,
                    GObject *obj)
{
    g_object_weak_unref (G_OBJECT (data),
                         window_weak_notify,
                         obj);
}

static void
set_data (GObject        *obj,
          ActionMenuData *amd)
{
    g_object_set_data (obj, "netk-action-data", amd);
    if (amd && amd->window)
    {
        g_object_weak_ref (G_OBJECT (amd->window), window_weak_notify, obj);
        g_object_weak_ref (obj, object_weak_notify, amd->window);
    }
}

static ActionMenuData*
get_data (GObject *obj)
{
    return g_object_get_data (obj, "netk-action-data");
}

static void
item_activated_callback (GtkWidget *menu_item,
                                                 gpointer     data)
{
    ActionMenuData *amd = get_data (G_OBJECT (menu_item));
    WindowAction action = GPOINTER_TO_INT (data);
    
    if (amd == NULL)
        return;

    switch (action)
    {
        case CLOSE:
            netk_window_close (amd->window);
            break;
        case MINIMIZE:
            if (netk_window_is_minimized (amd->window))
                netk_window_unminimize (amd->window);
            else
                netk_window_minimize (amd->window);
            break;
        case MAXIMIZE:
            if (netk_window_is_maximized (amd->window))
                netk_window_unmaximize (amd->window);
            else
                netk_window_maximize (amd->window);
            break;
        case SHADE:
            if (netk_window_is_shaded (amd->window))
                netk_window_unshade (amd->window);
            else
                netk_window_shade (amd->window);
            break;
        case PIN:
            if (netk_window_is_pinned (amd->window))
                netk_window_unpin (amd->window);
            else
                netk_window_pin (amd->window);
            break;
        default:
            break;
    }
}

static void
set_item_text (GtkWidget  *mi,
               const char *text)
{
    gtk_label_set_text (GTK_LABEL (GTK_BIN (mi)->child),
                        text);
    gtk_label_set_use_underline (GTK_LABEL (GTK_BIN (mi)->child), TRUE);
}

static void
set_item_stock (GtkWidget  *mi,
                const char *stock_id)
{
    GtkWidget *image;
    
    image = gtk_image_menu_item_get_image (GTK_IMAGE_MENU_ITEM (mi));

    if (stock_id == NULL)
    {
        if (image != NULL)
            gtk_widget_destroy (image);
        return;
    }

    if (image == NULL)
        {
            image = gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_MENU);
            gtk_widget_show (image);
            gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (mi), image);
        }
    else
        {
            gtk_image_set_from_stock (GTK_IMAGE (image), stock_id,
                                                                GTK_ICON_SIZE_MENU);
        }
}

static void
sendto_item_activated (GtkMenuItem *mi,
                       gpointer user_data)
{
    gint ws_number = GPOINTER_TO_INT (user_data);
    ActionMenuData *amd = get_data (G_OBJECT (mi));
    NetkScreen *netk_screen = netk_window_get_screen (amd->window);
    NetkWorkspace *new_ws = netk_screen_get_workspace (netk_screen, ws_number);

    netk_window_move_to_workspace (amd->window, new_ws);
}

static GtkWidget *
create_sendto_menu (ActionMenuData *amd)
{
    GtkWidget *submenu, *ws_item;
    NetkScreen *netk_screen;
    NetkWorkspace *ws, *win_ws;
    gint nworkspaces, win_ws_n, i;
    const gchar *ws_name;
    gchar mi_label[2048];

    submenu = gtk_menu_new ();
    gtk_widget_show (submenu);

    netk_screen = netk_window_get_screen (amd->window);
    nworkspaces = netk_screen_get_workspace_count (netk_screen);

    win_ws = netk_window_get_workspace (amd->window);
    win_ws_n = netk_workspace_get_number (win_ws);

    for (i = 0; i < nworkspaces; i++)
    {
        ws = netk_screen_get_workspace (netk_screen, i);
        ws_name = netk_workspace_get_name (ws);

        if (ws_name)
            g_snprintf(mi_label, 2048, "%s %d (%s)", _("Workspace"), i+1, ws_name);
        else
            g_snprintf(mi_label, 2048, "%s %d", _("Workspace"), i+1);

        ws_item = gtk_check_menu_item_new_with_label (mi_label);
        if (i == win_ws_n)
            gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (ws_item), TRUE);

        gtk_widget_show (ws_item);
        gtk_menu_shell_append(GTK_MENU_SHELL (submenu), ws_item);

        set_data (G_OBJECT (ws_item), amd);
        g_signal_connect (G_OBJECT (ws_item), "activate",
                          G_CALLBACK (sendto_item_activated),
                          GINT_TO_POINTER (i));
    }
    
    return submenu;
}

static gboolean
update_menu_state (ActionMenuData *amd)
{
    NetkWindowActions actions;    

    amd->idle_handler = 0;
    
    actions = netk_window_get_actions (amd->window);
    
    if (netk_window_is_minimized (amd->window))
    {
        set_item_text (amd->minimize_item, _("S_how"));
        set_item_stock (amd->minimize_item, NETK_STOCK_UNMINIMIZE);
        gtk_widget_set_sensitive (amd->minimize_item,
                                  (actions & NETK_WINDOW_ACTION_UNMINIMIZE) != 0);
    }
    else
    {
        set_item_text (amd->minimize_item, _("_Hide"));
        set_item_stock (amd->minimize_item, NETK_STOCK_MINIMIZE);
        gtk_widget_set_sensitive (amd->minimize_item,
                                  (actions & NETK_WINDOW_ACTION_MINIMIZE) != 0);
    }

    if (netk_window_is_maximized (amd->window))
    {
        set_item_text (amd->maximize_item, _("Un_maximize"));
        set_item_stock (amd->maximize_item, NETK_STOCK_UNMAXIMIZE);
        gtk_widget_set_sensitive (amd->maximize_item,
                                  (actions & NETK_WINDOW_ACTION_UNMAXIMIZE) != 0);
    }
    else
    {
        set_item_text (amd->maximize_item, _("Ma_ximize"));
        set_item_stock (amd->maximize_item, NETK_STOCK_MAXIMIZE);
        gtk_widget_set_sensitive (amd->maximize_item,
                                  (actions & NETK_WINDOW_ACTION_MAXIMIZE) != 0);
    }

    if (netk_window_is_shaded (amd->window))
    {
        set_item_text (amd->shade_item, _("Un_shade"));
        set_item_stock (amd->shade_item, NETK_STOCK_UNSHADE);
        gtk_widget_set_sensitive (amd->shade_item,
                                  (actions & NETK_WINDOW_ACTION_UNSHADE) != 0);
    }
    else
    {
        set_item_text (amd->shade_item, _("_Shade"));
        set_item_stock (amd->shade_item, NETK_STOCK_SHADE);
        gtk_widget_set_sensitive (amd->shade_item,
                                  (actions & NETK_WINDOW_ACTION_SHADE) != 0);
    }

    if (netk_window_is_pinned (amd->window))
    {
        set_item_text (amd->pin_item, _("Uns_tick"));
        set_item_stock (amd->pin_item, NETK_STOCK_UNSTICK);
        gtk_widget_set_sensitive (amd->pin_item,
                                  (actions & NETK_WINDOW_ACTION_CHANGE_WORKSPACE) != 0);
        gtk_widget_set_sensitive (amd->sendto_item, FALSE);
    }
    else
    {
        set_item_text (amd->pin_item, _("S_tick"));
        set_item_stock (amd->pin_item, NETK_STOCK_STICK);
        gtk_widget_set_sensitive (amd->pin_item,
                                  (actions & NETK_WINDOW_ACTION_CHANGE_WORKSPACE) != 0);

        if (actions & NETK_WINDOW_ACTION_CHANGE_WORKSPACE)
        {
            GtkWidget *sendto_menu = create_sendto_menu (amd);
            gtk_menu_item_set_submenu (GTK_MENU_ITEM (amd->sendto_item),
                                       sendto_menu);
            gtk_widget_set_sensitive (amd->sendto_item, TRUE);
        }
        else
        {
            gtk_widget_set_sensitive (amd->sendto_item, FALSE);
        }
    }
    
    gtk_widget_set_sensitive (amd->close_item,
                              (actions & NETK_WINDOW_ACTION_CLOSE) != 0);

    return FALSE;
}

static void
queue_update (ActionMenuData *amd)
{
    if (amd->idle_handler == 0)
        amd->idle_handler = g_idle_add ((GSourceFunc)update_menu_state, amd);
}

static void
state_changed_callback (NetkWindow      *window,
                        NetkWindowState changed_mask,
                        NetkWindowState new_state,
                        gpointer        data)
{
    ActionMenuData *amd;

    amd = get_data (data);

    if (amd)
        queue_update (amd);
}

static void
actions_changed_callback (NetkWindow        *window,
                          NetkWindowActions changed_mask,
                          NetkWindowActions new_actions,
                          gpointer          data)
{
    ActionMenuData *amd;

    amd = get_data (data);

    if (amd)
        queue_update (amd);
}

static GtkWidget*
make_menu_item (ActionMenuData *amd,
                WindowAction    action)
{
    GtkWidget *mi;
    
    mi = gtk_image_menu_item_new_with_label ("");
    
    set_data (G_OBJECT (mi), amd);
    
    g_signal_connect (G_OBJECT (mi), "activate",
                      G_CALLBACK (item_activated_callback),
                      GINT_TO_POINTER (action));
    
    gtk_widget_show (mi);

    return mi;
}

static void
amd_free (ActionMenuData *amd)
{
    if (amd->idle_handler)
        g_source_remove (amd->idle_handler);

    _netk_slice_free (ActionMenuData, amd);
}

/**
 * netk_create_window_action_menu:
 * @window: a #NetkWindow
 * 
 * Creates a menu of window operations for @window.
 * 
 * Return value: a new menu of window operations
 **/
GtkWidget*
netk_create_window_action_menu (NetkWindow *window)
{
    GtkWidget *menu;
    ActionMenuData *amd;
    GtkWidget *separator;

    /* be sure to initialize libraries i18n support first */
    _xfce_i18n_init ();

    amd = _netk_slice_new0 (ActionMenuData);
    amd->window = window;
    
    menu = gtk_menu_new ();
    g_object_ref (G_OBJECT (menu));
    gtk_object_sink (GTK_OBJECT (menu));

    amd->menu = menu;
    
    g_object_set_data_full (G_OBJECT (menu), "netk-action-data",
                            amd, (GDestroyNotify) amd_free);

    g_object_weak_ref (G_OBJECT (window), window_weak_notify, menu);
    g_object_weak_ref (G_OBJECT (menu), object_weak_notify, window);

    amd->maximize_item = make_menu_item (amd, MAXIMIZE);
    gtk_menu_shell_append (GTK_MENU_SHELL (menu),
                           amd->maximize_item);

    amd->minimize_item = make_menu_item (amd, MINIMIZE);
    gtk_menu_shell_append (GTK_MENU_SHELL (menu),
                           amd->minimize_item);

    amd->shade_item = make_menu_item (amd, SHADE);
    gtk_menu_shell_append (GTK_MENU_SHELL (menu),
                           amd->shade_item);

    amd->pin_item = make_menu_item (amd, PIN);
    gtk_menu_shell_append (GTK_MENU_SHELL (menu),
                           amd->pin_item);
    set_item_stock (amd->pin_item, NULL);

    amd->sendto_item = make_menu_item (amd, SENDTO);
    gtk_menu_shell_append (GTK_MENU_SHELL (menu),
                           amd->sendto_item);
    set_item_text (amd->sendto_item, _("Send to..."));
    set_item_stock (amd->sendto_item, GTK_STOCK_JUMP_TO);

    if (netk_window_is_sticky (amd->window))
    {
        GtkWidget *dummy = gtk_menu_new ();
        gtk_widget_show (dummy);
        gtk_menu_item_set_submenu (GTK_MENU_ITEM (amd->sendto_item), dummy);
        gtk_widget_set_sensitive (amd->sendto_item, FALSE);
    }
    else
    {
        GtkWidget *sendto_menu = create_sendto_menu (amd);
        gtk_menu_item_set_submenu (GTK_MENU_ITEM (amd->sendto_item), sendto_menu);
    }
    
    separator = gtk_separator_menu_item_new ();
    gtk_widget_show (separator);
    gtk_menu_shell_append (GTK_MENU_SHELL (menu),
                                                 separator);

    amd->close_item = make_menu_item (amd, CLOSE);
    gtk_menu_shell_append (GTK_MENU_SHELL (menu),
                           amd->close_item);
    set_item_text (amd->close_item, _("_Close"));
    set_item_stock (amd->close_item, NETK_STOCK_DELETE);

    g_signal_connect_object (G_OBJECT (amd->window), 
                             "state_changed",
                             G_CALLBACK (state_changed_callback),
                             G_OBJECT (menu),
                             0);

    g_signal_connect_object (G_OBJECT (amd->window),
                             "actions_changed",
                             G_CALLBACK (actions_changed_callback),
                             G_OBJECT (menu),
                             0);

    update_menu_state (amd);
    
    return menu;
}
