diff options
Diffstat (limited to 'src/fe-gtk/menu.c')
-rw-r--r-- | src/fe-gtk/menu.c | 2270 |
1 files changed, 2270 insertions, 0 deletions
diff --git a/src/fe-gtk/menu.c b/src/fe-gtk/menu.c new file mode 100644 index 00000000..d04be222 --- /dev/null +++ b/src/fe-gtk/menu.c @@ -0,0 +1,2270 @@ +/* X-Chat + * Copyright (C) 1998-2007 Peter Zelezny. + * + * 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 + */ + +#include <stdio.h> +#include <stdlib.h> +#include <fcntl.h> +#include <string.h> +#include <unistd.h> + +#ifdef WIN32 +#include <windows.h> +#endif + +#include "fe-gtk.h" + +#include <gtk/gtkhbox.h> +#include <gtk/gtkcheckmenuitem.h> +#include <gtk/gtkentry.h> +#include <gtk/gtkimage.h> +#include <gtk/gtkimagemenuitem.h> +#include <gtk/gtkradiomenuitem.h> +#include <gtk/gtklabel.h> +#include <gtk/gtkmessagedialog.h> +#include <gtk/gtkmenu.h> +#include <gtk/gtkmenubar.h> +#include <gtk/gtkstock.h> +#include <gtk/gtkversion.h> +#include <gdk/gdkkeysyms.h> + +#include "../common/xchat.h" +#include "../common/xchatc.h" +#include "../common/cfgfiles.h" +#include "../common/outbound.h" +#include "../common/ignore.h" +#include "../common/fe.h" +#include "../common/server.h" +#include "../common/servlist.h" +#include "../common/notify.h" +#include "../common/util.h" +#include "xtext.h" +#include "about.h" +#include "ascii.h" +#include "banlist.h" +#include "chanlist.h" +#include "editlist.h" +#include "fkeys.h" +#include "gtkutil.h" +#include "maingui.h" +#include "notifygui.h" +#include "pixmaps.h" +#include "rawlog.h" +#include "palette.h" +#include "plugingui.h" +#include "search.h" +#include "textgui.h" +#include "urlgrab.h" +#include "userlistgui.h" +#include "menu.h" + +static GSList *submenu_list; + +enum +{ + M_MENUITEM, + M_NEWMENU, + M_END, + M_SEP, + M_MENUTOG, + M_MENURADIO, + M_MENUSTOCK, + M_MENUPIX, + M_MENUSUB +}; + +struct mymenu +{ + char *text; + void *callback; + char *image; + unsigned char type; /* M_XXX */ + unsigned char id; /* MENU_ID_XXX (menu.h) */ + unsigned char state; /* ticked or not? */ + unsigned char sensitive; /* shaded out? */ + guint key; /* GDK_x */ +}; + +#define XCMENU_DOLIST 1 +#define XCMENU_SHADED 1 +#define XCMENU_MARKUP 2 +#define XCMENU_MNEMONIC 4 + +/* execute a userlistbutton/popupmenu command */ + +static void +nick_command (session * sess, char *cmd) +{ + if (*cmd == '!') + xchat_exec (cmd + 1); + else + handle_command (sess, cmd, TRUE); +} + +/* fill in the %a %s %n etc and execute the command */ + +void +nick_command_parse (session *sess, char *cmd, char *nick, char *allnick) +{ + char *buf; + char *host = _("Host unknown"); + struct User *user; + int len; + +/* if (sess->type == SESS_DIALOG) + { + buf = (char *)(GTK_ENTRY (sess->gui->topic_entry)->text); + buf = strrchr (buf, '@'); + if (buf) + host = buf + 1; + } else*/ + { + user = userlist_find (sess, nick); + if (user && user->hostname) + host = strchr (user->hostname, '@') + 1; + } + + /* this can't overflow, since popup->cmd is only 256 */ + len = strlen (cmd) + strlen (nick) + strlen (allnick) + 512; + buf = malloc (len); + + auto_insert (buf, len, cmd, 0, 0, allnick, sess->channel, "", + server_get_network (sess->server, TRUE), host, + sess->server->nick, nick); + + nick_command (sess, buf); + + free (buf); +} + +/* userlist button has been clicked */ + +void +userlist_button_cb (GtkWidget * button, char *cmd) +{ + int i, num_sel, using_allnicks = FALSE; + char **nicks, *allnicks; + char *nick = NULL; + session *sess; + + sess = current_sess; + + if (strstr (cmd, "%a")) + using_allnicks = TRUE; + + if (sess->type == SESS_DIALOG) + { + /* fake a selection */ + nicks = malloc (sizeof (char *) * 2); + nicks[0] = g_strdup (sess->channel); + nicks[1] = NULL; + num_sel = 1; + } else + { + /* find number of selected rows */ + nicks = userlist_selection_list (sess->gui->user_tree, &num_sel); + if (num_sel < 1) + { + nick_command_parse (sess, cmd, "", ""); + return; + } + } + + /* create "allnicks" string */ + allnicks = malloc (((NICKLEN + 1) * num_sel) + 1); + *allnicks = 0; + + i = 0; + while (nicks[i]) + { + if (i > 0) + strcat (allnicks, " "); + strcat (allnicks, nicks[i]); + + if (!nick) + nick = nicks[0]; + + /* if not using "%a", execute the command once for each nickname */ + if (!using_allnicks) + nick_command_parse (sess, cmd, nicks[i], ""); + + i++; + } + + if (using_allnicks) + { + if (!nick) + nick = ""; + nick_command_parse (sess, cmd, nick, allnicks); + } + + while (num_sel) + { + num_sel--; + g_free (nicks[num_sel]); + } + + free (nicks); + free (allnicks); +} + +/* a popup-menu-item has been selected */ + +static void +popup_menu_cb (GtkWidget * item, char *cmd) +{ + char *nick; + + /* the userdata is set in menu_quick_item() */ + nick = g_object_get_data (G_OBJECT (item), "u"); + + if (!nick) /* userlist popup menu */ + { + /* treat it just like a userlist button */ + userlist_button_cb (NULL, cmd); + return; + } + + if (!current_sess) /* for url grabber window */ + nick_command_parse (sess_list->data, cmd, nick, nick); + else + nick_command_parse (current_sess, cmd, nick, nick); +} + +GtkWidget * +menu_toggle_item (char *label, GtkWidget *menu, void *callback, void *userdata, + int state) +{ + GtkWidget *item; + + item = gtk_check_menu_item_new_with_mnemonic (label); + gtk_check_menu_item_set_active ((GtkCheckMenuItem*)item, state); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + g_signal_connect (G_OBJECT (item), "activate", + G_CALLBACK (callback), userdata); + gtk_widget_show (item); + + return item; +} + +GtkWidget * +menu_quick_item (char *cmd, char *label, GtkWidget * menu, int flags, + gpointer userdata, char *icon) +{ + GtkWidget *img, *item; + char *path; + + if (!label) + item = gtk_menu_item_new (); + else + { + if (icon) + { + /*if (flags & XCMENU_MARKUP) + item = gtk_image_menu_item_new_with_markup (label); + else*/ + item = gtk_image_menu_item_new_with_mnemonic (label); + img = NULL; + if (access (icon, R_OK) == 0) /* try fullpath */ + img = gtk_image_new_from_file (icon); + else + { + /* try relative to ~/.xchat2 */ + path = g_strdup_printf ("%s/%s", get_xdir_fs (), icon); + if (access (path, R_OK) == 0) + img = gtk_image_new_from_file (path); + else + img = gtk_image_new_from_stock (icon, GTK_ICON_SIZE_MENU); + g_free (path); + } + + if (img) + gtk_image_menu_item_set_image ((GtkImageMenuItem *)item, img); + } + else + { + if (flags & XCMENU_MARKUP) + { + item = gtk_menu_item_new_with_label (""); + if (flags & XCMENU_MNEMONIC) + gtk_label_set_markup_with_mnemonic (GTK_LABEL (GTK_BIN (item)->child), label); + else + gtk_label_set_markup (GTK_LABEL (GTK_BIN (item)->child), label); + } else + { + if (flags & XCMENU_MNEMONIC) + item = gtk_menu_item_new_with_mnemonic (label); + else + item = gtk_menu_item_new_with_label (label); + } + } + } + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + g_object_set_data (G_OBJECT (item), "u", userdata); + if (cmd) + g_signal_connect (G_OBJECT (item), "activate", + G_CALLBACK (popup_menu_cb), cmd); + if (flags & XCMENU_SHADED) + gtk_widget_set_sensitive (GTK_WIDGET (item), FALSE); + gtk_widget_show_all (item); + + return item; +} + +static void +menu_quick_item_with_callback (void *callback, char *label, GtkWidget * menu, + void *arg) +{ + GtkWidget *item; + + item = gtk_menu_item_new_with_label (label); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + g_signal_connect (G_OBJECT (item), "activate", + G_CALLBACK (callback), arg); + gtk_widget_show (item); +} + +GtkWidget * +menu_quick_sub (char *name, GtkWidget *menu, GtkWidget **sub_item_ret, int flags, int pos) +{ + GtkWidget *sub_menu; + GtkWidget *sub_item; + + if (!name) + return menu; + + /* Code to add a submenu */ + sub_menu = gtk_menu_new (); + if (flags & XCMENU_MARKUP) + { + sub_item = gtk_menu_item_new_with_label (""); + gtk_label_set_markup (GTK_LABEL (GTK_BIN (sub_item)->child), name); + } + else + { + if (flags & XCMENU_MNEMONIC) + sub_item = gtk_menu_item_new_with_mnemonic (name); + else + sub_item = gtk_menu_item_new_with_label (name); + } + gtk_menu_shell_insert (GTK_MENU_SHELL (menu), sub_item, pos); + gtk_widget_show (sub_item); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (sub_item), sub_menu); + + if (sub_item_ret) + *sub_item_ret = sub_item; + + if (flags & XCMENU_DOLIST) + /* We create a new element in the list */ + submenu_list = g_slist_prepend (submenu_list, sub_menu); + return sub_menu; +} + +static GtkWidget * +menu_quick_endsub () +{ + /* Just delete the first element in the linked list pointed to by first */ + if (submenu_list) + submenu_list = g_slist_remove (submenu_list, submenu_list->data); + + if (submenu_list) + return (submenu_list->data); + else + return NULL; +} + +static void +toggle_cb (GtkWidget *item, char *pref_name) +{ + char buf[256]; + + if (GTK_CHECK_MENU_ITEM (item)->active) + snprintf (buf, sizeof (buf), "set %s 1", pref_name); + else + snprintf (buf, sizeof (buf), "set %s 0", pref_name); + + handle_command (current_sess, buf, FALSE); +} + +static int +is_in_path (char *cmd) +{ + char *prog = strdup (cmd + 1); /* 1st char is "!" */ + char *space, *path, *orig; + + orig = prog; /* save for free()ing */ + /* special-case these default entries. */ + /* 123456789012345678 */ + if (strncmp (prog, "gnome-terminal -x ", 18) == 0) + /* don't check for gnome-terminal, but the thing it's executing! */ + prog += 18; + + space = strchr (prog, ' '); /* this isn't 100% but good enuf */ + if (space) + *space = 0; + + path = g_find_program_in_path (prog); + if (path) + { + g_free (path); + g_free (orig); + return 1; + } + + g_free (orig); + return 0; +} + +/* syntax: "LABEL~ICON~STUFF~ADDED~LATER~" */ + +static void +menu_extract_icon (char *name, char **label, char **icon) +{ + char *p = name; + char *start = NULL; + char *end = NULL; + + while (*p) + { + if (*p == '~') + { + /* escape \~ */ + if (p == name || p[-1] != '\\') + { + if (!start) + start = p + 1; + else if (!end) + end = p + 1; + } + } + p++; + } + + if (!end) + end = p; + + if (start && start != end) + { + *label = g_strndup (name, (start - name) - 1); + *icon = g_strndup (start, (end - start) - 1); + } + else + { + *label = g_strdup (name); + *icon = NULL; + } +} + +/* append items to "menu" using the (struct popup*) list provided */ + +void +menu_create (GtkWidget *menu, GSList *list, char *target, int check_path) +{ + struct popup *pop; + GtkWidget *tempmenu = menu, *subitem = NULL; + int childcount = 0; + + submenu_list = g_slist_prepend (0, menu); + while (list) + { + pop = (struct popup *) list->data; + + if (!strncasecmp (pop->name, "SUB", 3)) + { + childcount = 0; + tempmenu = menu_quick_sub (pop->cmd, tempmenu, &subitem, XCMENU_DOLIST|XCMENU_MNEMONIC, -1); + + } else if (!strncasecmp (pop->name, "TOGGLE", 6)) + { + childcount++; + menu_toggle_item (pop->name + 7, tempmenu, toggle_cb, pop->cmd, + cfg_get_bool (pop->cmd)); + + } else if (!strncasecmp (pop->name, "ENDSUB", 6)) + { + /* empty sub menu due to no programs in PATH? */ + if (check_path && childcount < 1) + gtk_widget_destroy (subitem); + subitem = NULL; + + if (tempmenu != menu) + tempmenu = menu_quick_endsub (); + /* If we get here and tempmenu equals menu that means we havent got any submenus to exit from */ + + } else if (!strncasecmp (pop->name, "SEP", 3)) + { + menu_quick_item (0, 0, tempmenu, XCMENU_SHADED, 0, 0); + + } else + { + char *icon, *label; + + /* default command in xchat.c */ + if (pop->cmd[0] == 'n' && !strcmp (pop->cmd, "notify -n ASK %s")) + { + /* don't create this item if already in notify list */ + if (!target || notify_is_in_list (current_sess->server, target)) + { + list = list->next; + continue; + } + } + + menu_extract_icon (pop->name, &label, &icon); + + if (!check_path || pop->cmd[0] != '!') + { + menu_quick_item (pop->cmd, label, tempmenu, 0, target, icon); + /* check if the program is in path, if not, leave it out! */ + } else if (is_in_path (pop->cmd)) + { + childcount++; + menu_quick_item (pop->cmd, label, tempmenu, 0, target, icon); + } + + g_free (label); + g_free (icon); + } + + list = list->next; + } + + /* Let's clean up the linked list from mem */ + while (submenu_list) + submenu_list = g_slist_remove (submenu_list, submenu_list->data); +} + +static char *str_copy = NULL; /* for all pop-up menus */ +static GtkWidget *nick_submenu = NULL; /* user info submenu */ + +static void +menu_destroy (GtkWidget *menu, gpointer objtounref) +{ + gtk_widget_destroy (menu); + g_object_unref (menu); + if (objtounref) + g_object_unref (G_OBJECT (objtounref)); + nick_submenu = NULL; +} + +static void +menu_popup (GtkWidget *menu, GdkEventButton *event, gpointer objtounref) +{ +#if (GTK_MAJOR_VERSION != 2) || (GTK_MINOR_VERSION != 0) + if (event && event->window) + gtk_menu_set_screen (GTK_MENU (menu), gdk_drawable_get_screen (event->window)); +#endif + + g_object_ref (menu); + g_object_ref_sink (menu); + g_object_unref (menu); + g_signal_connect (G_OBJECT (menu), "selection-done", + G_CALLBACK (menu_destroy), objtounref); + gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, + 0, event ? event->time : 0); +} + +static void +menu_nickinfo_cb (GtkWidget *menu, session *sess) +{ + char buf[512]; + + if (!is_session (sess)) + return; + + /* issue a /WHOIS */ + snprintf (buf, sizeof (buf), "WHOIS %s %s", str_copy, str_copy); + handle_command (sess, buf, FALSE); + /* and hide the output */ + sess->server->skip_next_whois = 1; +} + +static void +copy_to_clipboard_cb (GtkWidget *item, char *url) +{ + gtkutil_copy_to_clipboard (item, NULL, url); +} + +/* returns boolean: Some data is missing */ + +static gboolean +menu_create_nickinfo_menu (struct User *user, GtkWidget *submenu) +{ + char buf[512]; + char unknown[96]; + char *real, *fmt; + struct away_msg *away; + gboolean missing = FALSE; + GtkWidget *item; + + /* let the translators tweak this if need be */ + fmt = _("<tt><b>%-11s</b></tt> %s"); + snprintf (unknown, sizeof (unknown), "<i>%s</i>", _("Unknown")); + + if (user->realname) + { + real = strip_color (user->realname, -1, STRIP_ALL|STRIP_ESCMARKUP); + snprintf (buf, sizeof (buf), fmt, _("Real Name:"), real); + g_free (real); + } else + { + snprintf (buf, sizeof (buf), fmt, _("Real Name:"), unknown); + } + item = menu_quick_item (0, buf, submenu, XCMENU_MARKUP, 0, 0); + g_signal_connect (G_OBJECT (item), "activate", + G_CALLBACK (copy_to_clipboard_cb), + user->realname ? user->realname : unknown); + + snprintf (buf, sizeof (buf), fmt, _("User:"), + user->hostname ? user->hostname : unknown); + item = menu_quick_item (0, buf, submenu, XCMENU_MARKUP, 0, 0); + g_signal_connect (G_OBJECT (item), "activate", + G_CALLBACK (copy_to_clipboard_cb), + user->hostname ? user->hostname : unknown); + + snprintf (buf, sizeof (buf), fmt, _("Country:"), + user->hostname ? country(user->hostname) : unknown); + item = menu_quick_item (0, buf, submenu, XCMENU_MARKUP, 0, 0); + g_signal_connect (G_OBJECT (item), "activate", + G_CALLBACK (copy_to_clipboard_cb), + user->hostname ? country(user->hostname) : unknown); + + snprintf (buf, sizeof (buf), fmt, _("Server:"), + user->servername ? user->servername : unknown); + item = menu_quick_item (0, buf, submenu, XCMENU_MARKUP, 0, 0); + g_signal_connect (G_OBJECT (item), "activate", + G_CALLBACK (copy_to_clipboard_cb), + user->servername ? user->servername : unknown); + + if (user->lasttalk) + { + char min[96]; + + snprintf (min, sizeof (min), _("%u minutes ago"), + (unsigned int) ((time (0) - user->lasttalk) / 60)); + snprintf (buf, sizeof (buf), fmt, _("Last Msg:"), min); + } else + { + snprintf (buf, sizeof (buf), fmt, _("Last Msg:"), unknown); + } + menu_quick_item (0, buf, submenu, XCMENU_MARKUP, 0, 0); + + if (user->away) + { + away = server_away_find_message (current_sess->server, user->nick); + if (away) + { + char *msg = strip_color (away->message ? away->message : unknown, -1, STRIP_ALL|STRIP_ESCMARKUP); + snprintf (buf, sizeof (buf), fmt, _("Away Msg:"), msg); + g_free (msg); + item = menu_quick_item (0, buf, submenu, XCMENU_MARKUP, 0, 0); + g_signal_connect (G_OBJECT (item), "activate", + G_CALLBACK (copy_to_clipboard_cb), + away->message ? away->message : unknown); + } + else + missing = TRUE; + } + + return missing; +} + +void +fe_userlist_update (session *sess, struct User *user) +{ + GList *items, *next; + + if (!nick_submenu || !str_copy) + return; + + /* not the same nick as the menu? */ + if (sess->server->p_cmp (user->nick, str_copy)) + return; + + /* get rid of the "show" signal */ + g_signal_handlers_disconnect_by_func (nick_submenu, menu_nickinfo_cb, sess); + + /* destroy all the old items */ + items = ((GtkMenuShell *) nick_submenu)->children; + while (items) + { + next = items->next; + gtk_widget_destroy (items->data); + items = next; + } + + /* and re-create them with new info */ + menu_create_nickinfo_menu (user, nick_submenu); +} + +void +menu_nickmenu (session *sess, GdkEventButton *event, char *nick, int num_sel) +{ + char buf[512]; + struct User *user; + GtkWidget *submenu, *menu = gtk_menu_new (); + + if (str_copy) + free (str_copy); + str_copy = strdup (nick); + + submenu_list = 0; /* first time through, might not be 0 */ + + /* more than 1 nick selected? */ + if (num_sel > 1) + { + snprintf (buf, sizeof (buf), _("%d nicks selected."), num_sel); + menu_quick_item (0, buf, menu, 0, 0, 0); + menu_quick_item (0, 0, menu, XCMENU_SHADED, 0, 0); + } else + { + user = userlist_find (sess, nick); /* lasttalk is channel specific */ + if (!user) + user = userlist_find_global (current_sess->server, nick); + if (user) + { + nick_submenu = submenu = menu_quick_sub (nick, menu, NULL, XCMENU_DOLIST, -1); + + if (menu_create_nickinfo_menu (user, submenu) || + !user->hostname || !user->realname || !user->servername) + { + g_signal_connect (G_OBJECT (submenu), "show", G_CALLBACK (menu_nickinfo_cb), sess); + } + + menu_quick_endsub (); + menu_quick_item (0, 0, menu, XCMENU_SHADED, 0, 0); + } + } + + if (num_sel > 1) + menu_create (menu, popup_list, NULL, FALSE); + else + menu_create (menu, popup_list, str_copy, FALSE); + + if (num_sel == 0) /* xtext click */ + menu_add_plugin_items (menu, "\x5$NICK", str_copy); + else /* userlist treeview click */ + menu_add_plugin_items (menu, "\x5$NICK", NULL); + + menu_popup (menu, event, NULL); +} + +/* stuff for the View menu */ + +static void +menu_showhide_cb (session *sess) +{ + if (prefs.hidemenu) + gtk_widget_hide (sess->gui->menu); + else + gtk_widget_show (sess->gui->menu); +} + +static void +menu_topic_showhide_cb (session *sess) +{ + if (prefs.topicbar) + gtk_widget_show (sess->gui->topic_bar); + else + gtk_widget_hide (sess->gui->topic_bar); +} + +static void +menu_userlist_showhide_cb (session *sess) +{ + mg_decide_userlist (sess, TRUE); +} + +static void +menu_ulbuttons_showhide_cb (session *sess) +{ + if (prefs.userlistbuttons) + gtk_widget_show (sess->gui->button_box); + else + gtk_widget_hide (sess->gui->button_box); +} + +static void +menu_cmbuttons_showhide_cb (session *sess) +{ + switch (sess->type) + { + case SESS_CHANNEL: + if (prefs.chanmodebuttons) + gtk_widget_show (sess->gui->topicbutton_box); + else + gtk_widget_hide (sess->gui->topicbutton_box); + break; + default: + gtk_widget_hide (sess->gui->topicbutton_box); + } +} + +static void +menu_setting_foreach (void (*callback) (session *), int id, guint state) +{ + session *sess; + GSList *list; + int maindone = FALSE; /* do it only once for EVERY tab */ + + list = sess_list; + while (list) + { + sess = list->data; + + if (!sess->gui->is_tab || !maindone) + { + if (sess->gui->is_tab) + maindone = TRUE; + if (id != -1) + GTK_CHECK_MENU_ITEM (sess->gui->menu_item[id])->active = state; + if (callback) + callback (sess); + } + + list = list->next; + } +} + +void +menu_bar_toggle (void) +{ + prefs.hidemenu = !prefs.hidemenu; + menu_setting_foreach (menu_showhide_cb, MENU_ID_MENUBAR, !prefs.hidemenu); +} + +static void +menu_bar_toggle_cb (void) +{ + menu_bar_toggle (); + if (prefs.hidemenu) + fe_message (_("The Menubar is now hidden. You can show it again" + " by pressing F9 or right-clicking in a blank part of" + " the main text area."), FE_MSG_INFO); +} + +static void +menu_topicbar_toggle (GtkWidget *wid, gpointer ud) +{ + prefs.topicbar = !prefs.topicbar; + menu_setting_foreach (menu_topic_showhide_cb, MENU_ID_TOPICBAR, + prefs.topicbar); +} + +static void +menu_userlist_toggle (GtkWidget *wid, gpointer ud) +{ + prefs.hideuserlist = !prefs.hideuserlist; + menu_setting_foreach (menu_userlist_showhide_cb, MENU_ID_USERLIST, + !prefs.hideuserlist); +} + +static void +menu_ulbuttons_toggle (GtkWidget *wid, gpointer ud) +{ + prefs.userlistbuttons = !prefs.userlistbuttons; + menu_setting_foreach (menu_ulbuttons_showhide_cb, MENU_ID_ULBUTTONS, + prefs.userlistbuttons); +} + +static void +menu_cmbuttons_toggle (GtkWidget *wid, gpointer ud) +{ + prefs.chanmodebuttons = !prefs.chanmodebuttons; + menu_setting_foreach (menu_cmbuttons_showhide_cb, MENU_ID_MODEBUTTONS, + prefs.chanmodebuttons); +} + +void +menu_middlemenu (session *sess, GdkEventButton *event) +{ + GtkWidget *menu; + GtkAccelGroup *accel_group; + + accel_group = gtk_accel_group_new (); + menu = menu_create_main (accel_group, FALSE, sess->server->is_away, !sess->gui->is_tab, NULL); + menu_popup (menu, event, accel_group); +} + +static void +open_url_cb (GtkWidget *item, char *url) +{ + char buf[512]; + + /* pass this to /URL so it can handle irc:// */ + snprintf (buf, sizeof (buf), "URL %s", url); + handle_command (current_sess, buf, FALSE); +} + +void +menu_urlmenu (GdkEventButton *event, char *url) +{ + GtkWidget *menu; + char *tmp, *chop; + + if (str_copy) + free (str_copy); + str_copy = strdup (url); + + menu = gtk_menu_new (); + /* more than 51 chars? Chop it */ + if (g_utf8_strlen (str_copy, -1) >= 52) + { + tmp = strdup (str_copy); + chop = g_utf8_offset_to_pointer (tmp, 48); + chop[0] = chop[1] = chop[2] = '.'; + chop[3] = 0; + menu_quick_item (0, tmp, menu, XCMENU_SHADED, 0, 0); + free (tmp); + } else + { + menu_quick_item (0, str_copy, menu, XCMENU_SHADED, 0, 0); + } + menu_quick_item (0, 0, menu, XCMENU_SHADED, 0, 0); + + /* Two hardcoded entries */ + if (strncmp (str_copy, "irc://", 6) == 0 || + strncmp (str_copy, "ircs://",7) == 0) + menu_quick_item_with_callback (open_url_cb, _("Connect"), menu, str_copy); + else + menu_quick_item_with_callback (open_url_cb, _("Open Link in Browser"), menu, str_copy); + menu_quick_item_with_callback (copy_to_clipboard_cb, _("Copy Selected Link"), menu, str_copy); + /* custom ones from urlhandlers.conf */ + menu_create (menu, urlhandler_list, str_copy, TRUE); + menu_add_plugin_items (menu, "\x4$URL", str_copy); + menu_popup (menu, event, NULL); +} + +static void +menu_chan_cycle (GtkWidget * menu, char *chan) +{ + char tbuf[256]; + + if (current_sess) + { + snprintf (tbuf, sizeof tbuf, "CYCLE %s", chan); + handle_command (current_sess, tbuf, FALSE); + } +} + +static void +menu_chan_part (GtkWidget * menu, char *chan) +{ + char tbuf[256]; + + if (current_sess) + { + snprintf (tbuf, sizeof tbuf, "part %s", chan); + handle_command (current_sess, tbuf, FALSE); + } +} + +static void +menu_chan_join (GtkWidget * menu, char *chan) +{ + char tbuf[256]; + + if (current_sess) + { + snprintf (tbuf, sizeof tbuf, "join %s", chan); + handle_command (current_sess, tbuf, FALSE); + } +} + +void +menu_chanmenu (struct session *sess, GdkEventButton * event, char *chan) +{ + GtkWidget *menu; + int is_joined = FALSE; + + if (find_channel (sess->server, chan)) + is_joined = TRUE; + + if (str_copy) + free (str_copy); + str_copy = strdup (chan); + + menu = gtk_menu_new (); + + menu_quick_item (0, chan, menu, XCMENU_SHADED, str_copy, 0); + menu_quick_item (0, 0, menu, XCMENU_SHADED, str_copy, 0); + + if (!is_joined) + menu_quick_item_with_callback (menu_chan_join, _("Join Channel"), menu, + str_copy); + else + { + menu_quick_item_with_callback (menu_chan_part, _("Part Channel"), menu, + str_copy); + menu_quick_item_with_callback (menu_chan_cycle, _("Cycle Channel"), menu, + str_copy); + } + + menu_addfavoritemenu (sess->server, menu, str_copy); + + menu_add_plugin_items (menu, "\x5$CHAN", str_copy); + menu_popup (menu, event, NULL); +} + +static void +menu_delfav_cb (GtkWidget *item, server *serv) +{ + servlist_autojoinedit (serv->network, str_copy, FALSE); +} + +static void +menu_addfav_cb (GtkWidget *item, server *serv) +{ + servlist_autojoinedit (serv->network, str_copy, TRUE); +} + +void +menu_addfavoritemenu (server *serv, GtkWidget *menu, char *channel) +{ + if (!serv->network) + return; + + if (channel != str_copy) + { + if (str_copy) + free (str_copy); + str_copy = strdup (channel); + } + + if (joinlist_is_in_list (serv, channel)) + mg_create_icon_item (_("_Remove from Favorites"), GTK_STOCK_REMOVE, menu, menu_delfav_cb, serv); + else + mg_create_icon_item (_("_Add to Favorites"), GTK_STOCK_ADD, menu, menu_addfav_cb, serv); +} + +static void +menu_open_server_list (GtkWidget *wid, gpointer none) +{ + fe_serverlist_open (current_sess); +} + +static void +menu_settings (GtkWidget * wid, gpointer none) +{ + extern void setup_open (void); + setup_open (); +} + +static void +menu_usermenu (void) +{ + editlist_gui_open (NULL, NULL, usermenu_list, _("XChat: User menu"), + "usermenu", "usermenu.conf", 0); +} + +static void +usermenu_create (GtkWidget *menu) +{ + menu_create (menu, usermenu_list, "", FALSE); + menu_quick_item (0, 0, menu, XCMENU_SHADED, 0, 0); /* sep */ + menu_quick_item_with_callback (menu_usermenu, _("Edit This Menu..."), menu, 0); +} + +static void +usermenu_destroy (GtkWidget * menu) +{ + GList *items = ((GtkMenuShell *) menu)->children; + GList *next; + + while (items) + { + next = items->next; + gtk_widget_destroy (items->data); + items = next; + } +} + +void +usermenu_update (void) +{ + int done_main = FALSE; + GSList *list = sess_list; + session *sess; + GtkWidget *menu; + + while (list) + { + sess = list->data; + menu = sess->gui->menu_item[MENU_ID_USERMENU]; + if (sess->gui->is_tab) + { + if (!done_main && menu) + { + usermenu_destroy (menu); + usermenu_create (menu); + done_main = TRUE; + } + } else if (menu) + { + usermenu_destroy (menu); + usermenu_create (menu); + } + list = list->next; + } +} + +static void +menu_newserver_window (GtkWidget * wid, gpointer none) +{ + int old = prefs.tabchannels; + + prefs.tabchannels = 0; + new_ircwindow (NULL, NULL, SESS_SERVER, 0); + prefs.tabchannels = old; +} + +static void +menu_newchannel_window (GtkWidget * wid, gpointer none) +{ + int old = prefs.tabchannels; + + prefs.tabchannels = 0; + new_ircwindow (current_sess->server, NULL, SESS_CHANNEL, 0); + prefs.tabchannels = old; +} + +static void +menu_newserver_tab (GtkWidget * wid, gpointer none) +{ + int old = prefs.tabchannels; + int oldf = prefs.newtabstofront; + + prefs.tabchannels = 1; + /* force focus if setting is "only requested tabs" */ + if (prefs.newtabstofront == 2) + prefs.newtabstofront = 1; + new_ircwindow (NULL, NULL, SESS_SERVER, 0); + prefs.tabchannels = old; + prefs.newtabstofront = oldf; +} + +static void +menu_newchannel_tab (GtkWidget * wid, gpointer none) +{ + int old = prefs.tabchannels; + + prefs.tabchannels = 1; + new_ircwindow (current_sess->server, NULL, SESS_CHANNEL, 0); + prefs.tabchannels = old; +} + +static void +menu_rawlog (GtkWidget * wid, gpointer none) +{ + open_rawlog (current_sess->server); +} + +static void +menu_detach (GtkWidget * wid, gpointer none) +{ + mg_detach (current_sess, 0); +} + +static void +menu_close (GtkWidget * wid, gpointer none) +{ + mg_close_sess (current_sess); +} + +static void +menu_quit (GtkWidget * wid, gpointer none) +{ + mg_open_quit_dialog (FALSE); +} + +static void +menu_search () +{ + search_open (current_sess); +} + +static void +menu_resetmarker (GtkWidget * wid, gpointer none) +{ + gtk_xtext_reset_marker_pos (GTK_XTEXT (current_sess->gui->xtext)); +} + +static void +menu_flushbuffer (GtkWidget * wid, gpointer none) +{ + fe_text_clear (current_sess, 0); +} + +static void +savebuffer_req_done (session *sess, char *file) +{ + int fh; + + if (!file) + return; + + fh = open (file, O_TRUNC | O_WRONLY | O_CREAT, 0600); + if (fh != -1) + { + gtk_xtext_save (GTK_XTEXT (sess->gui->xtext), fh); + close (fh); + } +} + +static void +menu_savebuffer (GtkWidget * wid, gpointer none) +{ + gtkutil_file_req (_("Select an output filename"), savebuffer_req_done, + current_sess, NULL, FRF_WRITE); +} + +static void +menu_disconnect (GtkWidget * wid, gpointer none) +{ + handle_command (current_sess, "DISCON", FALSE); +} + +static void +menu_reconnect (GtkWidget * wid, gpointer none) +{ + if (current_sess->server->hostname[0]) + handle_command (current_sess, "RECONNECT", FALSE); + else + fe_serverlist_open (current_sess); +} + +static void +menu_join_cb (GtkWidget *dialog, gint response, GtkEntry *entry) +{ + switch (response) + { + case GTK_RESPONSE_ACCEPT: + menu_chan_join (NULL, entry->text); + break; + + case GTK_RESPONSE_HELP: + chanlist_opengui (current_sess->server, TRUE); + break; + } + + gtk_widget_destroy (dialog); +} + +static void +menu_join_entry_cb (GtkWidget *entry, GtkDialog *dialog) +{ + gtk_dialog_response (dialog, GTK_RESPONSE_ACCEPT); +} + +static void +menu_join (GtkWidget * wid, gpointer none) +{ + GtkWidget *hbox, *dialog, *entry, *label; + + dialog = gtk_dialog_new_with_buttons (_("Join Channel"), + GTK_WINDOW (parent_window), 0, + _("Retrieve channel list..."), GTK_RESPONSE_HELP, + GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, + GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, + NULL); + gtk_box_set_homogeneous (GTK_BOX (GTK_DIALOG (dialog)->vbox), TRUE); + gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE); + hbox = gtk_hbox_new (TRUE, 0); + + entry = gtk_entry_new (); + GTK_ENTRY (entry)->editable = 0; /* avoid auto-selection */ + gtk_entry_set_text (GTK_ENTRY (entry), "#"); + g_signal_connect (G_OBJECT (entry), "activate", + G_CALLBACK (menu_join_entry_cb), dialog); + gtk_box_pack_end (GTK_BOX (hbox), entry, 0, 0, 0); + + label = gtk_label_new (_("Enter Channel to Join:")); + gtk_box_pack_end (GTK_BOX (hbox), label, 0, 0, 0); + + g_signal_connect (G_OBJECT (dialog), "response", + G_CALLBACK (menu_join_cb), entry); + + gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), hbox); + + gtk_widget_show_all (dialog); + + gtk_editable_set_editable (GTK_EDITABLE (entry), TRUE); + gtk_editable_set_position (GTK_EDITABLE (entry), 1); +} + +static void +menu_away (GtkCheckMenuItem *item, gpointer none) +{ + handle_command (current_sess, item->active ? "away" : "back", FALSE); +} + +static void +menu_chanlist (GtkWidget * wid, gpointer none) +{ + chanlist_opengui (current_sess->server, FALSE); +} + +static void +menu_banlist (GtkWidget * wid, gpointer none) +{ + banlist_opengui (current_sess); +} + +#ifdef USE_PLUGIN + +static void +menu_loadplugin (void) +{ + plugingui_load (); +} + +static void +menu_pluginlist (void) +{ + plugingui_open (); +} + +#else + +#define menu_pluginlist 0 +#define menu_loadplugin 0 + +#endif + +#define usercommands_help _("User Commands - Special codes:\n\n"\ + "%c = current channel\n"\ + "%e = current network name\n"\ + "%m = machine info\n"\ + "%n = your nick\n"\ + "%t = time/date\n"\ + "%v = xchat version\n"\ + "%2 = word 2\n"\ + "%3 = word 3\n"\ + "&2 = word 2 to the end of line\n"\ + "&3 = word 3 to the end of line\n\n"\ + "eg:\n"\ + "/cmd john hello\n\n"\ + "%2 would be \042john\042\n"\ + "&2 would be \042john hello\042.") + +#define ulbutton_help _("Userlist Buttons - Special codes:\n\n"\ + "%a = all selected nicks\n"\ + "%c = current channel\n"\ + "%e = current network name\n"\ + "%h = selected nick's hostname\n"\ + "%m = machine info\n"\ + "%n = your nick\n"\ + "%s = selected nick\n"\ + "%t = time/date\n") + +#define dlgbutton_help _("Dialog Buttons - Special codes:\n\n"\ + "%a = all selected nicks\n"\ + "%c = current channel\n"\ + "%e = current network name\n"\ + "%h = selected nick's hostname\n"\ + "%m = machine info\n"\ + "%n = your nick\n"\ + "%s = selected nick\n"\ + "%t = time/date\n") + +#define ctcp_help _("CTCP Replies - Special codes:\n\n"\ + "%d = data (the whole ctcp)\n"\ + "%e = current network name\n"\ + "%m = machine info\n"\ + "%s = nick who sent the ctcp\n"\ + "%t = time/date\n"\ + "%2 = word 2\n"\ + "%3 = word 3\n"\ + "&2 = word 2 to the end of line\n"\ + "&3 = word 3 to the end of line\n\n") + +#define url_help _("URL Handlers - Special codes:\n\n"\ + "%s = the URL string\n\n"\ + "Putting a ! infront of the command\n"\ + "indicates it should be sent to a\n"\ + "shell instead of XChat") + +static void +menu_usercommands (void) +{ + editlist_gui_open (NULL, NULL, command_list, _("XChat: User Defined Commands"), + "commands", "commands.conf", usercommands_help); +} + +static void +menu_ulpopup (void) +{ + editlist_gui_open (NULL, NULL, popup_list, _("XChat: Userlist Popup menu"), "popup", + "popup.conf", ulbutton_help); +} + +static void +menu_rpopup (void) +{ + editlist_gui_open (_("Text"), _("Replace with"), replace_list, _("XChat: Replace"), "replace", + "replace.conf", 0); +} + +static void +menu_urlhandlers (void) +{ + editlist_gui_open (NULL, NULL, urlhandler_list, _("XChat: URL Handlers"), "urlhandlers", + "urlhandlers.conf", url_help); +} + +static void +menu_evtpopup (void) +{ + pevent_dialog_show (); +} + +static void +menu_keypopup (void) +{ + key_dialog_show (); +} + +static void +menu_ulbuttons (void) +{ + editlist_gui_open (NULL, NULL, button_list, _("XChat: Userlist buttons"), "buttons", + "buttons.conf", ulbutton_help); +} + +static void +menu_dlgbuttons (void) +{ + editlist_gui_open (NULL, NULL, dlgbutton_list, _("XChat: Dialog buttons"), "dlgbuttons", + "dlgbuttons.conf", dlgbutton_help); +} + +static void +menu_ctcpguiopen (void) +{ + editlist_gui_open (NULL, NULL, ctcp_list, _("XChat: CTCP Replies"), "ctcpreply", + "ctcpreply.conf", ctcp_help); +} + +static void +menu_docs (GtkWidget *wid, gpointer none) +{ + fe_open_url ("http://xchat.org/docs/"); +} + +/*static void +menu_webpage (GtkWidget *wid, gpointer none) +{ + fe_open_url ("http://xchat.org"); +}*/ + +static void +menu_dcc_win (GtkWidget *wid, gpointer none) +{ + fe_dcc_open_recv_win (FALSE); + fe_dcc_open_send_win (FALSE); +} + +static void +menu_dcc_chat_win (GtkWidget *wid, gpointer none) +{ + fe_dcc_open_chat_win (FALSE); +} + +void +menu_change_layout (void) +{ + if (prefs.tab_layout == 0) + { + menu_setting_foreach (NULL, MENU_ID_LAYOUT_TABS, 1); + menu_setting_foreach (NULL, MENU_ID_LAYOUT_TREE, 0); + mg_change_layout (0); + } else + { + menu_setting_foreach (NULL, MENU_ID_LAYOUT_TABS, 0); + menu_setting_foreach (NULL, MENU_ID_LAYOUT_TREE, 1); + mg_change_layout (2); + } +} + +static void +menu_layout_cb (GtkWidget *item, gpointer none) +{ + prefs.tab_layout = 2; + if (GTK_CHECK_MENU_ITEM (item)->active) + prefs.tab_layout = 0; + + menu_change_layout (); +} + +static void +menu_apply_metres_cb (session *sess) +{ + mg_update_meters (sess->gui); +} + +static void +menu_metres_off (GtkWidget *item, gpointer none) +{ + if (GTK_CHECK_MENU_ITEM (item)->active) + { + prefs.lagometer = 0; + prefs.throttlemeter = 0; + menu_setting_foreach (menu_apply_metres_cb, -1, 0); + } +} + +static void +menu_metres_text (GtkWidget *item, gpointer none) +{ + if (GTK_CHECK_MENU_ITEM (item)->active) + { + prefs.lagometer = 2; + prefs.throttlemeter = 2; + menu_setting_foreach (menu_apply_metres_cb, -1, 0); + } +} + +static void +menu_metres_graph (GtkWidget *item, gpointer none) +{ + if (GTK_CHECK_MENU_ITEM (item)->active) + { + prefs.lagometer = 1; + prefs.throttlemeter = 1; + menu_setting_foreach (menu_apply_metres_cb, -1, 0); + } +} + +static void +menu_metres_both (GtkWidget *item, gpointer none) +{ + if (GTK_CHECK_MENU_ITEM (item)->active) + { + prefs.lagometer = 3; + prefs.throttlemeter = 3; + menu_setting_foreach (menu_apply_metres_cb, -1, 0); + } +} + +static struct mymenu mymenu[] = { + {N_("_XChat"), 0, 0, M_NEWMENU, 0, 0, 1}, + {N_("Network Li_st..."), menu_open_server_list, (char *)&pix_book, M_MENUPIX, 0, 0, 1, GDK_s}, + {0, 0, 0, M_SEP, 0, 0, 0}, + + {N_("_New"), 0, GTK_STOCK_NEW, M_MENUSUB, 0, 0, 1}, + {N_("Server Tab..."), menu_newserver_tab, 0, M_MENUITEM, 0, 0, 1, GDK_t}, + {N_("Channel Tab..."), menu_newchannel_tab, 0, M_MENUITEM, 0, 0, 1}, + {N_("Server Window..."), menu_newserver_window, 0, M_MENUITEM, 0, 0, 1}, + {N_("Channel Window..."), menu_newchannel_window, 0, M_MENUITEM, 0, 0, 1}, + {0, 0, 0, M_END, 0, 0, 0}, + {0, 0, 0, M_SEP, 0, 0, 0}, + +#ifdef USE_PLUGIN + {N_("_Load Plugin or Script..."), menu_loadplugin, GTK_STOCK_REVERT_TO_SAVED, M_MENUSTOCK, 0, 0, 1}, +#else + {N_("_Load Plugin or Script..."), 0, GTK_STOCK_REVERT_TO_SAVED, M_MENUSTOCK, 0, 0, 0}, +#endif + {0, 0, 0, M_SEP, 0, 0, 0}, /* 11 */ +#define DETACH_OFFSET (12) + {0, menu_detach, GTK_STOCK_REDO, M_MENUSTOCK, 0, 0, 1, GDK_I}, /* 12 */ +#define CLOSE_OFFSET (13) + {0, menu_close, GTK_STOCK_CLOSE, M_MENUSTOCK, 0, 0, 1, GDK_w}, + {0, 0, 0, M_SEP, 0, 0, 0}, + {N_("_Quit"), menu_quit, GTK_STOCK_QUIT, M_MENUSTOCK, 0, 0, 1, GDK_q}, /* 15 */ + + {N_("_View"), 0, 0, M_NEWMENU, 0, 0, 1}, +#define MENUBAR_OFFSET (17) + {N_("_Menu Bar"), menu_bar_toggle_cb, 0, M_MENUTOG, MENU_ID_MENUBAR, 0, 1, GDK_F9}, + {N_("_Topic Bar"), menu_topicbar_toggle, 0, M_MENUTOG, MENU_ID_TOPICBAR, 0, 1}, + {N_("_User List"), menu_userlist_toggle, 0, M_MENUTOG, MENU_ID_USERLIST, 0, 1, GDK_F7}, + {N_("U_serlist Buttons"), menu_ulbuttons_toggle, 0, M_MENUTOG, MENU_ID_ULBUTTONS, 0, 1}, + {N_("M_ode Buttons"), menu_cmbuttons_toggle, 0, M_MENUTOG, MENU_ID_MODEBUTTONS, 0, 1}, + {0, 0, 0, M_SEP, 0, 0, 0}, + {N_("_Channel Switcher"), 0, 0, M_MENUSUB, 0, 0, 1}, /* 23 */ +#define TABS_OFFSET (24) + {N_("_Tabs"), menu_layout_cb, 0, M_MENURADIO, MENU_ID_LAYOUT_TABS, 0, 1}, + {N_("T_ree"), 0, 0, M_MENURADIO, MENU_ID_LAYOUT_TREE, 0, 1}, + {0, 0, 0, M_END, 0, 0, 0}, + {N_("_Network Meters"), 0, 0, M_MENUSUB, 0, 0, 1}, /* 27 */ +#define METRE_OFFSET (28) + {N_("Off"), menu_metres_off, 0, M_MENURADIO, 0, 0, 1}, + {N_("Graph"), menu_metres_graph, 0, M_MENURADIO, 0, 0, 1}, + {N_("Text"), menu_metres_text, 0, M_MENURADIO, 0, 0, 1}, + {N_("Both"), menu_metres_both, 0, M_MENURADIO, 0, 0, 1}, + {0, 0, 0, M_END, 0, 0, 0}, /* 32 */ + + {N_("_Server"), 0, 0, M_NEWMENU, 0, 0, 1}, + {N_("_Disconnect"), menu_disconnect, GTK_STOCK_DISCONNECT, M_MENUSTOCK, MENU_ID_DISCONNECT, 0, 1}, + {N_("_Reconnect"), menu_reconnect, GTK_STOCK_CONNECT, M_MENUSTOCK, MENU_ID_RECONNECT, 0, 1}, + {N_("Join a Channel..."), menu_join, GTK_STOCK_JUMP_TO, M_MENUSTOCK, MENU_ID_JOIN, 0, 1}, + {N_("List of Channels..."), menu_chanlist, GTK_STOCK_INDEX, M_MENUITEM, 0, 0, 1}, + {0, 0, 0, M_SEP, 0, 0, 0}, +#define AWAY_OFFSET (39) + {N_("Marked Away"), menu_away, 0, M_MENUTOG, MENU_ID_AWAY, 0, 1, GDK_a}, + + {N_("_Usermenu"), 0, 0, M_NEWMENU, MENU_ID_USERMENU, 0, 1}, /* 40 */ + + {N_("S_ettings"), 0, 0, M_NEWMENU, 0, 0, 1}, + {N_("_Preferences"), menu_settings, GTK_STOCK_PREFERENCES, M_MENUSTOCK, 0, 0, 1}, + + {N_("Advanced"), 0, GTK_STOCK_JUSTIFY_LEFT, M_MENUSUB, 0, 0, 1}, + {N_("Auto Replace..."), menu_rpopup, 0, M_MENUITEM, 0, 0, 1}, + {N_("CTCP Replies..."), menu_ctcpguiopen, 0, M_MENUITEM, 0, 0, 1}, + {N_("Dialog Buttons..."), menu_dlgbuttons, 0, M_MENUITEM, 0, 0, 1}, + {N_("Keyboard Shortcuts..."), menu_keypopup, 0, M_MENUITEM, 0, 0, 1}, + {N_("Text Events..."), menu_evtpopup, 0, M_MENUITEM, 0, 0, 1}, + {N_("URL Handlers..."), menu_urlhandlers, 0, M_MENUITEM, 0, 0, 1}, + {N_("User Commands..."), menu_usercommands, 0, M_MENUITEM, 0, 0, 1}, + {N_("Userlist Buttons..."), menu_ulbuttons, 0, M_MENUITEM, 0, 0, 1}, + {N_("Userlist Popup..."), menu_ulpopup, 0, M_MENUITEM, 0, 0, 1}, + {0, 0, 0, M_END, 0, 0, 0}, /* 53 */ + + {N_("_Window"), 0, 0, M_NEWMENU, 0, 0, 1}, + {N_("Ban List..."), menu_banlist, 0, M_MENUITEM, 0, 0, 1}, + {N_("Character Chart..."), ascii_open, 0, M_MENUITEM, 0, 0, 1}, + {N_("Direct Chat..."), menu_dcc_chat_win, 0, M_MENUITEM, 0, 0, 1}, + {N_("File Transfers..."), menu_dcc_win, 0, M_MENUITEM, 0, 0, 1}, + {N_("Friends List..."), notify_opengui, 0, M_MENUITEM, 0, 0, 1}, + {N_("Ignore List..."), ignore_gui_open, 0, M_MENUITEM, 0, 0, 1}, + {N_("Plugins and Scripts..."), menu_pluginlist, 0, M_MENUITEM, 0, 0, 1}, + {N_("Raw Log..."), menu_rawlog, 0, M_MENUITEM, 0, 0, 1}, /* 62 */ + {N_("URL Grabber..."), url_opengui, 0, M_MENUITEM, 0, 0, 1}, + {0, 0, 0, M_SEP, 0, 0, 0}, + {N_("Reset Marker Line"), menu_resetmarker, 0, M_MENUITEM, 0, 0, 1, GDK_m}, + {N_("C_lear Text"), menu_flushbuffer, GTK_STOCK_CLEAR, M_MENUSTOCK, 0, 0, 1, GDK_l}, +#define SEARCH_OFFSET 67 + {N_("Search Text..."), menu_search, GTK_STOCK_FIND, M_MENUSTOCK, 0, 0, 1, GDK_f}, + {N_("Save Text..."), menu_savebuffer, GTK_STOCK_SAVE, M_MENUSTOCK, 0, 0, 1}, + + {N_("_Help"), 0, 0, M_NEWMENU, 0, 0, 1}, /* 69 */ + {N_("_Contents"), menu_docs, GTK_STOCK_HELP, M_MENUSTOCK, 0, 0, 1, GDK_F1}, +#if 0 + {N_("Check for updates"), menu_update, 0, M_MENUITEM, 0, 1}, +#endif + {N_("_About"), menu_about, GTK_STOCK_ABOUT, M_MENUSTOCK, 0, 0, 1}, + + {0, 0, 0, M_END, 0, 0, 0}, +}; + +GtkWidget * +create_icon_menu (char *labeltext, void *stock_name, int is_stock) +{ + GtkWidget *item, *img; + + if (is_stock) + img = gtk_image_new_from_stock (stock_name, GTK_ICON_SIZE_MENU); + else + img = gtk_image_new_from_pixbuf (*((GdkPixbuf **)stock_name)); + item = gtk_image_menu_item_new_with_mnemonic (labeltext); + gtk_image_menu_item_set_image ((GtkImageMenuItem *)item, img); + gtk_widget_show (img); + + return item; +} + +#if GTK_CHECK_VERSION(2,4,0) + +/* Override the default GTK2.4 handler, which would make menu + bindings not work when the menu-bar is hidden. */ +static gboolean +menu_canacaccel (GtkWidget *widget, guint signal_id, gpointer user_data) +{ + /* GTK2.2 behaviour */ +#if GTK_CHECK_VERSION(2,20,0) + return gtk_widget_is_sensitive (widget); +#else + return GTK_WIDGET_IS_SENSITIVE (widget); +#endif +} + +#endif + + +/* === STUFF FOR /MENU === */ + +static GtkMenuItem * +menu_find_item (GtkWidget *menu, char *name) +{ + GList *items = ((GtkMenuShell *) menu)->children; + GtkMenuItem *item; + GtkWidget *child; + const char *labeltext; + + while (items) + { + item = items->data; + child = GTK_BIN (item)->child; + if (child) /* separators arn't labels, skip them */ + { + labeltext = g_object_get_data (G_OBJECT (item), "name"); + if (!labeltext) + labeltext = gtk_label_get_text (GTK_LABEL (child)); + if (!menu_streq (labeltext, name, 1)) + return item; + } else if (name == NULL) + { + return item; + } + items = items->next; + } + + return NULL; +} + +static GtkWidget * +menu_find_path (GtkWidget *menu, char *path) +{ + GtkMenuItem *item; + char *s; + char name[128]; + int len; + + /* grab the next part of the path */ + s = strchr (path, '/'); + len = s - path; + if (!s) + len = strlen (path); + len = MIN (len, sizeof (name) - 1); + memcpy (name, path, len); + name[len] = 0; + + item = menu_find_item (menu, name); + if (!item) + return NULL; + + menu = gtk_menu_item_get_submenu (item); + if (!menu) + return NULL; + + path += len; + if (*path == 0) + return menu; + + return menu_find_path (menu, path + 1); +} + +static GtkWidget * +menu_find (GtkWidget *menu, char *path, char *label) +{ + GtkWidget *item = NULL; + + if (path[0] != 0) + menu = menu_find_path (menu, path); + if (menu) + item = (GtkWidget *)menu_find_item (menu, label); + return item; +} + +static void +menu_foreach_gui (menu_entry *me, void (*callback) (GtkWidget *, menu_entry *, char *)) +{ + GSList *list = sess_list; + int tabdone = FALSE; + session *sess; + + if (!me->is_main) + return; /* not main menu */ + + while (list) + { + sess = list->data; + /* do it only once for tab sessions, since they share a GUI */ + if (!sess->gui->is_tab || !tabdone) + { + callback (sess->gui->menu, me, NULL); + if (sess->gui->is_tab) + tabdone = TRUE; + } + list = list->next; + } +} + +static void +menu_update_cb (GtkWidget *menu, menu_entry *me, char *target) +{ + GtkWidget *item; + + item = menu_find (menu, me->path, me->label); + if (item) + { + gtk_widget_set_sensitive (item, me->enable); + /* must do it without triggering the callback */ + if (GTK_IS_CHECK_MENU_ITEM (item)) + GTK_CHECK_MENU_ITEM (item)->active = me->state; + } +} + +/* radio state changed via mouse click */ +static void +menu_radio_cb (GtkCheckMenuItem *item, menu_entry *me) +{ + me->state = 0; + if (item->active) + me->state = 1; + + /* update the state, incase this was changed via right-click. */ + /* This will update all other windows and menu bars */ + menu_foreach_gui (me, menu_update_cb); + + if (me->state && me->cmd) + handle_command (current_sess, me->cmd, FALSE); +} + +/* toggle state changed via mouse click */ +static void +menu_toggle_cb (GtkCheckMenuItem *item, menu_entry *me) +{ + me->state = 0; + if (item->active) + me->state = 1; + + /* update the state, incase this was changed via right-click. */ + /* This will update all other windows and menu bars */ + menu_foreach_gui (me, menu_update_cb); + + if (me->state) + handle_command (current_sess, me->cmd, FALSE); + else + handle_command (current_sess, me->ucmd, FALSE); +} + +static GtkWidget * +menu_radio_item (char *label, GtkWidget *menu, void *callback, void *userdata, + int state, char *groupname) +{ + GtkWidget *item; + GtkMenuItem *parent; + GSList *grouplist = NULL; + + parent = menu_find_item (menu, groupname); + if (parent) + grouplist = gtk_radio_menu_item_get_group ((GtkRadioMenuItem *)parent); + + item = gtk_radio_menu_item_new_with_label (grouplist, label); + gtk_check_menu_item_set_active ((GtkCheckMenuItem*)item, state); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + g_signal_connect (G_OBJECT (item), "activate", + G_CALLBACK (callback), userdata); + gtk_widget_show (item); + + return item; +} + +static void +menu_reorder (GtkMenu *menu, GtkWidget *item, int pos) +{ + if (pos == 0xffff) /* outbound.c uses this default */ + return; + + if (pos < 0) /* position offset from end/bottom */ + gtk_menu_reorder_child (menu, item, (g_list_length (GTK_MENU_SHELL (menu)->children) + pos) - 1); + else + gtk_menu_reorder_child (menu, item, pos); +} + +static GtkWidget * +menu_add_radio (GtkWidget *menu, menu_entry *me) +{ + GtkWidget *item = NULL; + char *path = me->path + me->root_offset; + + if (path[0] != 0) + menu = menu_find_path (menu, path); + if (menu) + { + item = menu_radio_item (me->label, menu, menu_radio_cb, me, me->state, me->group); + menu_reorder (GTK_MENU (menu), item, me->pos); + } + return item; +} + +static GtkWidget * +menu_add_toggle (GtkWidget *menu, menu_entry *me) +{ + GtkWidget *item = NULL; + char *path = me->path + me->root_offset; + + if (path[0] != 0) + menu = menu_find_path (menu, path); + if (menu) + { + item = menu_toggle_item (me->label, menu, menu_toggle_cb, me, me->state); + menu_reorder (GTK_MENU (menu), item, me->pos); + } + return item; +} + +static GtkWidget * +menu_add_item (GtkWidget *menu, menu_entry *me, char *target) +{ + GtkWidget *item = NULL; + char *path = me->path + me->root_offset; + + if (path[0] != 0) + menu = menu_find_path (menu, path); + if (menu) + { + item = menu_quick_item (me->cmd, me->label, menu, me->markup ? XCMENU_MARKUP|XCMENU_MNEMONIC : XCMENU_MNEMONIC, target, me->icon); + menu_reorder (GTK_MENU (menu), item, me->pos); + } + return item; +} + +static GtkWidget * +menu_add_sub (GtkWidget *menu, menu_entry *me) +{ + GtkWidget *item = NULL; + char *path = me->path + me->root_offset; + int pos; + + if (path[0] != 0) + menu = menu_find_path (menu, path); + if (menu) + { + pos = me->pos; + if (pos < 0) /* position offset from end/bottom */ + pos = g_list_length (GTK_MENU_SHELL (menu)->children) + pos; + menu_quick_sub (me->label, menu, &item, me->markup ? XCMENU_MARKUP|XCMENU_MNEMONIC : XCMENU_MNEMONIC, pos); + } + return item; +} + +static void +menu_del_cb (GtkWidget *menu, menu_entry *me, char *target) +{ + GtkWidget *item = menu_find (menu, me->path + me->root_offset, me->label); + if (item) + gtk_widget_destroy (item); +} + +static void +menu_add_cb (GtkWidget *menu, menu_entry *me, char *target) +{ + GtkWidget *item; + GtkAccelGroup *accel_group; + + if (me->group) /* have a group name? Must be a radio item */ + item = menu_add_radio (menu, me); + else if (me->ucmd) /* have unselect-cmd? Must be a toggle item */ + item = menu_add_toggle (menu, me); + else if (me->cmd || !me->label) /* label=NULL for separators */ + item = menu_add_item (menu, me, target); + else + item = menu_add_sub (menu, me); + + if (item) + { + gtk_widget_set_sensitive (item, me->enable); + if (me->key) + { + accel_group = g_object_get_data (G_OBJECT (menu), "accel"); + if (accel_group) /* popup menus don't have them */ + gtk_widget_add_accelerator (item, "activate", accel_group, me->key, + me->modifier, GTK_ACCEL_VISIBLE); + } + } +} + +char * +fe_menu_add (menu_entry *me) +{ + char *text; + + menu_foreach_gui (me, menu_add_cb); + + if (!me->markup) + return NULL; + + if (!pango_parse_markup (me->label, -1, 0, NULL, &text, NULL, NULL)) + return NULL; + + /* return the label with markup stripped */ + return text; +} + +void +fe_menu_del (menu_entry *me) +{ + menu_foreach_gui (me, menu_del_cb); +} + +void +fe_menu_update (menu_entry *me) +{ + menu_foreach_gui (me, menu_update_cb); +} + +/* used to add custom menus to the right-click menu */ + +static void +menu_add_plugin_mainmenu_items (GtkWidget *menu) +{ + GSList *list; + menu_entry *me; + + list = menu_list; /* outbound.c */ + while (list) + { + me = list->data; + if (me->is_main) + menu_add_cb (menu, me, NULL); + list = list->next; + } +} + +void +menu_add_plugin_items (GtkWidget *menu, char *root, char *target) +{ + GSList *list; + menu_entry *me; + + list = menu_list; /* outbound.c */ + while (list) + { + me = list->data; + if (!me->is_main && !strncmp (me->path, root + 1, root[0])) + menu_add_cb (menu, me, target); + list = list->next; + } +} + +/* === END STUFF FOR /MENU === */ + +GtkWidget * +menu_create_main (void *accel_group, int bar, int away, int toplevel, + GtkWidget **menu_widgets) +{ + int i = 0; + GtkWidget *item; + GtkWidget *menu = 0; + GtkWidget *menu_item = 0; + GtkWidget *menu_bar; + GtkWidget *usermenu = 0; + GtkWidget *submenu = 0; + int close_mask = GDK_CONTROL_MASK; + int away_mask = GDK_MOD1_MASK; + char *key_theme = NULL; + GtkSettings *settings; + GSList *group = NULL; + + if (bar) + menu_bar = gtk_menu_bar_new (); + else + menu_bar = gtk_menu_new (); + + /* /MENU needs to know this later */ + g_object_set_data (G_OBJECT (menu_bar), "accel", accel_group); + +#if GTK_CHECK_VERSION(2,4,0) + g_signal_connect (G_OBJECT (menu_bar), "can-activate-accel", + G_CALLBACK (menu_canacaccel), 0); +#endif + + /* set the initial state of toggles */ + mymenu[MENUBAR_OFFSET].state = !prefs.hidemenu; + mymenu[MENUBAR_OFFSET+1].state = prefs.topicbar; + mymenu[MENUBAR_OFFSET+2].state = !prefs.hideuserlist; + mymenu[MENUBAR_OFFSET+3].state = prefs.userlistbuttons; + mymenu[MENUBAR_OFFSET+4].state = prefs.chanmodebuttons; + + mymenu[AWAY_OFFSET].state = away; + + switch (prefs.tab_layout) + { + case 0: + mymenu[TABS_OFFSET].state = 1; + mymenu[TABS_OFFSET+1].state = 0; + break; + default: + mymenu[TABS_OFFSET].state = 0; + mymenu[TABS_OFFSET+1].state = 1; + } + + mymenu[METRE_OFFSET].state = 0; + mymenu[METRE_OFFSET+1].state = 0; + mymenu[METRE_OFFSET+2].state = 0; + mymenu[METRE_OFFSET+3].state = 0; + switch (prefs.lagometer) + { + case 0: + mymenu[METRE_OFFSET].state = 1; + break; + case 1: + mymenu[METRE_OFFSET+1].state = 1; + break; + case 2: + mymenu[METRE_OFFSET+2].state = 1; + break; + default: + mymenu[METRE_OFFSET+3].state = 1; + } + + /* change Close binding to ctrl-shift-w when using emacs keys */ + settings = gtk_widget_get_settings (menu_bar); + if (settings) + { + g_object_get (settings, "gtk-key-theme-name", &key_theme, NULL); + if (key_theme) + { + if (!strcasecmp (key_theme, "Emacs")) + { + close_mask = GDK_SHIFT_MASK | GDK_CONTROL_MASK; + mymenu[SEARCH_OFFSET].key = 0; + } + g_free (key_theme); + } + } + + /* Away binding to ctrl-alt-a if the _Help menu conflicts (FR/PT/IT) */ + { + char *help = _("_Help"); + char *under = strchr (help, '_'); + if (under && (under[1] == 'a' || under[1] == 'A')) + away_mask = GDK_MOD1_MASK | GDK_CONTROL_MASK; + } + + if (!toplevel) + { + mymenu[DETACH_OFFSET].text = N_("_Detach"); + mymenu[CLOSE_OFFSET].text = N_("_Close"); + } + else + { + mymenu[DETACH_OFFSET].text = N_("_Attach"); + mymenu[CLOSE_OFFSET].text = N_("_Close"); + } + + while (1) + { + item = NULL; + if (mymenu[i].id == MENU_ID_USERMENU && !prefs.gui_usermenu) + { + i++; + continue; + } + + switch (mymenu[i].type) + { + case M_NEWMENU: + if (menu) + gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu_item), menu); + item = menu = gtk_menu_new (); + if (mymenu[i].id == MENU_ID_USERMENU) + usermenu = menu; + menu_item = gtk_menu_item_new_with_mnemonic (_(mymenu[i].text)); + /* record the English name for /menu */ + g_object_set_data (G_OBJECT (menu_item), "name", mymenu[i].text); + gtk_menu_shell_append (GTK_MENU_SHELL (menu_bar), menu_item); + gtk_widget_show (menu_item); + break; + + case M_MENUPIX: + item = create_icon_menu (_(mymenu[i].text), mymenu[i].image, FALSE); + goto normalitem; + + case M_MENUSTOCK: + item = create_icon_menu (_(mymenu[i].text), mymenu[i].image, TRUE); + goto normalitem; + + case M_MENUITEM: + item = gtk_menu_item_new_with_mnemonic (_(mymenu[i].text)); +normalitem: + if (mymenu[i].key != 0) + gtk_widget_add_accelerator (item, "activate", accel_group, + mymenu[i].key, + mymenu[i].key == GDK_F1 ? 0 : + mymenu[i].key == GDK_w ? close_mask : + GDK_CONTROL_MASK, + GTK_ACCEL_VISIBLE); + if (mymenu[i].callback) + g_signal_connect (G_OBJECT (item), "activate", + G_CALLBACK (mymenu[i].callback), 0); + if (submenu) + gtk_menu_shell_append (GTK_MENU_SHELL (submenu), item); + else + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + gtk_widget_show (item); + break; + + case M_MENUTOG: + item = gtk_check_menu_item_new_with_mnemonic (_(mymenu[i].text)); +togitem: + /* must avoid callback for Radio buttons */ + GTK_CHECK_MENU_ITEM (item)->active = mymenu[i].state; + /*gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), + mymenu[i].state);*/ + if (mymenu[i].key != 0) + gtk_widget_add_accelerator (item, "activate", accel_group, + mymenu[i].key, mymenu[i].id == MENU_ID_AWAY ? + away_mask : GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE); + if (mymenu[i].callback) + g_signal_connect (G_OBJECT (item), "toggled", + G_CALLBACK (mymenu[i].callback), 0); + if (submenu) + gtk_menu_shell_append (GTK_MENU_SHELL (submenu), item); + else + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + gtk_widget_show (item); + gtk_widget_set_sensitive (item, mymenu[i].sensitive); + break; + + case M_MENURADIO: + item = gtk_radio_menu_item_new_with_mnemonic (group, _(mymenu[i].text)); + group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (item)); + goto togitem; + + case M_SEP: + item = gtk_menu_item_new (); + gtk_widget_set_sensitive (item, FALSE); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + gtk_widget_show (item); + break; + + case M_MENUSUB: + group = NULL; + submenu = gtk_menu_new (); + item = create_icon_menu (_(mymenu[i].text), mymenu[i].image, TRUE); + /* record the English name for /menu */ + g_object_set_data (G_OBJECT (item), "name", mymenu[i].text); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + gtk_widget_show (item); + break; + + /*case M_END:*/ default: + if (!submenu) + { + if (menu) + { + gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu_item), menu); + menu_add_plugin_mainmenu_items (menu_bar); + } + if (usermenu) + usermenu_create (usermenu); + return (menu_bar); + } + submenu = NULL; + } + + /* record this GtkWidget * so it's state might be changed later */ + if (mymenu[i].id != 0 && menu_widgets) + /* this ends up in sess->gui->menu_item[MENU_ID_XXX] */ + menu_widgets[mymenu[i].id] = item; + + i++; + } +} |