grim/guifications2

Initial support for building the installer
draft default tip
2021-05-19, Gary Kramlich
e60a596742b7
Initial support for building the installer
/*
* Guifications - The end all, be all, toaster popup plugin
* Copyright (C) 2003-2008 Gary Kramlich
*
* 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 Street, Fifth Floor, Boston, MA 02111-1301, USA.
*/
#include <glib.h>
#include <gtk/gtk.h>
#ifdef HAVE_CONFIG_H
# include "../gf_config.h"
#endif
#include "gf_internal.h"
#include <account.h>
#include <blist.h>
#include <debug.h>
#include <gtkblist.h>
#include <gtkconv.h>
#include <gtkdialogs.h>
#include <gtklog.h>
#include <gtkpounce.h>
#include <gtkutils.h>
#include <pidginstock.h>
#include <plugin.h>
#include <version.h>
#ifdef HAVE_CONFIG_H
# include "../gf_config.h"
#endif
#include "gf_action.h"
#include "gf_display.h"
#include "gf_event.h"
#include "gf_event_info.h"
#include "gf_notification.h"
#include "gf_preferences.h"
#include "gf_utils.h"
struct _GfAction {
gchar *name;
gchar *i18n;
GfActionFunc func;
};
static GList *actions = NULL;
/*******************************************************************************
* API
******************************************************************************/
GfAction *
gf_action_new() {
GfAction *action;
action = g_new0(GfAction, 1);
return action;
}
void
gf_action_destroy(GfAction *action) {
g_return_if_fail(action);
g_free(action->name);
g_free(action->i18n);
g_free(action);
action = NULL;
}
void
gf_action_set_name(GfAction *action, const gchar *name) {
g_return_if_fail(action);
g_return_if_fail(name);
if(action->name)
g_free(action->name);
action->name = g_strdup(name);
}
const gchar *
gf_action_get_name(GfAction *action) {
g_return_val_if_fail(action, NULL);
return action->name;
}
void
gf_action_set_i18n(GfAction *action, const gchar *i18n) {
g_return_if_fail(action);
g_return_if_fail(i18n);
if(action->i18n)
g_free(action->i18n);
action->i18n = g_strdup(i18n);
}
const gchar *
gf_action_get_i18n(GfAction *action) {
g_return_val_if_fail(action, NULL);
return action->i18n;
}
void
gf_action_set_func(GfAction *action, GfActionFunc func) {
g_return_if_fail(action);
g_return_if_fail(func);
action->func = func;
}
GfActionFunc
gf_action_get_func(GfAction *action) {
g_return_val_if_fail(action, NULL);
return action->func;
}
void
gf_action_execute(GfAction *action, GfDisplay *display, GdkEventButton *event) {
g_return_if_fail(action);
g_return_if_fail(display);
action->func(display, event);
}
GfAction *
gf_action_find_with_name(const gchar *name) {
GfAction *action;
GList *l;
g_return_val_if_fail(name, NULL);
for(l = actions; l; l = l->next) {
action = GF_ACTION(l->data);
if(!g_ascii_strcasecmp(name, action->name))
return action;
}
return NULL;
}
GfAction *
gf_action_find_with_i18n(const gchar *i18n) {
GfAction *action;
GList *l;
g_return_val_if_fail(i18n, NULL);
for(l = actions; l; l = l->next) {
action = GF_ACTION(l->data);
if(!g_ascii_strcasecmp(i18n, action->i18n))
return action;
}
return NULL;
}
gint
gf_action_get_position(GfAction *action) {
g_return_val_if_fail(action, -1);
return g_list_index(actions, action);
}
/*******************************************************************************
* Sub System
******************************************************************************/
static void
gf_action_add_default(const gchar *name, const gchar *i18n, GfActionFunc func) {
GfAction *action;
g_return_if_fail(name);
g_return_if_fail(func);
action = gf_action_new();
gf_action_set_name(action, name);
gf_action_set_i18n(action, i18n);
gf_action_set_func(action, func);
gf_actions_add_action(action);
}
void
gf_actions_init() {
gf_action_add_default("close", _("Close"), gf_action_execute_close);
gf_action_add_default("open", _("Open Conversation"),
gf_action_execute_open_conv);
gf_action_add_default("context", _("Context Menu"),
gf_action_execute_context);
gf_action_add_default("info", _("Get Info"), gf_action_execute_info);
gf_action_add_default("log", _("Display Log"), gf_action_execute_log);
}
void
gf_actions_uninit() {
GList *l, *ll;
for(l = actions; l; l = ll) {
ll = l->next;
GfAction *action = GF_ACTION(l->data);
gf_actions_remove_action(GF_ACTION(l->data));
gf_action_destroy(action);
}
g_list_free(actions);
actions = NULL;
}
void
gf_actions_add_action(GfAction *action) {
g_return_if_fail(action);
actions = g_list_append(actions, action);
}
void
gf_actions_remove_action(GfAction *action) {
g_return_if_fail(action);
actions = g_list_remove(actions, action);
}
gint
gf_actions_count() {
return g_list_length(actions);
}
const gchar *
gf_actions_get_nth_name(gint nth) {
GfAction *action;
action = GF_ACTION(g_list_nth_data(actions, nth));
return action->name;
}
const gchar *
gf_actions_get_nth_i18n(gint nth) {
GfAction *action;
action = GF_ACTION(g_list_nth_data(actions, nth));
return action->i18n;
}
/*******************************************************************************
* Action Functions
******************************************************************************/
void
gf_action_execute_close(GfDisplay *display, GdkEventButton *gdk_event) {
g_return_if_fail(display);
gf_display_destroy(display);
}
static gboolean
conversation_exists(PurpleConversation *conv) {
PurpleConversation *lconv;
GList *l;
for(l = purple_get_conversations(); l; l = l->next) {
lconv = (PurpleConversation *)l->data;
if(conv == lconv)
return TRUE;
}
return FALSE;
}
void
gf_action_execute_open_conv(GfDisplay *display, GdkEventButton *gdk_event) {
GfEventInfo *info;
PurpleAccount *account = NULL;
PurpleBuddy *buddy = NULL;
PurpleConversation *conv = NULL;
const GHashTable *components = NULL;
const gchar *target;
g_return_if_fail(display);
info = gf_display_get_event_info(display);
account = gf_event_info_get_account(info);
buddy = gf_event_info_get_buddy(info);
conv = gf_event_info_get_conversation(info);
components = gf_event_info_get_components(info);
target = gf_event_info_get_target(info);
if(conv) { /* we have a conv */
if(!conversation_exists(conv)) {
/* the conv we have doesn't exist anymore.. */
const gchar *target;
target = gf_event_info_get_target(info);
conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY, target, account);
}
} else if(components) { /* it's a chat invite */
const gchar *extra = gf_event_info_get_extra(info);
conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, extra, account);
if(!conv) {
serv_join_chat(account->gc, (GHashTable *)components);
gf_display_destroy(display);
return;
}
} else if (buddy) { /* we have a buddy or a target */
conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, buddy->name, account);
if(!conv)
conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, buddy->name);
} else { /* the only thing that _should_ be left is warnings.. */
if(target) {
conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, target, account);
if(!conv)
conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, target);
}
}
if(conv) {
purple_conversation_present(conv);
gf_display_destroy(display);
}
}
void
gf_action_execute_info(GfDisplay *display, GdkEventButton *gdk_event) {
GfEventInfo *info;
const gchar *target;
PurpleAccount *account;
g_return_if_fail(display);
info = gf_display_get_event_info(display);
account = gf_event_info_get_account(info);
target = gf_event_info_get_target(info);
/* This covers everything.
*
* We used to make a distinction between event types, but since we store
* the target for every event, and how you can't get info about a chat
* we just use the target. The target for a buddy event is the
* buddy->name, in a warning event it's the screen name of the person
* who warned you, and in a chat it's the person that said something.
*/
if(target) {
serv_get_info(account->gc, target);
gf_display_destroy(display);
}
}
void
gf_action_execute_log(GfDisplay *display, GdkEventButton *gdk_event) {
GfEventInfo *info;
PurpleAccount *account;
PurpleConversation *conv;
const gchar *target;
g_return_if_fail(display);
info = gf_display_get_event_info(display);
account = gf_event_info_get_account(info);
conv = gf_event_info_get_conversation(info);
target = gf_event_info_get_target(info);
if(conv) {
PurpleConversationType type;
type = purple_conversation_get_type(conv);
if(type == PURPLE_CONV_TYPE_IM || type == PURPLE_CONV_TYPE_CHAT) {
if(type == PURPLE_CONV_TYPE_IM) {
pidgin_log_show(type, target, account);
} else {
const gchar *name = purple_conversation_get_name(conv);
pidgin_log_show(type, name, account);
}
gf_display_destroy(display);
}
} else if(target) {
pidgin_log_show(PURPLE_CONV_TYPE_IM, target, account);
gf_display_destroy(display);
}
}
/******************************************************************************
* Context menu stuff
*****************************************************************************/
static gboolean
gf_action_context_destroy_cb(gpointer data) {
GfDisplay *display = GF_DISPLAY(data);
gf_display_destroy(display);
return FALSE;
}
static void
gf_action_context_hide_cb(GtkWidget *w, gpointer data) {
GfDisplay *display = GF_DISPLAY(data);
GfEventInfo *info = NULL;
gint display_time;
guint timeout_id;
g_return_if_fail(display);
info = gf_display_get_event_info(display);
g_return_if_fail(info);
display_time = purple_prefs_get_int(GF_PREF_BEHAVIOR_DISPLAY_TIME);
timeout_id = g_timeout_add(display_time * 500,
gf_action_context_destroy_cb, display);
gf_event_info_set_timeout_id(info, timeout_id);
}
static void
gf_action_context_position(GtkMenu *menu, gint *x, gint *y, gboolean pushin,
gpointer data)
{
GtkRequisition req;
gint scrheight = 0;
scrheight = gdk_screen_get_height(gtk_widget_get_screen(GTK_WIDGET(menu)));
gtk_widget_size_request(GTK_WIDGET(menu), &req);
if((*y + req.height > scrheight) && (scrheight - req.height > 0))
*y = scrheight - req.height;
}
static void
gf_action_context_info_cb(GtkWidget *menuitem, GfDisplay *display) {
gf_action_execute_info(display, NULL);
}
static void
gf_action_context_im_cb(GtkWidget *menuitem, GfDisplay *display) {
GfEventInfo *info;
PurpleAccount *account;
PurpleConversation *conv = NULL;
PidginWindow *win = NULL;
const gchar *target;
info = gf_display_get_event_info(display);
account = gf_event_info_get_account(info);
target = gf_event_info_get_target(info);
conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, target, account);
if(!conv) {
conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, target);
}
if(conv) {
win = PIDGIN_CONVERSATION(conv)->win;
if(!win) {
gf_display_destroy(display);
return;
}
pidgin_conv_window_switch_gtkconv(win, PIDGIN_CONVERSATION(conv));
gtk_window_present(GTK_WINDOW(win->window));
}
gf_display_destroy(display);
}
static void
gf_action_context_pounce_cb(GtkWidget *menuitem, GfDisplay *display) {
GfEventInfo *info;
PurpleAccount *account = NULL;
PurpleBuddy *buddy = NULL;
info = gf_display_get_event_info(display);
g_return_if_fail(info);
account = gf_event_info_get_account(info);
g_return_if_fail(account);
buddy = gf_event_info_get_buddy(info);
g_return_if_fail(buddy);
pidgin_pounce_editor_show(account, buddy->name, NULL);
}
static void
gf_action_context_log_buddy_cb(GtkWidget *menuitem, GfDisplay *display) {
GfEventInfo *info = NULL;
PurpleAccount *account = NULL;
const gchar *target = NULL;
info = gf_display_get_event_info(display);
g_return_if_fail(info);
account = gf_event_info_get_account(info);
g_return_if_fail(account);
target = gf_event_info_get_target(info);
g_return_if_fail(target);
pidgin_log_show(PURPLE_LOG_IM, target, account);
}
static void
gf_action_context_log_chat_cb(GtkWidget *menuitem, GfDisplay *display) {
GfEventInfo *info = NULL;
PurpleAccount *account = NULL;
PurpleConversation *conv = NULL;
const gchar *name = NULL;
info = gf_display_get_event_info(display);
g_return_if_fail(info);
account = gf_event_info_get_account(info);
g_return_if_fail(account);
conv = gf_event_info_get_conversation(info);
g_return_if_fail(conv);
name = purple_conversation_get_name(conv);
pidgin_log_show(PURPLE_LOG_CHAT, name, account);
}
static void
gf_action_context_alias_buddy_cb(GtkWidget *menuitem, GfDisplay *display) {
GfEventInfo *info = NULL;
PurpleBuddy *buddy = NULL;
info = gf_display_get_event_info(display);
g_return_if_fail(info);
buddy = gf_event_info_get_buddy(info);
g_return_if_fail(buddy);
pidgin_dialogs_alias_buddy(buddy);
}
static void
gf_action_context_alias_chat_cb(GtkWidget *menuitem, GfDisplay *display) {
GfEventInfo *info = NULL;
PurpleAccount *account = NULL;
PurpleChat *chat = NULL;
PurpleConversation *conv = NULL;
const gchar *name = NULL;
info = gf_display_get_event_info(display);
g_return_if_fail(info);
account = gf_event_info_get_account(info);
g_return_if_fail(account);
conv = gf_event_info_get_conversation(info);
g_return_if_fail(conv);
name = purple_conversation_get_name(conv);
chat = purple_blist_find_chat(account, name);
g_return_if_fail(chat);
pidgin_dialogs_alias_chat(chat);
}
static void
gf_action_context_add_buddy_cb(GtkWidget *menuitem, GfDisplay *display) {
GfEventInfo *info = NULL;
PurpleAccount *account = NULL;
const gchar *target = NULL;
info = gf_display_get_event_info(display);
g_return_if_fail(info);
account = gf_event_info_get_account(info);
g_return_if_fail(account);
target = gf_event_info_get_target(info);
g_return_if_fail(target);
purple_blist_request_add_buddy(account, target, NULL, NULL);
}
static void
gf_action_context_add_chat_cb(GtkWidget *menuitem, GfDisplay *display) {
GfEventInfo *info = NULL;
PurpleAccount *account = NULL;
PurpleConversation *conv = NULL;
const gchar *name = NULL;
info = gf_display_get_event_info(display);
g_return_if_fail(info);
account = gf_event_info_get_account(info);
g_return_if_fail(account);
conv = gf_event_info_get_conversation(info);
g_return_if_fail(conv);
name = purple_conversation_get_name(conv);
purple_blist_request_add_chat(account, NULL, NULL, name);
}
static void
gf_action_context_remove_buddy_cb(GtkWidget *menuitem, GfDisplay *display) {
GfEventInfo *info = NULL;
PurpleBuddy *buddy = NULL;
info = gf_display_get_event_info(display);
g_return_if_fail(info);
buddy = gf_event_info_get_buddy(info);
g_return_if_fail(buddy);
pidgin_dialogs_remove_buddy(buddy);
}
static void
gf_action_context_remove_chat_cb(GtkWidget *menuitem, GfDisplay *display) {
GfEventInfo *info = NULL;
PurpleAccount *account = NULL;
PurpleChat *chat = NULL;
PurpleConversation *conv = NULL;
const gchar *name = NULL;
info = gf_display_get_event_info(display);
g_return_if_fail(info);
account = gf_event_info_get_account(info);
g_return_if_fail(account);
conv = gf_event_info_get_conversation(info);
g_return_if_fail(conv);
name = purple_conversation_get_name(conv);
chat = purple_blist_find_chat(account, name);
g_return_if_fail(chat);
pidgin_dialogs_remove_chat(chat);
}
static void
gf_action_context_join_cb(GtkWidget *menuitem, GfDisplay *display) {
GfEventInfo *info = NULL;
PurpleAccount *account = NULL;
const GHashTable *components = NULL;
info = gf_display_get_event_info(display);
g_return_if_fail(info);
account = gf_event_info_get_account(info);
g_return_if_fail(account);
components = gf_event_info_get_components(info);
g_return_if_fail(components);
serv_join_chat(account->gc, (GHashTable *)components);
}
static void
gf_action_context_autojoin_cb(GtkWidget *menuitem, GfDisplay *display) {
GfEventInfo *info = NULL;
PurpleAccount *account = NULL;
PurpleChat *chat = NULL;
PurpleConversation *conv = NULL;
const gchar *name = NULL;
info = gf_display_get_event_info(display);
g_return_if_fail(info);
account = gf_event_info_get_account(info);
g_return_if_fail(account);
conv = gf_event_info_get_conversation(info);
g_return_if_fail(conv);
name = purple_conversation_get_name(conv);
chat = purple_blist_find_chat(account, name);
g_return_if_fail(chat);
purple_blist_node_set_bool((PurpleBlistNode *)chat, "gtk-autojoin",
gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menuitem)));
}
/* This function has come from hell.. I summoned it last time I sent a wicked
* soul to it's eternal resting place.
*/
void
gf_action_execute_context(GfDisplay *display, GdkEventButton *gdk_event) {
GfEventInfo *info = NULL;
PurpleAccount *account = NULL;
PurpleBuddy *buddy = NULL;
PurpleChat *chat = NULL;
PurpleConversation *conv = NULL;
PurpleConversationType type = 0;
PurplePlugin *prpl = NULL;
PurplePluginProtocolInfo *prpl_info = NULL;
GtkWidget *menu;
const gchar *target = NULL, *name = NULL;
gboolean chat_sep_added = FALSE;
guint timeout_id;
g_return_if_fail(display);
/* grab the stuff we need from the display and event info */
info = gf_display_get_event_info(display);
g_return_if_fail(info);
/* get the pidgin stuff we need */
account = gf_event_info_get_account(info);
g_return_if_fail(account);
/* we're going to show the menu as long as the timeout is removed.
* We need to remove it otherwise the display get's destroyed when
* the menu is shown, or it'll leak if we don't clean it up later.
*/
timeout_id = gf_event_info_get_timeout_id(info);
g_return_if_fail(g_source_remove(timeout_id));
buddy = gf_event_info_get_buddy(info);
conv = gf_event_info_get_conversation(info);
target = gf_event_info_get_target(info);
if(conv) {
type = purple_conversation_get_type(conv);
name = purple_conversation_get_name(conv);
}
if(conv)
chat = purple_blist_find_chat(account, name);
/* find the prpl and it's info */
prpl = purple_find_prpl(purple_account_get_protocol_id(account));
if(prpl)
prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
/* create the menu */
menu = gtk_menu_new();
g_signal_connect(G_OBJECT(menu), "hide",
G_CALLBACK(gf_action_context_hide_cb), display);
gtk_widget_show(menu);
if(buddy || target) {
/* add get info if the prpl supports it */
if(prpl_info && prpl_info->get_info) {
pidgin_new_item_from_stock(menu, _("Get Info"), PIDGIN_STOCK_DIALOG_INFO,
G_CALLBACK(gf_action_context_info_cb),
display, 0, 0, NULL);
}
/* we can im anyone.. so.. */
pidgin_new_item_from_stock(menu, _("IM"), PIDGIN_STOCK_TOOLBAR_MESSAGE_NEW,
G_CALLBACK(gf_action_context_im_cb),
display, 0, 0, NULL);
}
/* It's pointless to add a buddy pounce for someone that isn't a buddy...
* Conversations currently allow this.. but that's because I haven't
* written a patch yet..
*/
if(buddy) {
pidgin_new_item_from_stock(menu, _("Add Buddy Pounce"), NULL,
G_CALLBACK(gf_action_context_pounce_cb),
display, 0, 0, NULL);
}
if(!buddy && target)
buddy = purple_find_buddy(account, target);
if(buddy) {
pidgin_new_item_from_stock(menu, _("View IM log"), NULL,
G_CALLBACK(gf_action_context_log_buddy_cb),
display, 0, 0, NULL);
/* add the protocol menu */
pidgin_append_blist_node_proto_menu(menu, account->gc,
(PurpleBlistNode *)buddy);
/* and now the extended menu */
pidgin_append_blist_node_extended_menu(menu, (PurpleBlistNode*)buddy);
/* separate it! */
pidgin_separator(menu);
}
/* we can't alias a non buddy, and we can't remove them either.. But if
* they're not a buddy we can add them!
*/
if(buddy) {
/* add the alias and remove button */
pidgin_new_item_from_stock(menu, _("Alias Buddy"), PIDGIN_STOCK_ALIAS,
G_CALLBACK(gf_action_context_alias_buddy_cb),
display, 0, 0, NULL);
pidgin_new_item_from_stock(menu, _("Remove Buddy"), GTK_STOCK_REMOVE,
G_CALLBACK(gf_action_context_remove_buddy_cb),
display, 0, 0, NULL);
} else if(target) {
pidgin_new_item_from_stock(menu, _("Add Buddy"), GTK_STOCK_ADD,
G_CALLBACK(gf_action_context_add_buddy_cb),
display, 0, 0, NULL);
}
/* Add a separtor if we have a buddy or target, and we have a chat */
if((buddy || target) && chat) {
pidgin_separator(menu);
chat_sep_added = TRUE;
}
if(chat) {
PurpleBlistNode *node = (PurpleBlistNode *)chat;
gboolean checked;
checked = (purple_blist_node_get_bool(node, "gtk-autojoin") ||
(purple_blist_node_get_string(node, "gtk-autojoin") != NULL));
/* add the join item, possibly redundant.. */
pidgin_new_item_from_stock(menu, _("Join"), PIDGIN_STOCK_CHAT,
G_CALLBACK(gf_action_context_join_cb),
display, 0, 0, NULL);
pidgin_new_check_item(menu, _("Auto-join"),
G_CALLBACK(gf_action_context_autojoin_cb),
display, checked);
}
/* if we have a conversation and it's a chat, we show the view log for it.
* if we don't do this check we end up with two menu items to view the
* same log..
*/
if(conv && type == PURPLE_CONV_TYPE_CHAT) {
if(!chat_sep_added) {
pidgin_separator(menu);
}
pidgin_new_item_from_stock(menu, _("View Chat Log"), NULL,
G_CALLBACK(gf_action_context_log_chat_cb),
display, 0, 0, NULL);
}
if(chat) {
/* add the proto menu for the chat */
pidgin_append_blist_node_proto_menu(menu, account->gc,
(PurpleBlistNode *)chat);
/* add the extended menu for the chat */
pidgin_append_blist_node_extended_menu(menu, (PurpleBlistNode *)chat);
/* add the alias and remove buttons */
pidgin_new_item_from_stock(menu, _("Alias Chat"), PIDGIN_STOCK_ALIAS,
G_CALLBACK(gf_action_context_alias_chat_cb),
display, 0, 0, NULL);
pidgin_new_item_from_stock(menu, _("Remove Chat"), GTK_STOCK_REMOVE,
G_CALLBACK(gf_action_context_remove_chat_cb),
display, 0, 0, NULL);
}
/* add the add chat item if it's not on our blist.. */
if(!chat && conv && type == PURPLE_CONV_TYPE_CHAT) {
pidgin_new_item_from_stock(menu, _("Add Chat"), GTK_STOCK_ADD,
G_CALLBACK(gf_action_context_add_chat_cb),
display, 0, 0, NULL);
}
/* show it already */
gtk_widget_show_all(menu);
gtk_menu_popup(GTK_MENU(menu), NULL, NULL,
(GtkMenuPositionFunc)gf_action_context_position, display,
gdk_event->button, gdk_event->time);
}