1 /*
2  * Purple Ticker Plugin
3  * The line below doesn't apply at all, does it?  It should be Syd, Sean, and
4  * maybe Nathan, I believe.
5  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20  * Boston, MA 02111-1301, USA.
21  */
22 
23 /*
24  * ticker.c -- Syd Logan, Summer 2000
25  * pluginized- Sean Egan, Summer 2002
26  */
27 #include "internal.h"
28 #include "pidgin.h"
29 
30 #include "blist.h"
31 #include "conversation.h"
32 #include "debug.h"
33 #include "prpl.h"
34 #include "signals.h"
35 #include "version.h"
36 
37 #include "gtkblist.h"
38 #include "gtkplugin.h"
39 #include "gtkutils.h"
40 #include "pidginstock.h"
41 
42 #include "gtkticker.h"
43 
44 #define TICKER_PLUGIN_ID "gtk-ticker"
45 
46 static GtkWidget *tickerwindow = NULL;
47 static GtkWidget *ticker;
48 
49 typedef struct {
50 	PurpleContact *contact;
51 	GtkWidget *ebox;
52 	GtkWidget *label;
53 	GtkWidget *icon;
54 	guint timeout;
55 } TickerData;
56 
57 static GList *tickerbuds = NULL;
58 
59 static void buddy_ticker_update_contact(PurpleContact *contact);
60 
buddy_ticker_destroy_window(GtkWidget * window,GdkEventAny * event,gpointer data)61 static gboolean buddy_ticker_destroy_window(GtkWidget *window,
62 		GdkEventAny *event, gpointer data) {
63 	if(window)
64 		gtk_widget_hide(window);
65 
66 	return TRUE; /* don't actually destroy the window */
67 }
68 
buddy_ticker_create_window(void)69 static void buddy_ticker_create_window(void) {
70 	if(tickerwindow) {
71 		gtk_widget_show(tickerwindow);
72 		return;
73 	}
74 
75 	tickerwindow = pidgin_create_window(_("Buddy Ticker"), 0, "ticker", TRUE);
76 	gtk_window_set_default_size(GTK_WINDOW(tickerwindow), 500, -1);
77 	g_signal_connect(G_OBJECT(tickerwindow), "delete_event",
78 			G_CALLBACK (buddy_ticker_destroy_window), NULL);
79 
80 	ticker = gtk_ticker_new();
81 	gtk_ticker_set_spacing(GTK_TICKER(ticker), 20);
82 	gtk_container_add(GTK_CONTAINER(tickerwindow), ticker);
83 	gtk_ticker_set_interval(GTK_TICKER(ticker), 500);
84 	gtk_ticker_set_scootch(GTK_TICKER(ticker), 10);
85 	gtk_ticker_start_scroll(GTK_TICKER(ticker));
86 	gtk_widget_set_size_request(ticker, 1, -1);
87 
88 	gtk_widget_show_all(tickerwindow);
89 }
90 
buddy_click_cb(GtkWidget * widget,GdkEventButton * event,gpointer user_data)91 static gboolean buddy_click_cb(GtkWidget *widget, GdkEventButton *event, gpointer user_data) {
92 	PurpleContact *contact = user_data;
93 	PurpleBuddy *b = purple_contact_get_priority_buddy(contact);
94 
95 	PurpleConversation *conv = purple_conversation_new(PURPLE_CONV_TYPE_IM,
96 	                                purple_buddy_get_account(b),
97 	                                purple_buddy_get_name(b));
98 	purple_conversation_present(conv);
99 	return TRUE;
100 }
101 
buddy_ticker_find_contact(PurpleContact * c)102 static TickerData *buddy_ticker_find_contact(PurpleContact *c) {
103 	GList *tb;
104 	for(tb = tickerbuds; tb; tb = tb->next) {
105 		TickerData *td = tb->data;
106 		if(td->contact == c)
107 			return td;
108 	}
109 	return NULL;
110 }
111 
buddy_ticker_set_pixmap(PurpleContact * c)112 static void buddy_ticker_set_pixmap(PurpleContact *c)
113 {
114 	TickerData *td = buddy_ticker_find_contact(c);
115 	PurpleBuddy *buddy;
116 	PurplePresence *presence;
117 	const char *stock;
118 
119 	if(!td)
120 		return;
121 
122 	buddy = purple_contact_get_priority_buddy(c);
123 	presence = purple_buddy_get_presence(buddy);
124 	stock = pidgin_stock_id_from_presence(presence);
125 	if(!td->icon) {
126 		td->icon = gtk_image_new();
127 		g_object_set(G_OBJECT(td->icon), "stock", stock,
128 				"icon-size", gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_MICROSCOPIC),
129 				NULL);
130 	} else {
131 		g_object_set(G_OBJECT(td->icon), "stock", stock, NULL);
132 	}
133 }
134 
buddy_ticker_set_pixmap_cb(gpointer data)135 static gboolean buddy_ticker_set_pixmap_cb(gpointer data) {
136 	TickerData *td = data;
137 
138 	if (g_list_find(tickerbuds, td) != NULL) {
139 		buddy_ticker_update_contact(td->contact);
140 		td->timeout = 0;
141 	}
142 
143 	return FALSE;
144 }
145 
buddy_ticker_add_buddy(PurpleBuddy * b)146 static void buddy_ticker_add_buddy(PurpleBuddy *b) {
147 	GtkWidget *hbox;
148 	TickerData *td;
149 	PurpleContact *contact;
150 
151 	contact = purple_buddy_get_contact(b);
152 
153 	buddy_ticker_create_window();
154 
155 	if (!ticker)
156 		return;
157 
158 	if (buddy_ticker_find_contact(contact))
159 	{
160 		buddy_ticker_update_contact(contact);
161 		return;
162 	}
163 
164 	td = g_new0(TickerData, 1);
165 	td->contact = contact;
166 	tickerbuds = g_list_append(tickerbuds, td);
167 
168 	td->ebox = gtk_event_box_new();
169 	gtk_ticker_add(GTK_TICKER(ticker), td->ebox);
170 	hbox = gtk_hbox_new(FALSE, 0);
171 	gtk_container_add(GTK_CONTAINER(td->ebox), hbox);
172 	buddy_ticker_set_pixmap(contact);
173 	gtk_box_pack_start(GTK_BOX(hbox), td->icon, FALSE, FALSE, 0);
174 
175 	g_signal_connect(G_OBJECT(td->ebox), "button-press-event",
176 		G_CALLBACK(buddy_click_cb), contact);
177 
178 	td->label = gtk_label_new(purple_contact_get_alias(contact));
179 	gtk_box_pack_start(GTK_BOX(hbox), td->label, FALSE, FALSE, 2);
180 
181 	gtk_widget_show_all(td->ebox);
182 	gtk_widget_show(tickerwindow);
183 
184 	/*
185 	 * Update the icon in a few seconds (after the open door icon has
186 	 * changed).  This is somewhat ugly.
187 	 */
188 	td->timeout = g_timeout_add(11000, buddy_ticker_set_pixmap_cb, td);
189 }
190 
buddy_ticker_remove(TickerData * td)191 static void buddy_ticker_remove(TickerData *td) {
192 	gtk_ticker_remove(GTK_TICKER(ticker), td->ebox);
193 	tickerbuds = g_list_remove(tickerbuds, td);
194 	if (td->timeout != 0)
195 		g_source_remove(td->timeout);
196 	g_free(td);
197 }
198 
buddy_ticker_update_contact(PurpleContact * contact)199 static void buddy_ticker_update_contact(PurpleContact *contact) {
200 	TickerData *td = buddy_ticker_find_contact(contact);
201 
202 	if (!td)
203 		return;
204 
205 	/* pop up the ticker window again */
206 	buddy_ticker_create_window();
207 	if (purple_contact_get_priority_buddy(contact) == NULL)
208 		buddy_ticker_remove(td);
209 	else {
210 		buddy_ticker_set_pixmap(contact);
211 		gtk_label_set_text(GTK_LABEL(td->label), purple_contact_get_alias(contact));
212 	}
213 }
214 
buddy_ticker_remove_buddy(PurpleBuddy * b)215 static void buddy_ticker_remove_buddy(PurpleBuddy *b) {
216 	PurpleContact *c = purple_buddy_get_contact(b);
217 	TickerData *td = buddy_ticker_find_contact(c);
218 
219 	if (!td)
220 		return;
221 
222 	purple_contact_invalidate_priority_buddy(c);
223 
224 	/* pop up the ticker window again */
225 	buddy_ticker_create_window();
226 	buddy_ticker_update_contact(c);
227 }
228 
buddy_ticker_show(void)229 static void buddy_ticker_show(void)
230 {
231 	PurpleBlistNode *gnode, *cnode, *bnode;
232 	PurpleBuddy *b;
233 
234 	for(gnode = purple_blist_get_root();
235 	    gnode;
236 	    gnode = purple_blist_node_get_sibling_next(gnode))
237 	{
238 		if(!PURPLE_BLIST_NODE_IS_GROUP(gnode))
239 			continue;
240 		for(cnode = purple_blist_node_get_first_child(gnode);
241 		    cnode;
242 		    cnode = purple_blist_node_get_sibling_next(cnode))
243 		{
244 			if(!PURPLE_BLIST_NODE_IS_CONTACT(cnode))
245 				continue;
246 			for(bnode = purple_blist_node_get_first_child(cnode);
247 			    bnode;
248 			    bnode = purple_blist_node_get_sibling_next(bnode))
249 			{
250 				if(!PURPLE_BLIST_NODE_IS_BUDDY(bnode))
251 					continue;
252 				b = (PurpleBuddy *)bnode;
253 				if(PURPLE_BUDDY_IS_ONLINE(b))
254 					buddy_ticker_add_buddy(b);
255 			}
256 		}
257 	}
258 }
259 
260 static void
buddy_signon_cb(PurpleBuddy * b)261 buddy_signon_cb(PurpleBuddy *b)
262 {
263 	PurpleContact *c = purple_buddy_get_contact(b);
264 	purple_contact_invalidate_priority_buddy(c);
265 	if(buddy_ticker_find_contact(c))
266 		buddy_ticker_update_contact(c);
267 	else
268 		buddy_ticker_add_buddy(b);
269 }
270 
271 static void
buddy_signoff_cb(PurpleBuddy * b)272 buddy_signoff_cb(PurpleBuddy *b)
273 {
274 	buddy_ticker_remove_buddy(b);
275 	if(!tickerbuds)
276 		gtk_widget_hide(tickerwindow);
277 }
278 
279 static void
status_changed_cb(PurpleBuddy * b,PurpleStatus * os,PurpleStatus * s)280 status_changed_cb(PurpleBuddy *b, PurpleStatus *os, PurpleStatus *s)
281 {
282 	PurpleContact *c = purple_buddy_get_contact(b);
283 	if(buddy_ticker_find_contact(c))
284 		buddy_ticker_set_pixmap(c);
285 	else
286 		buddy_ticker_add_buddy(b);
287 }
288 
289 static void
signoff_cb(PurpleConnection * gc)290 signoff_cb(PurpleConnection *gc)
291 {
292 	TickerData *td;
293 	if (!purple_connections_get_all()) {
294 		while (tickerbuds) {
295 			td = tickerbuds->data;
296 			tickerbuds = g_list_delete_link(tickerbuds, tickerbuds);
297 			if (td->timeout != 0)
298 				g_source_remove(td->timeout);
299 			g_free(td);
300 		}
301 		gtk_widget_destroy(tickerwindow);
302 		tickerwindow = NULL;
303 		ticker = NULL;
304 	} else {
305 		GList *t = tickerbuds;
306 		while (t) {
307 			td = t->data;
308 			t = t->next;
309 			buddy_ticker_update_contact(td->contact);
310 		}
311 	}
312 }
313 
314 
315 /*
316  *  EXPORTED FUNCTIONS
317  */
318 
319 static gboolean
plugin_load(PurplePlugin * plugin)320 plugin_load(PurplePlugin *plugin)
321 {
322 	void *blist_handle = purple_blist_get_handle();
323 
324 	purple_signal_connect(purple_connections_get_handle(), "signed-off",
325 						plugin, PURPLE_CALLBACK(signoff_cb), NULL);
326 	purple_signal_connect(blist_handle, "buddy-signed-on",
327 						plugin, PURPLE_CALLBACK(buddy_signon_cb), NULL);
328 	purple_signal_connect(blist_handle, "buddy-signed-off",
329 						plugin, PURPLE_CALLBACK(buddy_signoff_cb), NULL);
330 	purple_signal_connect(blist_handle, "buddy-status-changed",
331 						plugin, PURPLE_CALLBACK(status_changed_cb), NULL);
332 
333 	if (purple_connections_get_all())
334 		buddy_ticker_show();
335 
336 	return TRUE;
337 }
338 
339 static gboolean
plugin_unload(PurplePlugin * plugin)340 plugin_unload(PurplePlugin *plugin)
341 {
342 	TickerData *td;
343 
344 	while (tickerbuds) {
345 		td = tickerbuds->data;
346 		tickerbuds = g_list_delete_link(tickerbuds, tickerbuds);
347 		if (td->timeout != 0)
348 			g_source_remove(td->timeout);
349 		g_free(td);
350 	}
351 
352 	if (tickerwindow != NULL) {
353 		gtk_widget_destroy(tickerwindow);
354 		tickerwindow = NULL;
355 	}
356 
357 	return TRUE;
358 }
359 
360 static PurplePluginInfo info =
361 {
362 	PURPLE_PLUGIN_MAGIC,
363 	PURPLE_MAJOR_VERSION,
364 	PURPLE_MINOR_VERSION,
365 	PURPLE_PLUGIN_STANDARD,                             /**< type           */
366 	PIDGIN_PLUGIN_TYPE,                             /**< ui_requirement */
367 	0,                                                /**< flags          */
368 	NULL,                                             /**< dependencies   */
369 	PURPLE_PRIORITY_DEFAULT,                            /**< priority       */
370 
371 	TICKER_PLUGIN_ID,                                 /**< id             */
372 	N_("Buddy Ticker"),                               /**< name           */
373 	DISPLAY_VERSION,                                  /**< version        */
374 	                                                  /**  summary        */
375 	N_("A horizontal scrolling version of the buddy list."),
376 	                                                  /**  description    */
377 	N_("A horizontal scrolling version of the buddy list."),
378 	"Syd Logan",                                      /**< author         */
379 	PURPLE_WEBSITE,                                     /**< homepage       */
380 
381 	plugin_load,                                      /**< load           */
382 	plugin_unload,                                    /**< unload         */
383 	NULL,                                             /**< destroy        */
384 
385 	NULL,                                             /**< ui_info        */
386 	NULL,                                             /**< extra_info     */
387 	NULL,
388 	NULL,
389 
390 	/* padding */
391 	NULL,
392 	NULL,
393 	NULL,
394 	NULL
395 };
396 
397 static void
init_plugin(PurplePlugin * plugin)398 init_plugin(PurplePlugin *plugin)
399 {
400 }
401 
402 PURPLE_INIT_PLUGIN(ticker, init_plugin, info)
403