/*
 * Copyright (C) 2003-2017 the xine project
 *
 * 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 St, Fifth Floor, Boston, MA  02110-1301 USA
 *
 * main/context menu creation / handling
 */

#include "globals.h"

#include <string.h>
#include <stdlib.h>

#include "menu.h"
#include "playlist.h"
#include "engine.h"
#include "ui.h"
#include "utils.h"
#include "gtkvideo.h"
#include "vis.h"
#include "noskin_window.h"

/*
 * global variables
 */

GtkWidget *popup_menu = NULL, *menubar = NULL;

GtkUIManager *ui = NULL;
action_items_t action_items = { NULL };

/*
 * local bits
 */

#define get_action(name) gtk_action_group_get_action (action, (name))
#define get_toggle(name) GTK_TOGGLE_ACTION(get_action ((name)))
#define get_radio(name) GTK_RADIO_ACTION(get_action ((name)))
#define get_radio_group(name) gtk_radio_action_get_group (get_radio ((name)))

static const char type[][8] = { "menubar", "popup" };

const char *menu_get_tree_name (unsigned int tree)
{
  return tree < G_N_ELEMENTS(type) ? type[tree] : NULL;
}

static void autoplay_cb (GtkAction *action, const char *data)
{
  int n, i, pos;

  const char * const *mrls = xine_get_autoplay_mrls (xine, data, &n);
  if (!mrls)
  {
    playlist_logo (NULL);
    char *title = g_markup_printf_escaped (_("Autoplay input plugin ‘%s’ failed"),
					   data);
    display_error (FROM_XINE, title, _("Check engine output for further details."));
    free (title);
    return;
  }

  playlist_flush (PLAY_ITEM_AUTOPLAY);
  pos = -1;
  for (i = 0; mrls[i]; ++i)
  {
    /* don't duplicate the first item if it's already listed */
    if (pos < 0)
    {
      int j;
      for (j = playlist_size () - 1; j >= 0; --j)
	if (!strcmp (playlist_get_item (j)->mrl, mrls[i]))
	  break;
      if (j >= 0)
      {
	pos = j;
	continue;
      }
    }

    play_item_t *item = play_item_new (NULL, mrls[i], 0, 0);
    item->type = PLAY_ITEM_AUTOPLAY;
    if (pos < 0)
      pos = playlist_add (item, -1);
    else
      playlist_add (item, -1);
  }

  playlist_play (pos);
}

#define AUTOPLAY_NAME "_autoplay_"
#define VIS_NAME "_vis_"

static size_t add_autoplay_actions (GtkUIManager *ui)
{
  const char *const *autoplay_ids = xine_get_autoplay_input_plugin_ids (xine);
  GtkActionGroup *actions;
  char name[32];
  size_t i;

  static const struct {
    char id[8];
    const char *stock;
  } stock[] = {
    { "VCD", GTK_STOCK_CDROM },
    { "VCDO", GTK_STOCK_CDROM },
    { "DVD", GTK_STOCK_CDROM },
    { "CD", GTK_STOCK_CDROM },
  };

  if (!autoplay_ids)
  {
    logprintf ("menu: warning: didn't get any autoplay entries\n");
    return 0;
  }

  actions = gtk_action_group_new ("autoplay");
#ifdef ENABLE_NLS
  gtk_action_group_set_translate_func (actions, (GtkTranslateFunc)strstr, "", NULL);
#endif

  for (i = 0; autoplay_ids[i]; ++i)
  {
    int j = G_N_ELEMENTS (stock);
    while (j--)
      if (!strcmp (autoplay_ids[i], stock[j].id))
	break;
    snprintf (name, sizeof (name), AUTOPLAY_NAME"%zu", i);
    GtkAction *action = gtk_action_new (name, autoplay_ids[i], NULL,
					j < 0 ? NULL : stock[j].stock);
    g_signal_connect (G_OBJECT(action), "activate",
		      G_CALLBACK(autoplay_cb),
		      (gpointer) strdup (autoplay_ids[i]));
    gtk_action_group_add_action (actions, action);
  }

  gtk_ui_manager_insert_action_group (ui, actions, 0);

  return i;
}

static size_t add_vis_actions (GtkUIManager *ui, GtkAction *group_action)
{
  const char *const *pol =
    xine_list_post_plugins_typed (xine, XINE_POST_TYPE_AUDIO_VISUALIZATION);
  GtkActionGroup *actions;
  GSList *radiogroup;
  char name[32];
  xine_cfg_entry_t entry;
  const char *vis;
  size_t i;
  GtkRadioAction *tick;

  if (!pol)
    return 0;

  actions = gtk_action_group_new ("vis");
#ifdef ENABLE_NLS
  gtk_action_group_set_translate_func (actions, (GtkTranslateFunc)strstr, "", NULL);
#endif
  vis = xine_config_lookup_entry
	  (xine, "gui.post_plugins.audio_visualisation", &entry)
	? entry.enum_values[entry.num_value] : NULL;
  tick = GTK_RADIO_ACTION(group_action);
  radiogroup = gtk_radio_action_get_group (tick);

  for (i = 0; pol[i]; ++i)
  {
    snprintf (name, sizeof (name), VIS_NAME"%zu", i);
    GtkRadioAction *action = gtk_radio_action_new
			       (name, pol[i], NULL, NULL, i + 1);
    gtk_radio_action_set_group (action, radiogroup);
    radiogroup = gtk_radio_action_get_group (action);
    gtk_action_group_add_action (actions, GTK_ACTION(action));
    if (vis && !strcasecmp (vis, pol[i]))
      tick = action;
  }
  gtk_action_activate (GTK_ACTION(tick));

  gtk_ui_manager_insert_action_group (ui, actions, 0);

  return i;
}

void menu_add_items (GtkUIManager *ui, const char *const prefix,
		     const char *const label, guint merge, size_t count)
{
  char name[32];
  int i;
  for (i = 0; i < 2; ++i)
  {
    gchar *path = g_strdup_printf ("/ui/%s/%s", type[i], prefix);
    size_t j = count;
    while (j--)
    {
      snprintf (name, sizeof (name), "%s%zu", label, j);
      gtk_ui_manager_add_ui (ui, merge, path, name, name,
			     GTK_UI_MANAGER_MENUITEM, TRUE);
    }
    g_free (path);
  }
}

/* Standard callback (invokes Javascript) */
#define JS_CB(func, command) \
  static void js_##func##_cb (GtkAction *action, gpointer data) \
  { \
    gchar *fn = g_strdup_printf (_("menu function %s"), #func); \
    engine_exec ((command), NULL, NULL, fn); \
    g_free (fn); \
  }

/* Radio action callback (no Javascript)
 * Requires a function body, to which is passed int v = radio action value
 */
#define JS_CB_R(func) \
  static ALWAYS_INLINE void js_##func##_cb_int (int); \
  static void js_##func##_cb (GtkRadioAction *action, gpointer data) \
  { \
    /* \
    gint value, current = gtk_radio_action_get_current_value (action); \
    g_object_get ((GObject *)action, "value", &value, NULL); \
    if (value != current) \
      js_##func##_cb_int (current); \
    */ \
    js_##func##_cb_int (gtk_radio_action_get_current_value (action)); \
  } \
  static ALWAYS_INLINE void js_##func##_cb_int (int v)
  /* function body goes here */

/* Toggle action callback (no Javascript)
 * Requires a function body, to which is passed gboolean v = toggle state
 */
#define JS_CB_T(func) \
  static ALWAYS_INLINE void js_##func##_cb_int (gboolean); \
  static void js_##func##_cb (GtkToggleAction *action, gpointer data) \
  { js_##func##_cb_int (gtk_toggle_action_get_active (action)); } \
  static ALWAYS_INLINE void js_##func##_cb_int (gboolean v)
  /* function body goes here */

/* File */
JS_CB (Open, "open_show ();")
JS_CB (MRL, "open_mrl_show ();")
JS_CB (Playlist, "playlist_show ();")
JS_CB (Prefs, "preferences_show ();")
JS_CB (Keys, "keybindings_show ();")
JS_CB (Startup, "startup_cmds_show ();")
JS_CB (Snapshot, "snapshot ();")
JS_CB (HideMain, "set_minimised (1);")
JS_CB (ShowMain, "set_minimised (0);")
JS_CB (Quit, "exit ();")

/* View */
JS_CB_T (Fullscreen)
{
  if (!gtk_widget_get_mapped (app))
    app_show ();
  gtk_video_set_fullscreen ((GtkVideo *)gtv, v, &engine_lock);
  gtk_action_set_sensitive (action_items.hidemain, !v);
  gtk_action_set_sensitive (action_items.showmain, !v);
  if (v)
    window_fs_toolbar_restore ();
  else
    window_fs_toolbar_reset ();
}
JS_CB_T (FSToolbarVisible) { window_fs_toolbar_show (v); }
JS_CB_R (FSToolbarPos) { window_fs_toolbar_position (v); }
JS_CB_T (WMToolbarVisible) { window_wm_toolbar_show (v); }
JS_CB_T (WMToolbarSnap) { window_wm_toolbar_set_snap (v); }
JS_CB (Keypad, "keypad_show ();")
JS_CB_R (Window) { gtk_video_rescale ((GtkVideo *)gtv, (double) v / 1e+6); }
JS_CB (WindowRevert, "set_video_size (-1);")
JS_CB_T (WindowAutosize) { gtk_video_set_auto_resize ((GtkVideo *)gtv, v); }
JS_CB_T (WindowMagLow) { gtk_video_set_auto_rescale ((GtkVideo *)gtv, v); }
JS_CB (ZoomIn, "vo_zoom.v += 5;")
JS_CB (ZoomOut, "vo_zoom.v -= 5;")
JS_CB (Zoom100, "vo_zoom.v = 100;")
JS_CB_R (Aspect) { xine_set_param (stream, XINE_PARAM_VO_ASPECT_RATIO, v); }
JS_CB_R (Subtitles)
{
  if (v > -3)
    xine_set_param (stream, XINE_PARAM_SPU_CHANNEL, v);
}

/* Video */
JS_CB (VideoSettings, "settings_show (0);")
JS_CB_T (Deinterlace)
{
  gtk_video_set_use_post_plugins_deinterlace ((GtkVideo *)gtv, v);
}
JS_CB_T (VideoPP) { gtk_video_set_use_post_plugins_video ((GtkVideo *)gtv, v); }
JS_CB (DeinterlaceConf, "deinterlace_show ();")
JS_CB (VideoPPConf, "postproc_video_show ();")
JS_CB_T (WMUnblank)
{
  gtk_video_set_windowed_unblank ((GtkVideo *)gtv, v);
}
JS_CB_T (FSSticky) { window_fs_stickiness_update (v); }

/* Audio */
JS_CB (AudioSettings, "settings_show (1);")
JS_CB (AudioEq, "settings_show (2);")
JS_CB_T (AudioPP)
{
  gtk_video_set_use_post_plugins_audio ((GtkVideo *)gtv, v, audio_port);
}
JS_CB (AudioPPConf, "postproc_audio_show ();")

/* Media */
JS_CB (MediaAdd, "mm_add_show ();")
JS_CB (MediaManage, "mm_manage_show ();")
JS_CB (MediaImport, "import_mediamarks ();")

/* Help */
JS_CB (About, "about_show ();")

/* Configure */
JS_CB (EngineLog, "log_show ();")
JS_CB (JSConsole, "js_console_show ();")
JS_CB (StreamInfo, "stream_info_show ();")
JS_CB (SetupWizards, "run_wizards ();")
#ifdef HAVE_LIRC
JS_CB (ReloadLIRC, "reload_lirc ();")
#endif

/* Systray */
JS_CB (PrevTrack, "playlist_play (playlist_get_item () - 1);")
JS_CB (NextTrack, "playlist_play (playlist_get_item () + 1);")
JS_CB (Play, "play ();")
JS_CB (Pause, "if (!is_live_stream ()) pause ();")
JS_CB (FFwd, "if (!is_live_stream ()) av_speed.v = 5;")
JS_CB (Stop, "stop ();")

#define CB(aname, astock, alabel, akey) \
  { .name = #aname, .stock_id = (astock), .label = (alabel), .accelerator = (akey), .callback = G_CALLBACK(js_##aname##_cb), }
#define CM(aname, astock, alabel) \
  { .name =  #aname, .stock_id = (astock), .label = (alabel) }
static const GtkActionEntry main_actions[] = {
  /* Menu entry text */
  CM(FileMenu,          NULL,                   N_("_File")),
  CB(Open,		GTK_STOCK_OPEN,		N_("_Open..."),			"<control>O"),
  CB(MRL,		GTK_STOCK_OPEN,		N_("Open _MRL..."),		"<control>M"),
  CB(Playlist,		GTK_STOCK_INDEX,	N_("Play_list..."),		NULL),
  CM(ConfigMenu,        GTK_STOCK_PREFERENCES,  N_("_Configure")),
  CB(Prefs,		NULL,			N_("_Preferences..."),		NULL),
  CB(Keys,		NULL,			N_("_Keybindings..."),		NULL),
  CB(Startup,		NULL,			N_("_Startup script..."),	NULL),
#ifdef HAVE_LIRC
  CB(ReloadLIRC,	GTK_STOCK_REFRESH,	N_("_Reload LIRC"),		NULL),
#endif
  CB(Snapshot,		NULL,			N_("_Snapshot..."),		"<control>S"),
  CB(Quit,		GTK_STOCK_QUIT,		N_("_Quit"),			"<control>Q"),
  CM(ViewMenu,          NULL,                   N_("_View")),
  CB(HideMain,		GTK_STOCK_CLOSE,	N_("_Hide video"),		"<control>W"),
  CB(ShowMain,		NULL,			N_("_Show video"),		NULL),
  CM(ToolbarMenu,       NULL,                   N_("_Toolbars")),
  CM(WindowMenu,        NULL,                   N_("_Window size")),
  CB(WindowRevert,	NULL,			N_("Re_set"),			NULL),
  CB(Keypad,		NULL,			N_("_Keypad..."),		NULL),
  CB(ZoomIn,		GTK_STOCK_ZOOM_IN,	N_("Zoom _in"),			NULL),
  CB(ZoomOut,		GTK_STOCK_ZOOM_OUT,	N_("Zoom _out"),		NULL),
  CB(Zoom100,		GTK_STOCK_ZOOM_100,	N_("_Zoom 100%"),		NULL),
  CM(AspectMenu,        NULL,                   N_("_Aspect ratio")),
  CM(VisMenu,           NULL,                   N_("_Visualisations")),
  CM(SubsMenu,          "gxine-settings-spu",   N_("_Subtitles")),
  CM(ViewMisc,          GTK_STOCK_PROPERTIES,   N_("_Settings")),
  CM(VideoMenu,         NULL,                   N_("V_ideo")),
  CB(VideoSettings,	GTK_STOCK_PROPERTIES,	/* Video */ N_("_Controls..."),	NULL),
  CM(VideoConfigMenu,   GTK_STOCK_PREFERENCES,  N_("_Configure plugins")),
  CB(DeinterlaceConf,	NULL,			N_("_Deinterlace..."),		NULL),
  CB(VideoPPConf,	NULL,			N_("_Video post-processing..."),NULL),
  CM(AudioMenu,         NULL,                   N_("_Audio")),
  CB(AudioSettings,	GTK_STOCK_PROPERTIES,	/* Audio */ N_("_Controls..."),	NULL),
  CB(AudioEq,		GTK_STOCK_PROPERTIES,	N_("_Equaliser..."),		NULL),
  CM(AudioConfigMenu,   GTK_STOCK_PREFERENCES,  N_("_Configure plugins")),
  CB(AudioPPConf,	NULL,			N_("_Audio post-processing..."),NULL),
  CM(MediaMenu,         NULL,                   N_("_Media")),
  CB(MediaAdd,		GTK_STOCK_NEW,		N_("_Add media mark..."),	"<control>D"),
  CB(MediaManage,	GXINE_MEDIA_MARK,	N_("_Manage media marks..."),	"<control>B"),
  CB(MediaImport,	GTK_STOCK_OPEN,		N_("_Import media marks..."),	NULL),
  CM(ToolsMenu,         NULL,                   N_("_Tools")),
  CB(EngineLog,		NULL,			N_("Engine _log..."),		"<control>L"),
  CB(JSConsole,		GTK_STOCK_EXECUTE,	N_("_Javascript console..."),	"<control>J"),
  CB(StreamInfo,	GTK_STOCK_INFO,		N_("Stream _info..."),		NULL),
  CB(SetupWizards,	GTK_STOCK_REDO,		N_("_Re-run setup wizards..."), NULL),
  CM(HelpMenu,          NULL,                   N_("_Help")),
  CB(About,		GTK_STOCK_ABOUT,	N_("_About..."),		NULL),
  CB(PrevTrack,	GTK_STOCK_MEDIA_PREVIOUS,	N_("Pre_vious track"),		NULL),
  CB(Play,	GTK_STOCK_MEDIA_PLAY,		N_("_Play"),			NULL),
  CB(Pause,	GTK_STOCK_MEDIA_PAUSE,		N_("_Pause"),			NULL),
  CB(FFwd,	GTK_STOCK_MEDIA_FORWARD,	N_("_Fast forward"),		NULL),
  CB(Stop,	GTK_STOCK_MEDIA_STOP,		N_("_Stop"),			NULL),
  CB(NextTrack,	GTK_STOCK_MEDIA_NEXT,		N_("_Next track"),		NULL),
};
#undef CB
#undef CM

#define CB(aname, astock, alabel, akey, astate) \
  { .name = #aname, .stock_id = (astock), .label = (alabel), .accelerator = (akey), .callback = G_CALLBACK(js_##aname##_cb), .is_active = (astate), }
typedef enum {
  TOGGLE_FULLSCREEN,
  TOGGLE_WINDOWEDMODE_TOOLBAR,
  TOGGLE_WINDOWEDMODE_TOOLBAR_SNAP,
  TOGGLE_FULLSCREEN_TOOLBAR,
  TOGGLE_WINDOW_AUTOSIZE,
  TOGGLE_WINDOW_MAGNIFY,
  TOGGLE_DEINTERLACE,
  TOGGLE_VIDEO_PP,
  TOGGLE_AUDIO_PP,
  TOGGLE_UNBLANK,
} toggle_actions_e;
static GtkToggleActionEntry toggle_actions[] = {
  CB(Fullscreen,	NULL, N_("_Full-screen mode"),		"<control>F",	FALSE),
  CB(WMToolbarVisible,	NULL, N_("Visible (_windowed)"),	"Pointer_Button2",	TRUE),
  CB(WMToolbarSnap,	NULL, N_("_Snap to video window"),	NULL,	TRUE),
  CB(FSToolbarVisible,	NULL, N_("Visible (_full-screen)"),	"Pointer_Button2",	FALSE),
  CB(WindowAutosize,	NULL, N_("Auto _resize"),		NULL,		TRUE),
  CB(WindowMagLow,	NULL, N_("_Magnify low-res video"),	NULL,		FALSE),
  CB(Deinterlace,	NULL, N_("_Deinterlace"),		"<control>I",	FALSE),
  CB(VideoPP,		NULL, N_("_Post-processing"),		NULL,		FALSE),
  CB(AudioPP,		NULL, N_("_Post-processing"),		NULL,		FALSE),
  CB(WMUnblank,		NULL, N_("Prevent _blanking in windowed mode"), NULL,	FALSE),
  CB(FSSticky,		NULL, N_("Force _stickiness in full-screen mode"), NULL,	FALSE),
};
static const char *const toggle_prefs[] = {
  NULL, NULL, NULL, NULL, "gui.windowed_mode.auto_resize",
  "gui.windowed_mode.magnify_lowres_video", "gui.post_plugins.deinterlace_enable",
  "gui.post_plugins.video_enable", "gui.post_plugins.audio_enable",
  "gui.windowed_mode.unblank", "gui.fullscreen_mode.always_sticky",
};
#undef CB

static const GtkRadioActionEntry
fstoolbar_actions[] = {
  { "FSToolbarTop",	NULL, N_("At _top"),	NULL, NULL, 1 },
  { "FSToolbarBottom",	NULL, N_("At _bottom"),	NULL, NULL, 0 },
},
window_actions[] = {
  { "WindowOther",	NULL, "",		NULL, NULL, 0 },
  { "Window50",		NULL, N_("_50%"),	NULL, NULL, 50e+6 },
  { "Window75",		NULL, N_("_75%"),	NULL, NULL, 75e+6 },
  { "Window100",	NULL, N_("_100%"),	NULL, NULL, 100e+6 },
  { "Window150",	NULL, N_("15_0%"),	NULL, NULL, 150e+6 },
  { "Window200",	NULL, N_("_200%"),	NULL, NULL, 200e+6 },
  { "WindowDynamic",	NULL, "",		NULL, NULL, -1 },
},
aspect_actions[] = {
  /* Aspect ratio menu text */
  { "AspectAuto",	NULL, N_("_Auto"),	NULL, NULL, XINE_VO_ASPECT_AUTO },
  { "AspectSquare",	NULL, N_("_Square"),	NULL, NULL, XINE_VO_ASPECT_SQUARE },
  { "Aspect4_3",	NULL, N_("_4:3"),	NULL, NULL, XINE_VO_ASPECT_4_3 },
  { "Aspect16_9",	NULL, N_("_16:9"),	NULL, NULL, XINE_VO_ASPECT_ANAMORPHIC },
  { "Aspect2_1",	NULL, N_("_2:1"),	NULL, NULL, XINE_VO_ASPECT_DVB },
},
vis_actions[] = {
  { "VisNone",		NULL, N_("_None"),	NULL, NULL, 0 },
},
subtitle_actions[] = {
  /* menu item order is relied upon in player.c:set_subtitles() */
  { "Subtitles0",	NULL, N_("Channel _0"),	NULL, NULL, 0 },
  { "Subtitles1",	NULL, N_("Channel _1"),	NULL, NULL, 1 },
  { "Subtitles2",	NULL, N_("Channel _2"),	NULL, NULL, 2 },
  { "Subtitles3",	NULL, N_("Channel _3"),	NULL, NULL, 3 },
  { "Subtitles4",	NULL, N_("Channel _4"),	NULL, NULL, 4 },
  { "Subtitles5",	NULL, N_("Channel _5"),	NULL, NULL, 5 },
  { "Subtitles6",	NULL, N_("Channel _6"),	NULL, NULL, 6 },
  { "Subtitles7",	NULL, N_("Channel _7"),	NULL, NULL, 7 },
  { "Subtitles8",	NULL, N_("Channel _8"),	NULL, NULL, 8 },
  { "Subtitles9",	NULL, N_("Channel _9"),	NULL, NULL, 9 },
  { "Subtitles10",	NULL, N_("Channel 10"),	NULL, NULL, 10 },
  { "Subtitles11",	NULL, N_("Channel 11"),	NULL, NULL, 11 },
  { "Subtitles12",	NULL, N_("Channel 12"),	NULL, NULL, 12 },
  { "Subtitles13",	NULL, N_("Channel 13"),	NULL, NULL, 13 },
  { "Subtitles14",	NULL, N_("Channel 14"),	NULL, NULL, 14 },
  { "Subtitles15",	NULL, N_("Channel 15"),	NULL, NULL, 15 },
  /* Subtitles menu text */
  { "SubtitlesNone",	NULL, N_("_None"),	NULL, NULL, -2 },
  { "SubtitlesAuto",	NULL, N_("_Auto"),	NULL, NULL, -1 },
  /* Other subtitle channels */
  { "SubtitlesOther",	NULL, "",		NULL, NULL, -3 },
};

static GtkWidget *add_menu_ui (GtkUIManager *ui, int i)
{
  static const char def[] =
	"<menu action='FileMenu'>\n"
	  "<menuitem action='Open' />\n"
	  "<menuitem action='MRL' />\n"
	  "<menuitem action='Playlist' />\n"
	  "<separator />\n"
	  "<menu action='ConfigMenu'>\n"
	   "<menuitem action='Prefs' />\n"
	   "<menuitem action='Keys' />\n"
	   "<menuitem action='Startup' />\n"
	   "<separator />\n"
	   /* the next 3 items duplicate items in the video & audio menus */
	   "<menuitem action='DeinterlaceConf' />\n"
	   "<menuitem action='VideoPPConf' />\n"
	   "<menuitem action='AudioPPConf' />\n"
#ifdef HAVE_LIRC
	   "<separator />\n"
	   "<menuitem action='ReloadLIRC' />\n"
#endif
	   "<separator />\n"
	   "<menuitem action='SetupWizards' />\n"
	  "</menu>\n"
	  "<separator />\n"
	  "<placeholder name='AutoPlay' />\n"
	  "<separator />\n"
	  "<menuitem action='Snapshot' />\n"
	  "<menuitem action='Quit' />\n"
	"</menu>\n"
	"<menu action='ViewMenu'>\n"
	  "<menuitem action='Fullscreen' />\n"
	  "<menuitem action='HideMain' />\n"
	  "<menuitem action='ShowMain' />\n"
	  "<menu action='ToolbarMenu'>\n"
	    "<menuitem action='WMToolbarVisible' />\n"
	    "<menuitem action='WMToolbarSnap' />\n"
	    "<separator />\n"
	    "<menuitem action='FSToolbarVisible' />\n"
	    "<menuitem action='FSToolbarTop' />\n"
	    "<menuitem action='FSToolbarBottom' />\n"
	  "</menu>\n"
	  "<menuitem action='Keypad' />\n"
	  "<separator />\n"
	  "<menu action='WindowMenu'>\n"
	    "<menuitem action='Window50' />\n"
	    "<menuitem action='Window75' />\n"
	    "<menuitem action='Window100' />\n"
	    "<menuitem action='Window150' />\n"
	    "<menuitem action='Window200' />\n"
	    "<menuitem action='WindowDynamic' />\n"
	    "<menuitem action='WindowRevert' />\n"
	    "<separator />\n"
	    "<menuitem action='WindowAutosize' />\n"
	    "<menuitem action='WindowMagLow' />\n"
	  "</menu>\n"
	  "<menuitem action='ZoomIn' />\n"
	  "<menuitem action='ZoomOut' />\n"
	  "<menuitem action='Zoom100' />\n"
	  "<menu action='AspectMenu'>\n"
	    "<menuitem action='AspectAuto' />\n"
	    "<menuitem action='AspectSquare' />\n"
	    "<menuitem action='Aspect4_3' />\n"
	    "<menuitem action='Aspect16_9' />\n"
	    "<menuitem action='Aspect2_1' />\n"
	  "</menu>\n"
	  "<separator />\n"
	  "<menu action='VisMenu'>\n"
	    "<menuitem action='VisNone' />\n"
	    "<placeholder name='VisItems' />\n"
	  "</menu>\n"
	  "<menu action='SubsMenu'>\n"
	    "<menuitem action='SubtitlesAuto' />\n"
	    "<menuitem action='SubtitlesNone' />\n"
	    "<menuitem action='Subtitles0' />\n"
	    "<menuitem action='Subtitles1' />\n"
	    "<menuitem action='Subtitles2' />\n"
	    "<menuitem action='Subtitles3' />\n"
	    "<menuitem action='Subtitles4' />\n"
	    "<menuitem action='Subtitles5' />\n"
	    "<menuitem action='Subtitles6' />\n"
	    "<menuitem action='Subtitles7' />\n"
	    "<menuitem action='Subtitles8' />\n"
	    "<menuitem action='Subtitles9' />\n"
	    "<menuitem action='Subtitles10' />\n"
	    "<menuitem action='Subtitles11' />\n"
	    "<menuitem action='Subtitles12' />\n"
	    "<menuitem action='Subtitles13' />\n"
	    "<menuitem action='Subtitles14' />\n"
	    "<menuitem action='Subtitles15' />\n"
	  "</menu>\n"
	  "<menu action='ViewMisc'>\n"
	    "<menuitem action='WMUnblank' />\n"
	    "<menuitem action='FSSticky' />\n"
	  "</menu>\n"
	  "<separator />\n"
	  "<menuitem action='EngineLog' />\n"
	  "<menuitem action='JSConsole' />\n"
	  "<menuitem action='StreamInfo' />\n"
	"</menu>\n"
	"<menu action='VideoMenu'>\n"
	  "<menuitem action='VideoSettings' />\n"
	  "<menuitem action='Deinterlace' />\n"
	  "<menuitem action='VideoPP' />\n"
	  "<menu action='VideoConfigMenu'>\n"
	    "<menuitem action='DeinterlaceConf' />\n"
	    "<menuitem action='VideoPPConf' />\n"
	  "</menu>\n"
	"</menu>\n"
	"<menu action='AudioMenu'>\n"
	  "<menuitem action='AudioSettings' />\n"
	  "<menuitem action='AudioEq' />\n"
	  "<menuitem action='AudioPP' />\n"
	  "<menu action='AudioConfigMenu'>\n"
	    "<menuitem action='AudioPPConf' />\n"
	  "</menu>\n"
	"</menu>\n"
	"<menu action='MediaMenu'>\n"
	  "<menuitem action='MediaAdd' />\n"
	  "<menuitem action='MediaManage' />\n"
	  "<menuitem action='MediaImport' />\n"
	  "<separator />\n"
	  "<placeholder name='MediaItems' />\n"
	"</menu>\n"
	/*"<separator />\n"*/
	"<menu action='HelpMenu'>\n"
	  "<menuitem action='About' />\n"
	"</menu>\n";
  GError *error = NULL;
  char *key = NULL;

  gchar *xml = g_strdup_printf ("<ui><%s name='%s'>%s</%s></ui>",
                                type[i], type[i], def, type[i]);
  gtk_ui_manager_add_ui_from_string (ui, xml, -1, &error);
  g_free (xml);

  asreprintf (&key, "/ui/%s", type[i]);
  {
    GtkWidget *toplevel = gtk_ui_manager_get_widget (ui, key);
    free (key);
    return toplevel;
  }
}

GtkAction *find_video_size_action (gdouble factor)
{
  GSList *action;
  gint value, zoom = factor * 1e+6;
  foreach_glist (action, action_items.video_size)
    if (action->data != action_items.resize_factor /* ignore "Other" */
	&& (g_object_get (action->data, "value", &value, NULL), value) == zoom)
      return action->data;
  foreach_glist (action, action_items.video_size)
    if (action->data == action_items.resize_factor /* require "Other" */
	&& (g_object_get (action->data, "value", &value, NULL), value) == zoom)
      return action->data;
  return NULL;
}

void scale_changed_cb (GtkWidget *widget, gdouble factor, gpointer data)
{
  if (factor != -1 && action_items.resize_factor
      && xine_get_status (stream) != XINE_STATUS_IDLE
      && gtk_widget_get_visible (gtv))
  {
    logprintf ("gxine: received scale change event (%lf%%)\n", factor);
    GtkAction *action = find_video_size_action (factor);
    if (action)
    {
      g_signal_handlers_block_by_func (action, js_Window_cb, NULL);
      gtk_toggle_action_set_active ((GtkToggleAction *)action, TRUE);
      do_pending_events ();
      g_signal_handlers_unblock_by_func (action, js_Window_cb, NULL);
      return;
    }

    gchar *label = g_strdup_printf (_("_Other (%.1lf%%)"), factor);
    g_object_set ((GObject *)action_items.resize_factor, "label", label, NULL);
    g_free (label);
    GSList *changed;
    foreach_glist (changed, gtk_radio_action_get_group
			      (GTK_RADIO_ACTION (action_items.resize_factor)))
      if (g_signal_handlers_block_by_func (changed->data, js_Window_cb, NULL))
	break;
    g_object_set ((GObject *)action_items.resize_factor,
		  "value", (gint)(factor * 1e+6), NULL);
    gtk_action_set_visible ((GtkAction *)action_items.resize_factor, TRUE);
    gtk_toggle_action_set_active ((GtkToggleAction *)action_items.resize_factor, TRUE);
    do_pending_events ();
    if (changed)
      g_signal_handlers_unblock_by_func (changed->data, js_Window_cb, NULL);
  }
  else {
    logprintf ("gxine: ignored scale change event (%lf%%)\n", factor);
  }
}

static void update_subtitles_menu_internal (GtkActionGroup *action)
{
  if (!action)
    return;
  int i;
  for (i = 0; i < 16; ++i)
  {
    static char subt[] = "Subtitles..";
    char lang[XINE_LANG_MAX];
    snprintf (subt + 9, 3, "%d", i);
    GtkAction *item = get_action (subt);
    if (!item)
      break;
    char *label = NULL, *base = gettext (subtitle_actions[i].label);
    if (xine_get_spu_lang (stream, i, lang))
    {
      label = base - 1;
      while (*++label == ' ')
	/* skip spaces */;
      label = g_strconcat (label, strcmp (lang, "none") ? " / " : NULL, lang, NULL);
    }
    g_object_set ((GObject *) item, "label", label ? label : base, NULL);
    g_free (label);
  }
}

static gboolean subs_menu_cb (GtkWidget *widget, GdkEventExpose *event,
			      gpointer action)
{
  update_subtitles_menu_internal (action);
  return FALSE;
}

void update_subtitles_menu (void)
{
  if (ui)
    update_subtitles_menu_internal (ui_get_action_group (ui));
}

void create_menus (GtkWidget *window)
{
  GtkActionGroup *action;
  static const int scale[] = { 50e+6, 75e+6, 100e+6, 150e+6, 200e+6 };

  ui = ui_create_manager ("gxine", window);
  action = ui_get_action_group (ui);

  xine_cfg_entry_t entry;
  unsigned int i;

  for (i = 0; i < G_N_ELEMENTS(toggle_prefs); ++i)
    if (toggle_prefs[i] &&
	xine_config_lookup_entry (xine, toggle_prefs[i], &entry))
      toggle_actions[i].is_active = entry.num_value;

  if (!xine_config_lookup_entry (xine, "gui.fullscreen_mode.toolbar", &entry))
    entry.num_value = 0;
  toggle_actions[TOGGLE_FULLSCREEN_TOOLBAR].is_active = (entry.num_value >> 1) & 1;

#define AG(group) (group), G_N_ELEMENTS((group))
  gtk_action_group_add_actions (action, AG(main_actions), NULL);
  gtk_action_group_add_toggle_actions (action, AG(toggle_actions), NULL);
  gtk_action_group_add_radio_actions (action, AG(fstoolbar_actions), !(entry.num_value & 1), G_CALLBACK(js_FSToolbarPos_cb), NULL);
  if (!xine_config_lookup_entry (xine, "gui.window_size", &entry))
    entry.num_value = 0;
  gtk_action_group_add_radio_actions (action, AG(window_actions), scale[entry.num_value], G_CALLBACK(js_Window_cb), NULL);
  gtk_action_group_add_radio_actions (action, AG(aspect_actions), 0, G_CALLBACK(js_Aspect_cb), NULL);
  gtk_action_group_add_radio_actions (action, AG(vis_actions), 0, G_CALLBACK(vis_cb), NULL);
  gtk_action_group_add_radio_actions (action, AG(subtitle_actions), -1, G_CALLBACK(js_Subtitles_cb), NULL);

  menubar = add_menu_ui (ui, 0);
  popup_menu = add_menu_ui (ui, 1);

  {
    guint merge = gtk_ui_manager_new_merge_id (ui);
    size_t n_autoplay = add_autoplay_actions (ui);
    size_t n_vis = add_vis_actions (ui, get_action ("VisNone"));
    menu_add_items (ui, "FileMenu/AutoPlay", AUTOPLAY_NAME, merge, n_autoplay);
    menu_add_items (ui, "ViewMenu/VisMenu/VisItems", VIS_NAME, merge, n_vis);
  }
  gtk_ui_manager_ensure_update (ui);

#ifdef ENABLE_NLS
  /* For some reason (GtkActionGroup?), we need to reset our locale info here
   * or translation may not happen correctly
   */
  setlocale (LC_ALL, "");
#endif

  action_items.fullscreen = get_toggle ("Fullscreen");
  action_items.resize_factor = get_toggle ("WindowDynamic");
  gtk_action_set_visible (GTK_ACTION(action_items.resize_factor), FALSE);
  action_items.auto_resize = get_toggle ("WindowAutosize");
  action_items.auto_rescale = get_toggle ("WindowMagLow");
  action_items.deinterlace = get_toggle ("Deinterlace");
  action_items.vo_postproc = get_toggle ("VideoPP");
  action_items.ao_postproc = get_toggle ("AudioPP");
  action_items.fs_toolbar = get_toggle ("FSToolbarVisible");
  action_items.wm_toolbar = get_toggle ("WMToolbarVisible");
  action_items.wm_toolbar_snap = get_toggle ("WMToolbarSnap");
  action_items.wm_unblank = get_toggle ("WMUnblank");
  action_items.fs_sticky = get_toggle ("FSSticky");

  action_items.aspect = get_radio_group ("AspectAuto");
  action_items.vis = get_radio_group ("VisNone");
  action_items.fs_toolbar_pos = get_radio_group ("FSToolbarTop");
  action_items.video_size = get_radio_group ("Window50");
  action_items.subtitles = get_radio_group ("Subtitles0");

  action_items.keypad = get_action ("Keypad");
  action_items.hidemain = get_action ("HideMain");
  action_items.showmain = get_action ("ShowMain");
  gtk_action_set_visible (action_items.showmain, FALSE);

  if (xine_config_lookup_entry (xine, "gui.windowed_mode.separate_toolbar",
				&entry)
      && !entry.num_value)
  {
    gtk_action_set_sensitive (&action_items.wm_toolbar->parent, FALSE);
    gtk_action_set_sensitive (&action_items.wm_toolbar_snap->parent, FALSE);
  }

  GSList *proxy = gtk_action_get_proxies (get_action ("SubsMenu"));
  while (proxy)
  {
    g_signal_connect (proxy->data, "event", (GCallback) subs_menu_cb, action);
    proxy = g_slist_next (proxy);
  }
}
