* Integration with Unity's messaging menu and launcher * Copyright (C) 2013 Ankit Vani <a@nevitus.org> * 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 "savedstatuses.h" #include <messaging-menu.h> #define UNITY_PLUGIN_ID "gtk-unity-integration" static MessagingMenuApp *mmapp = NULL; static UnityLauncherEntry *launcher = NULL; static guint n_sources = 0; static gint launcher_count; static gint messaging_menu_text; static gboolean alert_chat_nick = TRUE; static int attach_signals(PurpleConversation *conv); static void detach_signals(PurpleConversation *conv); g_return_if_fail(launcher != NULL); if (launcher_count == LAUNCHER_COUNT_DISABLE) if (launcher_count == LAUNCHER_COUNT_MESSAGES) { for (convs = purple_conversations_get_all(); convs != NULL; convs = convs->next) { PurpleConversation *conv = convs->data; count += GPOINTER_TO_INT(g_object_get_data(G_OBJECT(conv), unity_launcher_entry_set_count_visible(launcher, TRUE); unity_launcher_entry_set_count_visible(launcher, FALSE); unity_launcher_entry_set_count(launcher, count); conversation_id(PurpleConversation *conv) PurpleAccount *account = purple_conversation_get_account(conv); return g_strconcat((PURPLE_IS_IM_CONVERSATION(conv) ? "im" : PURPLE_IS_CHAT_CONVERSATION(conv) ? "chat" : "misc"), ":", purple_conversation_get_name(conv), ":", purple_account_get_username(account), ":", purple_account_get_protocol_id(account), NULL); messaging_menu_add_conversation(PurpleConversation *conv, gint count) g_return_if_fail(count > 0); id = conversation_id(conv); /* GBytesIcon may be useful for messaging menu source icons using buddy if (!messaging_menu_app_has_source(mmapp, id)) messaging_menu_app_append_source(mmapp, id, NULL, purple_conversation_get_title(conv)); if (messaging_menu_text == MESSAGING_MENU_TIME) messaging_menu_app_set_source_time(mmapp, id, g_get_real_time()); else if (messaging_menu_text == MESSAGING_MENU_COUNT) messaging_menu_app_set_source_count(mmapp, id, count); messaging_menu_app_draw_attention(mmapp, id); messaging_menu_remove_conversation(PurpleConversation *conv) gchar *id = conversation_id(conv); if (messaging_menu_app_has_source(mmapp, id)) messaging_menu_app_remove_source(mmapp, id); for (convs = purple_conversations_get_all(); convs != NULL; convs = convs->next) { PurpleConversation *conv = convs->data; messaging_menu_add_conversation(conv, GPOINTER_TO_INT(g_object_get_data(G_OBJECT(conv), "unity-message-count"))); alert(PurpleConversation *conv) PidginConvWindow *purplewin = NULL; if (conv == NULL || PIDGIN_CONVERSATION(conv) == NULL) purplewin = PIDGIN_CONVERSATION(conv)->win; if (!pidgin_conv_window_has_focus(purplewin) || !pidgin_conv_window_is_active_conversation(conv)) count = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(conv), g_object_set_data(G_OBJECT(conv), "unity-message-count", messaging_menu_add_conversation(conv, count); unalert(PurpleConversation *conv) if (GPOINTER_TO_INT(g_object_get_data(G_OBJECT(conv), "unity-message-count")) > 0) g_object_set_data(G_OBJECT(conv), "unity-message-count", messaging_menu_remove_conversation(conv); unalert_cb(GtkWidget *widget, gpointer data, PurpleConversation *conv) message_displayed_cb(PurpleConversation *conv, PurpleMessage *msg, gpointer _unused) PurpleMessageFlags flags = purple_message_get_flags(msg); if ((PURPLE_IS_CHAT_CONVERSATION(conv) && alert_chat_nick && !(flags & PURPLE_MESSAGE_NICK))) if ((flags & PURPLE_MESSAGE_RECV) && !(flags & PURPLE_MESSAGE_DELAYED)) im_sent_im(PurpleAccount *account, PurpleMessage *msg, gpointer _unused) PurpleIMConversation *im = NULL; im = purple_conversations_find_im_with_account( purple_message_get_recipient(msg), account); unalert(PURPLE_CONVERSATION(im)); chat_sent_im(PurpleAccount *account, PurpleMessage *msg, int id) PurpleChatConversation *chat = NULL; chat = purple_conversations_find_chat(purple_account_get_connection(account), id); unalert(PURPLE_CONVERSATION(chat)); conv_created(PurpleConversation *conv) g_object_set_data(G_OBJECT(conv), "unity-message-count", deleting_conv(PurpleConversation *conv) message_source_activated(MessagingMenuApp *app, const gchar *id, gchar **sections = g_strsplit(id, ":", 0); PurpleConversation *conv = NULL; PidginConvWindow *purplewin = NULL; char *type = sections[0]; char *cname = sections[1]; char *aname = sections[2]; char *protocol = sections[3]; account = purple_accounts_find(aname, protocol); if (g_strcmp0(type, "im") == 0) conv = PURPLE_CONVERSATION(purple_conversations_find_im_with_account(cname, account)); else if (g_strcmp0(type, "chat") == 0) conv = PURPLE_CONVERSATION(purple_conversations_find_chat_with_account(cname, account)); conv = purple_conversations_find_with_account(cname, account); purplewin = PIDGIN_CONVERSATION(conv)->win; pidgin_conv_window_switch_gtkconv(purplewin, PIDGIN_CONVERSATION(conv)); gdk_window_focus(gtk_widget_get_window(purplewin->window), time(NULL)); static PurpleSavedStatus * create_transient_status(PurpleStatusPrimitive primitive, PurpleStatusType *status_type) PurpleSavedStatus *saved_status = purple_savedstatus_new(NULL, primitive); if(status_type != NULL) { GList *tmp, *active_accts = purple_accounts_get_all_active(); for (tmp = active_accts; tmp != NULL; tmp = tmp->next) { purple_savedstatus_set_substatus(saved_status, (PurpleAccount*) tmp->data, status_type, NULL); g_list_free(active_accts); status_changed_cb(PurpleSavedStatus *saved_status) MessagingMenuStatus status = MESSAGING_MENU_STATUS_AVAILABLE; switch (purple_savedstatus_get_primitive_type(saved_status)) { case PURPLE_STATUS_AVAILABLE: case PURPLE_STATUS_UNSET: status = MESSAGING_MENU_STATUS_AVAILABLE; case PURPLE_STATUS_EXTENDED_AWAY: status = MESSAGING_MENU_STATUS_AWAY; case PURPLE_STATUS_INVISIBLE: status = MESSAGING_MENU_STATUS_INVISIBLE; case PURPLE_STATUS_MOBILE: case PURPLE_STATUS_OFFLINE: status = MESSAGING_MENU_STATUS_OFFLINE; case PURPLE_STATUS_UNAVAILABLE: status = MESSAGING_MENU_STATUS_BUSY; messaging_menu_app_set_status(mmapp, status); messaging_menu_status_changed(MessagingMenuApp *mmapp, MessagingMenuStatus mm_status, gpointer user_data) PurpleSavedStatus *saved_status; PurpleStatusPrimitive primitive = PURPLE_STATUS_UNSET; case MESSAGING_MENU_STATUS_AVAILABLE: primitive = PURPLE_STATUS_AVAILABLE; case MESSAGING_MENU_STATUS_AWAY: primitive = PURPLE_STATUS_AWAY; case MESSAGING_MENU_STATUS_BUSY: primitive = PURPLE_STATUS_UNAVAILABLE; case MESSAGING_MENU_STATUS_INVISIBLE: primitive = PURPLE_STATUS_INVISIBLE; case MESSAGING_MENU_STATUS_OFFLINE: primitive = PURPLE_STATUS_OFFLINE; saved_status = purple_savedstatus_find_transient_by_type_and_message(primitive, NULL); if (saved_status == NULL) saved_status = create_transient_status(primitive, NULL); purple_savedstatus_activate(saved_status); alert_config_cb(GtkWidget *widget, gpointer data) gboolean on = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)); purple_prefs_set_bool("/plugins/gtk/unity/alert_chat_nick", on); launcher_config_cb(GtkWidget *widget, gpointer data) gint option = GPOINTER_TO_INT(data); if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) purple_prefs_set_int("/plugins/gtk/unity/launcher_count", option); if (option == LAUNCHER_COUNT_DISABLE) unity_launcher_entry_set_count_visible(launcher, FALSE); messaging_menu_config_cb(GtkWidget *widget, gpointer data) gint option = GPOINTER_TO_INT(data); if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) purple_prefs_set_int("/plugins/gtk/unity/messaging_menu_text", option); messaging_menu_text = option; attach_signals(PurpleConversation *conv) PidginConversation *gtkconv = NULL; gtkconv = PIDGIN_CONVERSATION(conv); id = g_signal_connect(G_OBJECT(gtkconv->entry), "focus-in-event", G_CALLBACK(unalert_cb), conv); g_object_set_data(G_OBJECT(conv), "unity-entry-signal", GUINT_TO_POINTER(id)); id = g_signal_connect(G_OBJECT(gtkconv->webview), "focus-in-event", G_CALLBACK(unalert_cb), conv); g_object_set_data(G_OBJECT(conv), "unity-webview-signal", GUINT_TO_POINTER(id)); detach_signals(PurpleConversation *conv) PidginConversation *gtkconv = NULL; gtkconv = PIDGIN_CONVERSATION(conv); id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(conv), "unity-webview-signal")); g_signal_handler_disconnect(gtkconv->webview, id); id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(conv), "unity-entry-signal")); g_signal_handler_disconnect(gtkconv->entry, id); g_object_set_data(G_OBJECT(conv), "unity-message-count", get_config_frame(PurplePlugin *plugin) GtkWidget *ret = NULL, *frame = NULL; GtkWidget *vbox = NULL, *toggle = NULL; ret = gtk_box_new(GTK_ORIENTATION_VERTICAL, 18); gtk_container_set_border_width(GTK_CONTAINER (ret), 12); frame = pidgin_make_frame(ret, _("Chatroom alerts")); vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5); gtk_container_add(GTK_CONTAINER(frame), vbox); toggle = gtk_check_button_new_with_mnemonic(_("Chatroom message alerts _only where someone says your username")); gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle), purple_prefs_get_bool("/plugins/gtk/unity/alert_chat_nick")); g_signal_connect(G_OBJECT(toggle), "toggled", G_CALLBACK(alert_config_cb), NULL); /* Launcher integration */ frame = pidgin_make_frame(ret, _("Launcher Icon")); vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5); gtk_container_add(GTK_CONTAINER(frame), vbox); toggle = gtk_radio_button_new_with_mnemonic(NULL, _("_Disable launcher integration")); gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle), purple_prefs_get_int("/plugins/gtk/unity/launcher_count") == LAUNCHER_COUNT_DISABLE); g_signal_connect(G_OBJECT(toggle), "toggled", G_CALLBACK(launcher_config_cb), GUINT_TO_POINTER(LAUNCHER_COUNT_DISABLE)); toggle = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(toggle), _("Show number of unread _messages on launcher icon")); gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle), purple_prefs_get_int("/plugins/gtk/unity/launcher_count") == LAUNCHER_COUNT_MESSAGES); g_signal_connect(G_OBJECT(toggle), "toggled", G_CALLBACK(launcher_config_cb), GUINT_TO_POINTER(LAUNCHER_COUNT_MESSAGES)); toggle = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(toggle), _("Show number of unread co_nversations on launcher icon")); gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle), purple_prefs_get_int("/plugins/gtk/unity/launcher_count") == LAUNCHER_COUNT_SOURCES); g_signal_connect(G_OBJECT(toggle), "toggled", G_CALLBACK(launcher_config_cb), GUINT_TO_POINTER(LAUNCHER_COUNT_SOURCES)); /* Messaging menu integration */ frame = pidgin_make_frame(ret, _("Messaging Menu")); vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5); gtk_container_add(GTK_CONTAINER(frame), vbox); toggle = gtk_radio_button_new_with_mnemonic(NULL, _("Show number of _unread messages for conversations in messaging menu")); gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle), purple_prefs_get_int("/plugins/gtk/unity/messaging_menu_text") == MESSAGING_MENU_COUNT); g_signal_connect(G_OBJECT(toggle), "toggled", G_CALLBACK(messaging_menu_config_cb), GUINT_TO_POINTER(MESSAGING_MENU_COUNT)); toggle = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(toggle), _("Show _elapsed time for unread conversations in messaging menu")); gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle), purple_prefs_get_int("/plugins/gtk/unity/messaging_menu_text") == MESSAGING_MENU_TIME); g_signal_connect(G_OBJECT(toggle), "toggled", G_CALLBACK(messaging_menu_config_cb), GUINT_TO_POINTER(MESSAGING_MENU_TIME)); gtk_widget_show_all(ret); static PidginPluginInfo * plugin_query(GError **error) const gchar * const authors[] = { "Ankit Vani <a@nevitus.org>", return pidgin_plugin_info_new( "name", N_("Unity Integration"), "version", DISPLAY_VERSION, "category", N_("Notification"), "summary", N_("Provides integration with Unity."), "description", N_("Provides integration with Unity's " "messaging menu and launcher."), "website", PURPLE_WEBSITE, "abi-version", PURPLE_ABI_VERSION, "gtk-config-frame-cb", get_config_frame, plugin_load(PurplePlugin *plugin, GError **error) GList *convs = purple_conversations_get_all(); PurpleSavedStatus *saved_status; void *conv_handle = purple_conversations_get_handle(); void *gtk_conv_handle = pidgin_conversations_get_handle(); void *savedstat_handle = purple_savedstatuses_get_handle(); purple_prefs_add_none("/plugins/gtk"); purple_prefs_add_none("/plugins/gtk/unity"); purple_prefs_add_int("/plugins/gtk/unity/launcher_count", LAUNCHER_COUNT_SOURCES); purple_prefs_add_int("/plugins/gtk/unity/messaging_menu_text", MESSAGING_MENU_COUNT); purple_prefs_add_bool("/plugins/gtk/unity/alert_chat_nick", TRUE); alert_chat_nick = purple_prefs_get_bool("/plugins/gtk/unity/alert_chat_nick"); mmapp = messaging_menu_app_new("pidgin.desktop"); messaging_menu_app_register(mmapp); messaging_menu_text = purple_prefs_get_int("/plugins/gtk/unity/messaging_menu_text"); g_signal_connect(mmapp, "activate-source", G_CALLBACK(message_source_activated), NULL); g_signal_connect(mmapp, "status-changed", G_CALLBACK(messaging_menu_status_changed), NULL); saved_status = purple_savedstatus_get_current(); status_changed_cb(saved_status); purple_signal_connect(savedstat_handle, "savedstatus-changed", plugin, PURPLE_CALLBACK(status_changed_cb), NULL); launcher = unity_launcher_entry_get_for_desktop_id("pidgin.desktop"); launcher_count = purple_prefs_get_int("/plugins/gtk/unity/launcher_count"); purple_signal_connect(gtk_conv_handle, "displayed-im-msg", plugin, PURPLE_CALLBACK(message_displayed_cb), NULL); purple_signal_connect(gtk_conv_handle, "displayed-chat-msg", plugin, PURPLE_CALLBACK(message_displayed_cb), NULL); purple_signal_connect(conv_handle, "sent-im-msg", plugin, PURPLE_CALLBACK(im_sent_im), NULL); purple_signal_connect(conv_handle, "sent-chat-msg", plugin, PURPLE_CALLBACK(chat_sent_im), NULL); purple_signal_connect(conv_handle, "conversation-created", plugin, PURPLE_CALLBACK(conv_created), NULL); purple_signal_connect(conv_handle, "deleting-conversation", plugin, PURPLE_CALLBACK(deleting_conv), NULL); PurpleConversation *conv = (PurpleConversation *)convs->data; plugin_unload(PurplePlugin *plugin, GError **error) GList *convs = purple_conversations_get_all(); PurpleConversation *conv = (PurpleConversation *)convs->data; unity_launcher_entry_set_count_visible(launcher, FALSE); messaging_menu_app_unregister(mmapp); g_object_unref(launcher); PURPLE_PLUGIN_INIT(unity, plugin_query, plugin_load, plugin_unload);