1 /*
2  * Integration with Unity's messaging menu and launcher
3  * Copyright (C) 2013 Ankit Vani <a@nevitus.org>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
18  */
19 #include "internal.h"
20 #include "version.h"
21 #include "account.h"
22 #include "savedstatuses.h"
23 
24 #include "gtkplugin.h"
25 #include "gtkconv.h"
26 #include "gtkutils.h"
27 
28 #include <unity.h>
29 #include <messaging-menu.h>
30 
31 #define UNITY_PLUGIN_ID "gtk-unity-integration"
32 
33 static MessagingMenuApp *mmapp = NULL;
34 static UnityLauncherEntry *launcher = NULL;
35 static guint n_sources = 0;
36 static gint launcher_count;
37 static gint messaging_menu_text;
38 static gboolean alert_chat_nick = TRUE;
39 
40 enum {
41 	LAUNCHER_COUNT_DISABLE,
42 	LAUNCHER_COUNT_MESSAGES,
43 	LAUNCHER_COUNT_SOURCES,
44 };
45 
46 enum {
47 	MESSAGING_MENU_COUNT,
48 	MESSAGING_MENU_TIME,
49 };
50 
51 static int attach_signals(PurpleConversation *conv);
52 static void detach_signals(PurpleConversation *conv);
53 
54 static void
update_launcher()55 update_launcher()
56 {
57 	guint count = 0;
58 	GList *convs = NULL;
59 	g_return_if_fail(launcher != NULL);
60 	if (launcher_count == LAUNCHER_COUNT_DISABLE)
61 		return;
62 
63 	if (launcher_count == LAUNCHER_COUNT_MESSAGES) {
64 		for (convs = purple_get_conversations(); convs != NULL; convs = convs->next) {
65 			PurpleConversation *conv = convs->data;
66 			count += GPOINTER_TO_INT(purple_conversation_get_data(conv,
67 			                         "unity-message-count"));
68 		}
69 	} else {
70 		count = n_sources;
71 	}
72 
73 	if (launcher != NULL) {
74 		if (count > 0)
75 			unity_launcher_entry_set_count_visible(launcher, TRUE);
76 		else
77 			unity_launcher_entry_set_count_visible(launcher, FALSE);
78 		unity_launcher_entry_set_count(launcher, count);
79 	}
80 }
81 
82 static gchar *
conversation_id(PurpleConversation * conv)83 conversation_id(PurpleConversation *conv)
84 {
85 	PurpleConversationType conv_type = purple_conversation_get_type(conv);
86 	PurpleAccount *account = purple_conversation_get_account(conv);
87 	char type[2] = "0";
88 	type[0] += conv_type;
89 
90 	return g_strconcat(type, ":",
91 	                   purple_conversation_get_name(conv), ":",
92 	                   purple_account_get_username(account), ":",
93 	                   purple_account_get_protocol_id(account), NULL);
94 }
95 
96 static void
messaging_menu_add_conversation(PurpleConversation * conv,gint count)97 messaging_menu_add_conversation(PurpleConversation *conv, gint count)
98 {
99 	gchar *id;
100 	g_return_if_fail(count > 0);
101 	id = conversation_id(conv);
102 
103 	/* GBytesIcon may be useful for messaging menu source icons using buddy
104 	   icon data for IMs */
105 	if (!messaging_menu_app_has_source(mmapp, id))
106 		messaging_menu_app_append_source(mmapp, id, NULL,
107 		                                 purple_conversation_get_title(conv));
108 
109 	if (messaging_menu_text == MESSAGING_MENU_TIME)
110 		messaging_menu_app_set_source_time(mmapp, id, g_get_real_time());
111 	else if (messaging_menu_text == MESSAGING_MENU_COUNT)
112 		messaging_menu_app_set_source_count(mmapp, id, count);
113 	messaging_menu_app_draw_attention(mmapp, id);
114 
115 	g_free(id);
116 }
117 
118 static void
messaging_menu_remove_conversation(PurpleConversation * conv)119 messaging_menu_remove_conversation(PurpleConversation *conv)
120 {
121 	gchar *id = conversation_id(conv);
122 	if (messaging_menu_app_has_source(mmapp, id))
123 		messaging_menu_app_remove_source(mmapp, id);
124 	g_free(id);
125 }
126 
127 static void
refill_messaging_menu()128 refill_messaging_menu()
129 {
130 	GList *convs;
131 
132 	for (convs = purple_get_conversations(); convs != NULL; convs = convs->next) {
133 		PurpleConversation *conv = convs->data;
134 		messaging_menu_add_conversation(conv,
135 			GPOINTER_TO_INT(purple_conversation_get_data(conv, "unity-message-count")));
136 	}
137 }
138 
139 static int
alert(PurpleConversation * conv)140 alert(PurpleConversation *conv)
141 {
142 	gint count;
143 	PidginWindow *purplewin = NULL;
144 	if (conv == NULL || PIDGIN_CONVERSATION(conv) == NULL)
145 		return 0;
146 
147 	purplewin = PIDGIN_CONVERSATION(conv)->win;
148 
149 	if (!pidgin_conv_window_has_focus(purplewin) ||
150 		!pidgin_conv_window_is_active_conversation(conv))
151 	{
152 		count = GPOINTER_TO_INT(purple_conversation_get_data(conv,
153 		                        "unity-message-count"));
154 		if (!count++)
155 			++n_sources;
156 
157 		purple_conversation_set_data(conv, "unity-message-count",
158 		                             GINT_TO_POINTER(count));
159 		messaging_menu_add_conversation(conv, count);
160 		update_launcher();
161 	}
162 
163 	return 0;
164 }
165 
166 static void
unalert(PurpleConversation * conv)167 unalert(PurpleConversation *conv)
168 {
169 	if (GPOINTER_TO_INT(purple_conversation_get_data(conv, "unity-message-count")) > 0)
170 		--n_sources;
171 	purple_conversation_set_data(conv, "unity-message-count",
172 	                             GINT_TO_POINTER(0));
173 	messaging_menu_remove_conversation(conv);
174 	update_launcher();
175 }
176 
177 static int
unalert_cb(GtkWidget * widget,gpointer data,PurpleConversation * conv)178 unalert_cb(GtkWidget *widget, gpointer data, PurpleConversation *conv)
179 {
180 	unalert(conv);
181 	return 0;
182 }
183 
184 static gboolean
message_displayed_cb(PurpleAccount * account,const char * who,char * message,PurpleConversation * conv,PurpleMessageFlags flags)185 message_displayed_cb(PurpleAccount *account, const char *who, char *message,
186                      PurpleConversation *conv, PurpleMessageFlags flags)
187 {
188 	if ((purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT &&
189 	     alert_chat_nick && !(flags & PURPLE_MESSAGE_NICK)))
190 		return FALSE;
191 
192 	if ((flags & PURPLE_MESSAGE_RECV) && !(flags & PURPLE_MESSAGE_DELAYED))
193 		alert(conv);
194 
195 	return FALSE;
196 }
197 
198 static void
im_sent_im(PurpleAccount * account,const char * receiver,const char * message)199 im_sent_im(PurpleAccount *account, const char *receiver, const char *message)
200 {
201 	PurpleConversation *conv = NULL;
202 	conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, receiver,
203 	                                             account);
204 	unalert(conv);
205 }
206 
207 static void
chat_sent_im(PurpleAccount * account,const char * message,int id)208 chat_sent_im(PurpleAccount *account, const char *message, int id)
209 {
210 	PurpleConversation *conv = NULL;
211 	conv = purple_find_chat(purple_account_get_connection(account), id);
212 	unalert(conv);
213 }
214 
215 static void
conv_created(PurpleConversation * conv)216 conv_created(PurpleConversation *conv)
217 {
218 	purple_conversation_set_data(conv, "unity-message-count",
219 	                             GINT_TO_POINTER(0));
220 	attach_signals(conv);
221 }
222 
223 static void
deleting_conv(PurpleConversation * conv)224 deleting_conv(PurpleConversation *conv)
225 {
226 	detach_signals(conv);
227 	unalert(conv);
228 }
229 
230 static void
message_source_activated(MessagingMenuApp * app,const gchar * id,gpointer user_data)231 message_source_activated(MessagingMenuApp *app, const gchar *id,
232                          gpointer user_data)
233 {
234 	gchar **sections = g_strsplit(id, ":", 0);
235 	PurpleConversation *conv = NULL;
236 	PurpleAccount *account;
237 	PidginWindow *purplewin = NULL;
238 	PurpleConversationType conv_type;
239 
240 	char *type     = sections[0];
241 	char *cname    = sections[1];
242 	char *aname    = sections[2];
243 	char *protocol = sections[3];
244 
245 	conv_type = type[0] - '0';
246 	account = purple_accounts_find(aname, protocol);
247 	conv = purple_find_conversation_with_account(conv_type, cname, account);
248 
249 	if (conv) {
250 		unalert(conv);
251 		purplewin = PIDGIN_CONVERSATION(conv)->win;
252 		pidgin_conv_window_switch_gtkconv(purplewin, PIDGIN_CONVERSATION(conv));
253 		gdk_window_focus(gtk_widget_get_window(purplewin->window), time(NULL));
254 	}
255 	g_strfreev (sections);
256 }
257 
258 static PurpleSavedStatus *
create_transient_status(PurpleStatusPrimitive primitive,PurpleStatusType * status_type)259 create_transient_status(PurpleStatusPrimitive primitive, PurpleStatusType *status_type)
260 {
261 	PurpleSavedStatus *saved_status = purple_savedstatus_new(NULL, primitive);
262 
263 	if(status_type != NULL) {
264 		GList *tmp, *active_accts = purple_accounts_get_all_active();
265 		for (tmp = active_accts; tmp != NULL; tmp = tmp->next) {
266 			purple_savedstatus_set_substatus(saved_status,
267 				(PurpleAccount*) tmp->data, status_type, NULL);
268 		}
269 		g_list_free(active_accts);
270 	}
271 
272 	return saved_status;
273 }
274 
275 static void
status_changed_cb(PurpleSavedStatus * saved_status)276 status_changed_cb(PurpleSavedStatus *saved_status)
277 {
278 	MessagingMenuStatus status = MESSAGING_MENU_STATUS_AVAILABLE;
279 
280 	switch (purple_savedstatus_get_type(saved_status)) {
281 	case PURPLE_STATUS_AVAILABLE:
282 	case PURPLE_STATUS_MOOD:
283 	case PURPLE_STATUS_TUNE:
284 	case PURPLE_STATUS_UNSET:
285 		status = MESSAGING_MENU_STATUS_AVAILABLE;
286 		break;
287 
288 	case PURPLE_STATUS_AWAY:
289 	case PURPLE_STATUS_EXTENDED_AWAY:
290 		status = MESSAGING_MENU_STATUS_AWAY;
291 		break;
292 
293 	case PURPLE_STATUS_INVISIBLE:
294 		status = MESSAGING_MENU_STATUS_INVISIBLE;
295 		break;
296 
297 	case PURPLE_STATUS_MOBILE:
298 	case PURPLE_STATUS_OFFLINE:
299 		status = MESSAGING_MENU_STATUS_OFFLINE;
300 		break;
301 
302 	case PURPLE_STATUS_UNAVAILABLE:
303 		status = MESSAGING_MENU_STATUS_BUSY;
304 		break;
305 
306 	default:
307 		g_assert_not_reached();
308 	}
309 	messaging_menu_app_set_status(mmapp, status);
310 }
311 
312 static void
messaging_menu_status_changed(MessagingMenuApp * mmapp,MessagingMenuStatus mm_status,gpointer user_data)313 messaging_menu_status_changed(MessagingMenuApp *mmapp,
314                               MessagingMenuStatus mm_status, gpointer user_data)
315 {
316 	PurpleSavedStatus *saved_status;
317 	PurpleStatusPrimitive primitive = PURPLE_STATUS_UNSET;
318 
319 	switch (mm_status) {
320 	case MESSAGING_MENU_STATUS_AVAILABLE:
321 		primitive = PURPLE_STATUS_AVAILABLE;
322 		break;
323 
324 	case MESSAGING_MENU_STATUS_AWAY:
325 		primitive = PURPLE_STATUS_AWAY;
326 		break;
327 
328 	case MESSAGING_MENU_STATUS_BUSY:
329 		primitive = PURPLE_STATUS_UNAVAILABLE;
330 		break;
331 
332 	case MESSAGING_MENU_STATUS_INVISIBLE:
333 		primitive = PURPLE_STATUS_INVISIBLE;
334 		break;
335 
336 	case MESSAGING_MENU_STATUS_OFFLINE:
337 		primitive = PURPLE_STATUS_OFFLINE;
338 		break;
339 
340 	default:
341 		g_assert_not_reached();
342 	}
343 
344 	saved_status = purple_savedstatus_find_transient_by_type_and_message(primitive, NULL);
345 	if (saved_status == NULL)
346 		saved_status = create_transient_status(primitive, NULL);
347 	purple_savedstatus_activate(saved_status);
348 }
349 
350 static void
alert_config_cb(GtkWidget * widget,gpointer data)351 alert_config_cb(GtkWidget *widget, gpointer data)
352 {
353 	gboolean on = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
354 	purple_prefs_set_bool("/plugins/gtk/unity/alert_chat_nick", on);
355 	alert_chat_nick = on;
356 }
357 
358 static void
launcher_config_cb(GtkWidget * widget,gpointer data)359 launcher_config_cb(GtkWidget *widget, gpointer data)
360 {
361 	gint option = GPOINTER_TO_INT(data);
362 	if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
363 		return;
364 
365 	purple_prefs_set_int("/plugins/gtk/unity/launcher_count", option);
366 	launcher_count = option;
367 	if (option == LAUNCHER_COUNT_DISABLE)
368 		unity_launcher_entry_set_count_visible(launcher, FALSE);
369 	else
370 		update_launcher();
371 }
372 
373 static void
messaging_menu_config_cb(GtkWidget * widget,gpointer data)374 messaging_menu_config_cb(GtkWidget *widget, gpointer data)
375 {
376 	gint option = GPOINTER_TO_INT(data);
377 	if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
378 		return;
379 
380 	purple_prefs_set_int("/plugins/gtk/unity/messaging_menu_text", option);
381 	messaging_menu_text = option;
382 	refill_messaging_menu();
383 }
384 
385 static int
attach_signals(PurpleConversation * conv)386 attach_signals(PurpleConversation *conv)
387 {
388 	PidginConversation *gtkconv = NULL;
389 	guint id;
390 
391 	gtkconv = PIDGIN_CONVERSATION(conv);
392 	if (!gtkconv)
393 		return 0;
394 
395 	id = g_signal_connect(G_OBJECT(gtkconv->entry), "focus-in-event",
396 	                      G_CALLBACK(unalert_cb), conv);
397 	purple_conversation_set_data(conv, "unity-entry-signal", GUINT_TO_POINTER(id));
398 
399 	id = g_signal_connect(G_OBJECT(gtkconv->imhtml), "focus-in-event",
400 	                      G_CALLBACK(unalert_cb), conv);
401 	purple_conversation_set_data(conv, "unity-imhtml-signal", GUINT_TO_POINTER(id));
402 
403 	return 0;
404 }
405 
406 static void
detach_signals(PurpleConversation * conv)407 detach_signals(PurpleConversation *conv)
408 {
409 	PidginConversation *gtkconv = NULL;
410 	guint id;
411 	gtkconv = PIDGIN_CONVERSATION(conv);
412 	if (!gtkconv)
413 		return;
414 
415 	id = GPOINTER_TO_INT(purple_conversation_get_data(conv, "unity-imhtml-signal"));
416 	g_signal_handler_disconnect(gtkconv->imhtml, id);
417 
418 	id = GPOINTER_TO_INT(purple_conversation_get_data(conv, "unity-entry-signal"));
419 	g_signal_handler_disconnect(gtkconv->entry, id);
420 
421 	purple_conversation_set_data(conv, "unity-message-count",
422 	                             GINT_TO_POINTER(0));
423 }
424 
425 static GtkWidget *
get_config_frame(PurplePlugin * plugin)426 get_config_frame(PurplePlugin *plugin)
427 {
428 	GtkWidget *ret = NULL, *frame = NULL;
429 	GtkWidget *vbox = NULL, *toggle = NULL;
430 
431 	ret = gtk_vbox_new(FALSE, 18);
432 	gtk_container_set_border_width(GTK_CONTAINER (ret), 12);
433 
434 	/* Alerts */
435 
436 	frame = pidgin_make_frame(ret, _("Chatroom alerts"));
437 	vbox = gtk_vbox_new(FALSE, 5);
438 	gtk_container_add(GTK_CONTAINER(frame), vbox);
439 
440 	toggle = gtk_check_button_new_with_mnemonic(_("Chatroom message alerts _only where someone says your username"));
441 	gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0);
442 	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle),
443 	                             purple_prefs_get_bool("/plugins/gtk/unity/alert_chat_nick"));
444 	g_signal_connect(G_OBJECT(toggle), "toggled",
445 	                 G_CALLBACK(alert_config_cb), NULL);
446 
447 	/* Launcher integration */
448 
449 	frame = pidgin_make_frame(ret, _("Launcher Icon"));
450 	vbox = gtk_vbox_new(FALSE, 5);
451 	gtk_container_add(GTK_CONTAINER(frame), vbox);
452 
453 	toggle = gtk_radio_button_new_with_mnemonic(NULL, _("_Disable launcher integration"));
454 	gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0);
455 	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle),
456 		purple_prefs_get_int("/plugins/gtk/unity/launcher_count") == LAUNCHER_COUNT_DISABLE);
457 	g_signal_connect(G_OBJECT(toggle), "toggled",
458 	                 G_CALLBACK(launcher_config_cb), GUINT_TO_POINTER(LAUNCHER_COUNT_DISABLE));
459 
460 	toggle = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(toggle),
461 	                                                        _("Show number of unread _messages on launcher icon"));
462 	gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0);
463 	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle),
464 		purple_prefs_get_int("/plugins/gtk/unity/launcher_count") == LAUNCHER_COUNT_MESSAGES);
465 	g_signal_connect(G_OBJECT(toggle), "toggled",
466 	                 G_CALLBACK(launcher_config_cb), GUINT_TO_POINTER(LAUNCHER_COUNT_MESSAGES));
467 
468 	toggle = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(toggle),
469 	                                                        _("Show number of unread co_nversations on launcher icon"));
470 	gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0);
471 	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle),
472 		purple_prefs_get_int("/plugins/gtk/unity/launcher_count") == LAUNCHER_COUNT_SOURCES);
473 	g_signal_connect(G_OBJECT(toggle), "toggled",
474 	                 G_CALLBACK(launcher_config_cb), GUINT_TO_POINTER(LAUNCHER_COUNT_SOURCES));
475 
476 	/* Messaging menu integration */
477 
478 	frame = pidgin_make_frame(ret, _("Messaging Menu"));
479 	vbox = gtk_vbox_new(FALSE, 5);
480 	gtk_container_add(GTK_CONTAINER(frame), vbox);
481 
482 	toggle = gtk_radio_button_new_with_mnemonic(NULL,
483 	                                                        _("Show number of _unread messages for conversations in messaging menu"));
484 	gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0);
485 	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle),
486 		purple_prefs_get_int("/plugins/gtk/unity/messaging_menu_text") == MESSAGING_MENU_COUNT);
487 	g_signal_connect(G_OBJECT(toggle), "toggled",
488 	                 G_CALLBACK(messaging_menu_config_cb), GUINT_TO_POINTER(MESSAGING_MENU_COUNT));
489 
490 	toggle = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(toggle),
491 	                                                        _("Show _elapsed time for unread conversations in messaging menu"));
492 	gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0);
493 	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle),
494 		purple_prefs_get_int("/plugins/gtk/unity/messaging_menu_text") == MESSAGING_MENU_TIME);
495 	g_signal_connect(G_OBJECT(toggle), "toggled",
496 	                 G_CALLBACK(messaging_menu_config_cb), GUINT_TO_POINTER(MESSAGING_MENU_TIME));
497 
498 	gtk_widget_show_all(ret);
499 	return ret;
500 }
501 
502 static gboolean
plugin_load(PurplePlugin * plugin)503 plugin_load(PurplePlugin *plugin)
504 {
505 	GList *convs = purple_get_conversations();
506 	PurpleSavedStatus *saved_status;
507 	void *conv_handle = purple_conversations_get_handle();
508 	void *gtk_conv_handle = pidgin_conversations_get_handle();
509 	void *savedstat_handle = purple_savedstatuses_get_handle();
510 
511 	alert_chat_nick = purple_prefs_get_bool("/plugins/gtk/unity/alert_chat_nick");
512 
513 	mmapp = messaging_menu_app_new("pidgin.desktop");
514 	g_object_ref(mmapp);
515 	messaging_menu_app_register(mmapp);
516 	messaging_menu_text = purple_prefs_get_int("/plugins/gtk/unity/messaging_menu_text");
517 
518 	g_signal_connect(mmapp, "activate-source",
519 	                 G_CALLBACK(message_source_activated), NULL);
520 	g_signal_connect(mmapp, "status-changed",
521 	                 G_CALLBACK(messaging_menu_status_changed), NULL);
522 
523 	saved_status = purple_savedstatus_get_current();
524 	status_changed_cb(saved_status);
525 
526 	purple_signal_connect(savedstat_handle, "savedstatus-changed", plugin,
527 	                    PURPLE_CALLBACK(status_changed_cb), NULL);
528 
529 	launcher = unity_launcher_entry_get_for_desktop_id("pidgin.desktop");
530 	g_object_ref(launcher);
531 	launcher_count = purple_prefs_get_int("/plugins/gtk/unity/launcher_count");
532 
533 	purple_signal_connect(gtk_conv_handle, "displayed-im-msg", plugin,
534 	                    PURPLE_CALLBACK(message_displayed_cb), NULL);
535 	purple_signal_connect(gtk_conv_handle, "displayed-chat-msg", plugin,
536 	                    PURPLE_CALLBACK(message_displayed_cb), NULL);
537 	purple_signal_connect(conv_handle, "sent-im-msg", plugin,
538 	                    PURPLE_CALLBACK(im_sent_im), NULL);
539 	purple_signal_connect(conv_handle, "sent-chat-msg", plugin,
540 	                    PURPLE_CALLBACK(chat_sent_im), NULL);
541 	purple_signal_connect(conv_handle, "conversation-created", plugin,
542 	                    PURPLE_CALLBACK(conv_created), NULL);
543 	purple_signal_connect(conv_handle, "deleting-conversation", plugin,
544 	                    PURPLE_CALLBACK(deleting_conv), NULL);
545 
546 	while (convs) {
547 		PurpleConversation *conv = (PurpleConversation *)convs->data;
548 		attach_signals(conv);
549 		convs = convs->next;
550 	}
551 
552 	return TRUE;
553 }
554 
555 static gboolean
plugin_unload(PurplePlugin * plugin)556 plugin_unload(PurplePlugin *plugin)
557 {
558 	GList *convs = purple_get_conversations();
559 	while (convs) {
560 		PurpleConversation *conv = (PurpleConversation *)convs->data;
561 		unalert(conv);
562 		detach_signals(conv);
563 		convs = convs->next;
564 	}
565 
566 	unity_launcher_entry_set_count_visible(launcher, FALSE);
567 	messaging_menu_app_unregister(mmapp);
568 
569 	g_object_unref(launcher);
570 	g_object_unref(mmapp);
571 	return TRUE;
572 }
573 
574 static PidginPluginUiInfo ui_info =
575 {
576 	get_config_frame,
577 	0, /* page_num (Reserved) */
578 
579 	/* padding */
580 	NULL,
581 	NULL,
582 	NULL,
583 	NULL
584 };
585 
586 static PurplePluginInfo info =
587 {
588 	PURPLE_PLUGIN_MAGIC,
589 	PURPLE_MAJOR_VERSION,
590 	PURPLE_MINOR_VERSION,
591 	PURPLE_PLUGIN_STANDARD,                           /**< type           */
592 	PIDGIN_PLUGIN_TYPE,                               /**< ui_requirement */
593 	0,                                                /**< flags          */
594 	NULL,                                             /**< dependencies   */
595 	PURPLE_PRIORITY_DEFAULT,                          /**< priority       */
596 
597 	UNITY_PLUGIN_ID,                                  /**< id             */
598 	N_("Unity Integration"),                          /**< name           */
599 	DISPLAY_VERSION,                                  /**< version        */
600 	                                                  /**  summary        */
601 	N_("Provides integration with Unity."),
602 	                                                  /**  description    */
603 	N_("Provides integration with Unity's messaging "
604 	   "menu and launcher."),
605 	                                                  /**< author         */
606 	"Ankit Vani <a@nevitus.org>",
607 	PURPLE_WEBSITE,                                   /**< homepage       */
608 
609 	plugin_load,                                      /**< load           */
610 	plugin_unload,                                    /**< unload         */
611 	NULL,                                             /**< destroy        */
612 
613 	&ui_info,                                         /**< ui_info        */
614 	NULL,                                             /**< extra_info     */
615 	NULL,
616 	NULL,
617 
618 	/* padding */
619 	NULL,
620 	NULL,
621 	NULL,
622 	NULL
623 };
624 
625 static void
init_plugin(PurplePlugin * plugin)626 init_plugin(PurplePlugin *plugin)
627 {
628 	purple_prefs_add_none("/plugins/gtk");
629 	purple_prefs_add_none("/plugins/gtk/unity");
630 	purple_prefs_add_int("/plugins/gtk/unity/launcher_count", LAUNCHER_COUNT_SOURCES);
631 	purple_prefs_add_int("/plugins/gtk/unity/messaging_menu_text", MESSAGING_MENU_COUNT);
632 	purple_prefs_add_bool("/plugins/gtk/unity/alert_chat_nick", TRUE);
633 }
634 
635 PURPLE_INIT_PLUGIN(unity, init_plugin, info)
636