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