src/gtkpounce.c

branch
gaim
changeset 20470
77693555855f
parent 13071
b98e72d4089a
parent 20469
b2836a24d81e
child 20471
1966704b3e42
equal deleted inserted replaced
13071:b98e72d4089a 20470:77693555855f
1 /**
2 * @file gtkpounce.c GTK+ Buddy Pounce API
3 * @ingroup gtkui
4 *
5 * gaim
6 *
7 * Gaim is the legal property of its developers, whose names are too numerous
8 * to list here. Please refer to the COPYRIGHT file distributed with this
9 * source distribution.
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 *
25 */
26 #include "internal.h"
27 #include "gtkgaim.h"
28
29 #include "account.h"
30 #include "conversation.h"
31 #include "debug.h"
32 #include "notify.h"
33 #include "prpl.h"
34 #include "request.h"
35 #include "server.h"
36 #include "sound.h"
37 #include "util.h"
38
39 #include "gtkblist.h"
40 #include "gtkdialogs.h"
41 #include "gtkpounce.h"
42 #include "gtkstock.h"
43 #include "gtkutils.h"
44
45 /**
46 * These are used for the GtkTreeView when you're scrolling through
47 * all your saved pounces.
48 */
49 enum
50 {
51 /* Hidden column containing the GaimPounce */
52 POUNCES_MANAGER_COLUMN_POUNCE,
53 POUNCES_MANAGER_COLUMN_ICON,
54 POUNCES_MANAGER_COLUMN_TARGET,
55 POUNCES_MANAGER_COLUMN_ACCOUNT,
56 POUNCES_MANAGER_COLUMN_RECURRING,
57 POUNCES_MANAGER_NUM_COLUMNS
58 };
59
60 typedef struct
61 {
62 /* Pounce data */
63 GaimPounce *pounce;
64 GaimAccount *account;
65
66 /* The window */
67 GtkWidget *window;
68
69 /* Pounce on Whom */
70 GtkWidget *account_menu;
71 GtkWidget *buddy_entry;
72
73 /* Pounce options */
74 GtkWidget *on_away;
75
76 /* Pounce When Buddy... */
77 GtkWidget *signon;
78 GtkWidget *signoff;
79 GtkWidget *away;
80 GtkWidget *away_return;
81 GtkWidget *idle;
82 GtkWidget *idle_return;
83 GtkWidget *typing;
84 GtkWidget *stop_typing;
85 GtkWidget *message_recv;
86
87 /* Action */
88 GtkWidget *open_win;
89 GtkWidget *popup;
90 GtkWidget *popup_entry;
91 GtkWidget *send_msg;
92 GtkWidget *send_msg_entry;
93 GtkWidget *exec_cmd;
94 GtkWidget *exec_cmd_entry;
95 GtkWidget *exec_cmd_browse;
96 GtkWidget *play_sound;
97 GtkWidget *play_sound_entry;
98 GtkWidget *play_sound_browse;
99 GtkWidget *play_sound_test;
100
101 GtkWidget *save_pounce;
102
103 /* Buttons */
104 GtkWidget *save_button;
105
106 } GaimGtkPounceDialog;
107
108 typedef struct
109 {
110 GtkWidget *window;
111 GtkListStore *model;
112 GtkWidget *treeview;
113 GtkWidget *modify_button;
114 GtkWidget *delete_button;
115 } PouncesManager;
116
117 static PouncesManager *pounces_manager = NULL;
118
119 /**************************************************************************
120 * Callbacks
121 **************************************************************************/
122 static gint
123 delete_win_cb(GtkWidget *w, GdkEventAny *e, GaimGtkPounceDialog *dialog)
124 {
125 gtk_widget_destroy(dialog->window);
126 g_free(dialog);
127
128 return TRUE;
129 }
130
131 static void
132 delete_cb(GtkWidget *w, GaimGtkPounceDialog *dialog)
133 {
134 gaim_pounce_destroy(dialog->pounce);
135
136 delete_win_cb(NULL, NULL, dialog);
137 }
138
139 static void
140 cancel_cb(GtkWidget *w, GaimGtkPounceDialog *dialog)
141 {
142 delete_win_cb(NULL, NULL, dialog);
143 }
144
145 static void
146 pounce_update_entry_fields(void *user_data, const char *filename)
147 {
148 GtkWidget *entry = (GtkWidget *)user_data;
149
150 gtk_entry_set_text(GTK_ENTRY(entry), filename);
151 }
152
153 static void
154 filesel(GtkWidget *widget, gpointer data)
155 {
156 GtkWidget *entry;
157 const gchar *name;
158
159 entry = (GtkWidget *)data;
160 name = gtk_entry_get_text(GTK_ENTRY(entry));
161
162 gaim_request_file(entry, _("Select a file"), name, FALSE,
163 G_CALLBACK(pounce_update_entry_fields), NULL, entry);
164 }
165
166 static void
167 pounce_test_sound(GtkWidget *w, GtkWidget *entry)
168 {
169 const char *filename;
170
171 filename = gtk_entry_get_text(GTK_ENTRY(entry));
172
173 if (filename != NULL && *filename != '\0')
174 gaim_sound_play_file(filename, NULL);
175 else
176 gaim_sound_play_event(GAIM_SOUND_POUNCE_DEFAULT, NULL);
177 }
178
179 static void
180 add_pounce_to_treeview(GtkListStore *model, GaimPounce *pounce)
181 {
182 GtkTreeIter iter;
183 GaimAccount *account;
184 GaimPounceEvent events;
185 gboolean recurring;
186 const char *pouncer;
187 const char *pouncee;
188 GdkPixbuf *pixbuf, *scale = NULL;
189
190 account = gaim_pounce_get_pouncer(pounce);
191
192 if (gaim_account_is_disconnected(account))
193 return;
194
195 events = gaim_pounce_get_events(pounce);
196
197 pixbuf = gaim_gtk_create_prpl_icon(account);
198
199 if (pixbuf != NULL)
200 scale = gdk_pixbuf_scale_simple(pixbuf, 16, 16,
201 GDK_INTERP_BILINEAR);
202
203 pouncer = gaim_account_get_username(account);
204 pouncee = gaim_pounce_get_pouncee(pounce);
205 recurring = gaim_pounce_get_save(pounce);
206
207 gtk_list_store_append(model, &iter);
208 gtk_list_store_set(model, &iter,
209 POUNCES_MANAGER_COLUMN_POUNCE, pounce,
210 POUNCES_MANAGER_COLUMN_ICON, scale,
211 POUNCES_MANAGER_COLUMN_TARGET, pouncee,
212 POUNCES_MANAGER_COLUMN_ACCOUNT, pouncer,
213 POUNCES_MANAGER_COLUMN_RECURRING, recurring,
214 -1);
215 }
216
217 static void
218 populate_pounces_list(PouncesManager *dialog)
219 {
220 const GList *pounces;
221
222 gtk_list_store_clear(dialog->model);
223
224 for (pounces = gaim_pounces_get_all(); pounces != NULL;
225 pounces = g_list_next(pounces))
226 {
227 add_pounce_to_treeview(dialog->model, pounces->data);
228 }
229 }
230
231 static void
232 update_pounces(void)
233 {
234 /* Rebuild the pounces list if the pounces manager is open */
235 if (pounces_manager != NULL)
236 {
237 populate_pounces_list(pounces_manager);
238 }
239 }
240
241 static void
242 signed_on_off_cb(GaimConnection *gc, gpointer user_data)
243 {
244 update_pounces();
245 }
246
247 static void
248 save_pounce_cb(GtkWidget *w, GaimGtkPounceDialog *dialog)
249 {
250 const char *name;
251 const char *message, *command, *sound, *reason;
252 GaimPounceEvent events = GAIM_POUNCE_NONE;
253 GaimPounceOption options = GAIM_POUNCE_OPTION_NONE;
254
255 name = gtk_entry_get_text(GTK_ENTRY(dialog->buddy_entry));
256
257 if (*name == '\0')
258 {
259 gaim_notify_error(NULL, NULL,
260 _("Please enter a buddy to pounce."), NULL);
261 return;
262 }
263
264 /* Options */
265 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dialog->on_away)))
266 options |= GAIM_POUNCE_OPTION_AWAY;
267
268 /* Events */
269 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dialog->signon)))
270 events |= GAIM_POUNCE_SIGNON;
271
272 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dialog->signoff)))
273 events |= GAIM_POUNCE_SIGNOFF;
274
275 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dialog->away)))
276 events |= GAIM_POUNCE_AWAY;
277
278 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dialog->away_return)))
279 events |= GAIM_POUNCE_AWAY_RETURN;
280
281 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dialog->idle)))
282 events |= GAIM_POUNCE_IDLE;
283
284 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dialog->idle_return)))
285 events |= GAIM_POUNCE_IDLE_RETURN;
286
287 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dialog->typing)))
288 events |= GAIM_POUNCE_TYPING;
289
290 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dialog->stop_typing)))
291 events |= GAIM_POUNCE_TYPING_STOPPED;
292
293 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dialog->message_recv)))
294 events |= GAIM_POUNCE_MESSAGE_RECEIVED;
295
296 /* Data fields */
297 message = gtk_entry_get_text(GTK_ENTRY(dialog->send_msg_entry));
298 command = gtk_entry_get_text(GTK_ENTRY(dialog->exec_cmd_entry));
299 sound = gtk_entry_get_text(GTK_ENTRY(dialog->play_sound_entry));
300 reason = gtk_entry_get_text(GTK_ENTRY(dialog->popup_entry));
301
302 if (*reason == '\0') reason = NULL;
303 if (*message == '\0') message = NULL;
304 if (*command == '\0') command = NULL;
305 if (*sound == '\0') sound = NULL;
306
307 if (dialog->pounce == NULL)
308 {
309 dialog->pounce = gaim_pounce_new(GAIM_GTK_UI, dialog->account,
310 name, events, options);
311 }
312 else {
313 gaim_pounce_set_events(dialog->pounce, events);
314 gaim_pounce_set_options(dialog->pounce, options);
315 gaim_pounce_set_pouncer(dialog->pounce, dialog->account);
316 gaim_pounce_set_pouncee(dialog->pounce, name);
317 }
318
319 /* Actions */
320 gaim_pounce_action_set_enabled(dialog->pounce, "open-window",
321 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dialog->open_win)));
322 gaim_pounce_action_set_enabled(dialog->pounce, "popup-notify",
323 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dialog->popup)));
324 gaim_pounce_action_set_enabled(dialog->pounce, "send-message",
325 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dialog->send_msg)));
326 gaim_pounce_action_set_enabled(dialog->pounce, "execute-command",
327 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dialog->exec_cmd)));
328 gaim_pounce_action_set_enabled(dialog->pounce, "play-sound",
329 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dialog->play_sound)));
330
331 gaim_pounce_action_set_attribute(dialog->pounce, "send-message",
332 "message", message);
333 gaim_pounce_action_set_attribute(dialog->pounce, "execute-command",
334 "command", command);
335 gaim_pounce_action_set_attribute(dialog->pounce, "play-sound",
336 "filename", sound);
337 gaim_pounce_action_set_attribute(dialog->pounce, "popup-notify",
338 "reason", reason);
339
340 /* Set the defaults for next time. */
341 gaim_prefs_set_bool("/gaim/gtk/pounces/default_actions/open-window",
342 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dialog->open_win)));
343 gaim_prefs_set_bool("/gaim/gtk/pounces/default_actions/popup-notify",
344 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dialog->popup)));
345 gaim_prefs_set_bool("/gaim/gtk/pounces/default_actions/send-message",
346 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dialog->send_msg)));
347 gaim_prefs_set_bool("/gaim/gtk/pounces/default_actions/execute-command",
348 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dialog->exec_cmd)));
349 gaim_prefs_set_bool("/gaim/gtk/pounces/default_actions/play-sound",
350 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dialog->play_sound)));
351
352 gaim_pounce_set_save(dialog->pounce,
353 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dialog->save_pounce)));
354
355 update_pounces();
356
357 delete_win_cb(NULL, NULL, dialog);
358 }
359
360 static void
361 pounce_choose_cb(GtkWidget *item, GaimAccount *account,
362 GaimGtkPounceDialog *dialog)
363 {
364 dialog->account = account;
365 }
366
367 static void
368 buddy_changed_cb(GtkEntry *entry, GaimGtkPounceDialog *dialog)
369 {
370 if (dialog->save_button == NULL)
371 return;
372
373 gtk_widget_set_sensitive(dialog->save_button,
374 *gtk_entry_get_text(entry) != '\0');
375 }
376
377 static void
378 message_recv_toggle(GtkButton *message_recv, GtkWidget *send_msg)
379 {
380 gboolean active = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(message_recv));
381
382 gtk_widget_set_sensitive(send_msg, !active);
383 if (active)
384 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(send_msg), FALSE);
385 }
386
387 static void
388 pounce_dnd_recv(GtkWidget *widget, GdkDragContext *dc, gint x, gint y,
389 GtkSelectionData *sd, guint info, guint t, gpointer data)
390 {
391 GaimGtkPounceDialog *dialog;
392
393 if (sd->target == gdk_atom_intern("GAIM_BLIST_NODE", FALSE))
394 {
395 GaimBlistNode *node = NULL;
396 GaimBuddy *buddy;
397
398 memcpy(&node, sd->data, sizeof(node));
399
400 if (GAIM_BLIST_NODE_IS_CONTACT(node))
401 buddy = gaim_contact_get_priority_buddy((GaimContact *)node);
402 else if (GAIM_BLIST_NODE_IS_BUDDY(node))
403 buddy = (GaimBuddy *)node;
404 else
405 return;
406
407 dialog = (GaimGtkPounceDialog *)data;
408
409 gtk_entry_set_text(GTK_ENTRY(dialog->buddy_entry), buddy->name);
410 dialog->account = buddy->account;
411 gaim_gtk_account_option_menu_set_selected(dialog->account_menu, buddy->account);
412
413 gtk_drag_finish(dc, TRUE, (dc->action == GDK_ACTION_MOVE), t);
414 }
415 else if (sd->target == gdk_atom_intern("application/x-im-contact", FALSE))
416 {
417 char *protocol = NULL;
418 char *username = NULL;
419 GaimAccount *account;
420
421 if (gaim_gtk_parse_x_im_contact((const char *)sd->data, FALSE, &account,
422 &protocol, &username, NULL))
423 {
424 if (account == NULL)
425 {
426 gaim_notify_error(NULL, NULL,
427 _("You are not currently signed on with an account that "
428 "can add that buddy."), NULL);
429 }
430 else
431 {
432 dialog = (GaimGtkPounceDialog *)data;
433
434 gtk_entry_set_text(GTK_ENTRY(dialog->buddy_entry), username);
435 dialog->account = account;
436 gaim_gtk_account_option_menu_set_selected(dialog->account_menu, account);
437 }
438 }
439
440 if (username != NULL) g_free(username);
441 if (protocol != NULL) g_free(protocol);
442
443 gtk_drag_finish(dc, TRUE, (dc->action == GDK_ACTION_MOVE), t);
444 }
445 }
446
447 static const GtkTargetEntry dnd_targets[] =
448 {
449 {"GAIM_BLIST_NODE", GTK_TARGET_SAME_APP, 0},
450 {"application/x-im-contact", 0, 1}
451 };
452
453 void
454 gaim_gtk_pounce_editor_show(GaimAccount *account, const char *name,
455 GaimPounce *cur_pounce)
456 {
457 GaimGtkPounceDialog *dialog;
458 GtkWidget *window;
459 GtkWidget *label;
460 GtkWidget *bbox;
461 GtkWidget *vbox1, *vbox2;
462 GtkWidget *hbox;
463 GtkWidget *button;
464 GtkWidget *frame;
465 GtkWidget *table;
466 GtkSizeGroup *sg;
467 GPtrArray *sound_widgets;
468 GPtrArray *exec_widgets;
469
470 g_return_if_fail((cur_pounce != NULL) || (account != NULL) ||
471 (gaim_connections_get_all() != NULL));
472
473 dialog = g_new0(GaimGtkPounceDialog, 1);
474
475 if (cur_pounce != NULL)
476 {
477 dialog->pounce = cur_pounce;
478 dialog->account = gaim_pounce_get_pouncer(cur_pounce);
479 }
480 else if (account != NULL)
481 {
482 dialog->pounce = NULL;
483 dialog->account = account;
484 }
485 else
486 {
487 GaimConnection *gc;
488
489 gc = (GaimConnection *)gaim_connections_get_all()->data;
490
491 dialog->pounce = NULL;
492 dialog->account = gaim_connection_get_account(gc);
493 }
494
495 sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
496
497 /* Create the window. */
498 dialog->window = window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
499 gtk_window_set_type_hint(GTK_WINDOW(window), GDK_WINDOW_TYPE_HINT_DIALOG);
500 gtk_window_set_role(GTK_WINDOW(window), "buddy_pounce");
501 gtk_window_set_resizable(GTK_WINDOW(window), FALSE);
502 gtk_window_set_title(GTK_WINDOW(window),
503 (cur_pounce == NULL
504 ? _("New Buddy Pounce") : _("Edit Buddy Pounce")));
505
506 gtk_container_set_border_width(GTK_CONTAINER(window), GAIM_HIG_BORDER);
507
508 g_signal_connect(G_OBJECT(window), "delete_event",
509 G_CALLBACK(delete_win_cb), dialog);
510
511 /* Create the parent vbox for everything. */
512 vbox1 = gtk_vbox_new(FALSE, GAIM_HIG_BORDER);
513 gtk_container_add(GTK_CONTAINER(window), vbox1);
514 gtk_widget_show(vbox1);
515
516 /* Create the vbox that will contain all the prefs stuff. */
517 vbox2 = gtk_vbox_new(FALSE, GAIM_HIG_BOX_SPACE);
518 gtk_box_pack_start(GTK_BOX(vbox1), vbox2, TRUE, TRUE, 0);
519
520 /* Create the "Pounce on Whom" frame. */
521 frame = gaim_gtk_make_frame(vbox2, _("Pounce on Whom"));
522
523 /* Account: */
524 hbox = gtk_hbox_new(FALSE, GAIM_HIG_BOX_SPACE);
525 gtk_box_pack_start(GTK_BOX(frame), hbox, FALSE, FALSE, 0);
526 gtk_widget_show(hbox);
527
528 label = gtk_label_new_with_mnemonic(_("_Account:"));
529 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
530 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
531 gtk_widget_show(label);
532 gtk_size_group_add_widget(sg, label);
533
534 dialog->account_menu =
535 gaim_gtk_account_option_menu_new(dialog->account, FALSE,
536 G_CALLBACK(pounce_choose_cb),
537 NULL, dialog);
538
539 gtk_box_pack_start(GTK_BOX(hbox), dialog->account_menu, FALSE, FALSE, 0);
540 gtk_widget_show(dialog->account_menu);
541 gaim_set_accessible_label (dialog->account_menu, label);
542
543 /* Buddy: */
544 hbox = gtk_hbox_new(FALSE, GAIM_HIG_BOX_SPACE);
545 gtk_box_pack_start(GTK_BOX(frame), hbox, FALSE, FALSE, 0);
546 gtk_widget_show(hbox);
547
548 label = gtk_label_new_with_mnemonic(_("_Buddy name:"));
549 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
550 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
551 gtk_widget_show(label);
552 gtk_size_group_add_widget(sg, label);
553
554 dialog->buddy_entry = gtk_entry_new();
555
556 gtk_box_pack_start(GTK_BOX(hbox), dialog->buddy_entry, TRUE, TRUE, 0);
557 gtk_widget_show(dialog->buddy_entry);
558
559 g_signal_connect(G_OBJECT(dialog->buddy_entry), "changed",
560 G_CALLBACK(buddy_changed_cb), dialog);
561 gaim_set_accessible_label (dialog->buddy_entry, label);
562
563 if (cur_pounce != NULL) {
564 gtk_entry_set_text(GTK_ENTRY(dialog->buddy_entry),
565 gaim_pounce_get_pouncee(cur_pounce));
566 }
567 else if (name != NULL) {
568 gtk_entry_set_text(GTK_ENTRY(dialog->buddy_entry), name);
569 }
570
571 /* Create the "Pounce When Buddy..." frame. */
572 frame = gaim_gtk_make_frame(vbox2, _("Pounce When Buddy..."));
573
574 table = gtk_table_new(2, 4, FALSE);
575 gtk_container_add(GTK_CONTAINER(frame), table);
576 gtk_table_set_col_spacings(GTK_TABLE(table), GAIM_HIG_BORDER);
577 gtk_widget_show(table);
578
579 dialog->signon =
580 gtk_check_button_new_with_mnemonic(_("Si_gns on"));
581 dialog->signoff =
582 gtk_check_button_new_with_mnemonic(_("Signs o_ff"));
583 dialog->away =
584 gtk_check_button_new_with_mnemonic(_("Goes a_way"));
585 dialog->away_return =
586 gtk_check_button_new_with_mnemonic(_("Ret_urns from away"));
587 dialog->idle =
588 gtk_check_button_new_with_mnemonic(_("Becomes _idle"));
589 dialog->idle_return =
590 gtk_check_button_new_with_mnemonic(_("Is no longer i_dle"));
591 dialog->typing =
592 gtk_check_button_new_with_mnemonic(_("Starts _typing"));
593 dialog->stop_typing =
594 gtk_check_button_new_with_mnemonic(_("Stops t_yping"));
595 dialog->message_recv =
596 gtk_check_button_new_with_mnemonic(_("Sends a _message"));
597
598 gtk_table_attach(GTK_TABLE(table), dialog->signon, 0, 1, 0, 1,
599 GTK_FILL, 0, 0, 0);
600 gtk_table_attach(GTK_TABLE(table), dialog->signoff, 1, 2, 0, 1,
601 GTK_FILL, 0, 0, 0);
602 gtk_table_attach(GTK_TABLE(table), dialog->away, 0, 1, 1, 2,
603 GTK_FILL, 0, 0, 0);
604 gtk_table_attach(GTK_TABLE(table), dialog->away_return, 1, 2, 1, 2,
605 GTK_FILL, 0, 0, 0);
606 gtk_table_attach(GTK_TABLE(table), dialog->idle, 0, 1, 2, 3,
607 GTK_FILL, 0, 0, 0);
608 gtk_table_attach(GTK_TABLE(table), dialog->idle_return, 1, 2, 2, 3,
609 GTK_FILL, 0, 0, 0);
610 gtk_table_attach(GTK_TABLE(table), dialog->typing, 0, 1, 3, 4,
611 GTK_FILL, 0, 0, 0);
612 gtk_table_attach(GTK_TABLE(table), dialog->stop_typing, 1, 2, 3, 4,
613 GTK_FILL, 0, 0, 0);
614 gtk_table_attach(GTK_TABLE(table), dialog->message_recv, 0, 1, 4, 5,
615 GTK_FILL, 0, 0, 0);
616
617 gtk_widget_show(dialog->signon);
618 gtk_widget_show(dialog->signoff);
619 gtk_widget_show(dialog->away);
620 gtk_widget_show(dialog->away_return);
621 gtk_widget_show(dialog->idle);
622 gtk_widget_show(dialog->idle_return);
623 gtk_widget_show(dialog->typing);
624 gtk_widget_show(dialog->stop_typing);
625 gtk_widget_show(dialog->message_recv);
626
627 /* Create the "Action" frame. */
628 frame = gaim_gtk_make_frame(vbox2, _("Action"));
629
630 table = gtk_table_new(3, 5, FALSE);
631 gtk_container_add(GTK_CONTAINER(frame), table);
632 gtk_table_set_col_spacings(GTK_TABLE(table), GAIM_HIG_BORDER);
633 gtk_widget_show(table);
634
635 dialog->open_win
636 = gtk_check_button_new_with_mnemonic(_("Ope_n an IM window"));
637 dialog->popup
638 = gtk_check_button_new_with_mnemonic(_("_Pop up a notification"));
639 dialog->send_msg
640 = gtk_check_button_new_with_mnemonic(_("Send a _message"));
641 dialog->exec_cmd
642 = gtk_check_button_new_with_mnemonic(_("E_xecute a command"));
643 dialog->play_sound
644 = gtk_check_button_new_with_mnemonic(_("P_lay a sound"));
645
646 dialog->send_msg_entry = gtk_entry_new();
647 dialog->exec_cmd_entry = gtk_entry_new();
648 dialog->popup_entry = gtk_entry_new();
649 dialog->exec_cmd_browse = gtk_button_new_with_mnemonic(_("Brows_e..."));
650 dialog->play_sound_entry = gtk_entry_new();
651 dialog->play_sound_browse = gtk_button_new_with_mnemonic(_("Br_owse..."));
652 dialog->play_sound_test = gtk_button_new_with_mnemonic(_("Pre_view"));
653
654 gtk_widget_set_sensitive(dialog->send_msg_entry, FALSE);
655 gtk_widget_set_sensitive(dialog->exec_cmd_entry, FALSE);
656 gtk_widget_set_sensitive(dialog->popup_entry, FALSE);
657 gtk_widget_set_sensitive(dialog->exec_cmd_browse, FALSE);
658 gtk_widget_set_sensitive(dialog->play_sound_entry, FALSE);
659 gtk_widget_set_sensitive(dialog->play_sound_browse, FALSE);
660 gtk_widget_set_sensitive(dialog->play_sound_test, FALSE);
661
662 sg = gtk_size_group_new(GTK_SIZE_GROUP_VERTICAL);
663 gtk_size_group_add_widget(sg, dialog->open_win);
664 gtk_size_group_add_widget(sg, dialog->popup);
665 gtk_size_group_add_widget(sg, dialog->popup_entry);
666 gtk_size_group_add_widget(sg, dialog->send_msg);
667 gtk_size_group_add_widget(sg, dialog->send_msg_entry);
668 gtk_size_group_add_widget(sg, dialog->exec_cmd);
669 gtk_size_group_add_widget(sg, dialog->exec_cmd_entry);
670 gtk_size_group_add_widget(sg, dialog->exec_cmd_browse);
671 gtk_size_group_add_widget(sg, dialog->play_sound);
672 gtk_size_group_add_widget(sg, dialog->play_sound_entry);
673 gtk_size_group_add_widget(sg, dialog->play_sound_browse);
674 gtk_size_group_add_widget(sg, dialog->play_sound_test);
675
676 gtk_table_attach(GTK_TABLE(table), dialog->open_win, 0, 1, 0, 1,
677 GTK_FILL, 0, 0, 0);
678 gtk_table_attach(GTK_TABLE(table), dialog->popup, 0, 1, 1, 2,
679 GTK_FILL, 0, 0, 0);
680 gtk_table_attach(GTK_TABLE(table), dialog->popup_entry, 1, 4, 1, 2,
681 GTK_FILL, 0, 0, 0);
682 gtk_table_attach(GTK_TABLE(table), dialog->send_msg, 0, 1, 2, 3,
683 GTK_FILL, 0, 0, 0);
684 gtk_table_attach(GTK_TABLE(table), dialog->send_msg_entry, 1, 4, 2, 3,
685 GTK_FILL, 0, 0, 0);
686 gtk_table_attach(GTK_TABLE(table), dialog->exec_cmd, 0, 1, 3, 4,
687 GTK_FILL, 0, 0, 0);
688 gtk_table_attach(GTK_TABLE(table), dialog->exec_cmd_entry, 1, 2, 3, 4,
689 GTK_FILL, 0, 0, 0);
690 gtk_table_attach(GTK_TABLE(table), dialog->exec_cmd_browse, 2, 3, 3, 4,
691 GTK_FILL | GTK_EXPAND, 0, 0, 0);
692 gtk_table_attach(GTK_TABLE(table), dialog->play_sound, 0, 1, 4, 5,
693 GTK_FILL, 0, 0, 0);
694 gtk_table_attach(GTK_TABLE(table), dialog->play_sound_entry, 1, 2, 4, 5,
695 GTK_FILL, 0, 0, 0);
696 gtk_table_attach(GTK_TABLE(table), dialog->play_sound_browse, 2, 3, 4, 5,
697 GTK_FILL | GTK_EXPAND, 0, 0, 0);
698 gtk_table_attach(GTK_TABLE(table), dialog->play_sound_test, 3, 4, 4, 5,
699 GTK_FILL | GTK_EXPAND, 0, 0, 0);
700
701 gtk_table_set_row_spacings(GTK_TABLE(table), GAIM_HIG_BOX_SPACE / 2);
702
703 gtk_widget_show(dialog->open_win);
704 gtk_widget_show(dialog->popup);
705 gtk_widget_show(dialog->popup_entry);
706 gtk_widget_show(dialog->send_msg);
707 gtk_widget_show(dialog->send_msg_entry);
708 gtk_widget_show(dialog->exec_cmd);
709 gtk_widget_show(dialog->exec_cmd_entry);
710 gtk_widget_show(dialog->exec_cmd_browse);
711 gtk_widget_show(dialog->play_sound);
712 gtk_widget_show(dialog->play_sound_entry);
713 gtk_widget_show(dialog->play_sound_browse);
714 gtk_widget_show(dialog->play_sound_test);
715
716 g_signal_connect(G_OBJECT(dialog->message_recv), "clicked",
717 G_CALLBACK(message_recv_toggle),
718 dialog->send_msg);
719
720 g_signal_connect(G_OBJECT(dialog->send_msg), "clicked",
721 G_CALLBACK(gaim_gtk_toggle_sensitive),
722 dialog->send_msg_entry);
723
724 g_signal_connect(G_OBJECT(dialog->popup), "clicked",
725 G_CALLBACK(gaim_gtk_toggle_sensitive),
726 dialog->popup_entry);
727
728 exec_widgets = g_ptr_array_new();
729 g_ptr_array_add(exec_widgets,dialog->exec_cmd_entry);
730 g_ptr_array_add(exec_widgets,dialog->exec_cmd_browse);
731
732 g_signal_connect(G_OBJECT(dialog->exec_cmd), "clicked",
733 G_CALLBACK(gaim_gtk_toggle_sensitive_array),
734 exec_widgets);
735 g_signal_connect(G_OBJECT(dialog->exec_cmd_browse), "clicked",
736 G_CALLBACK(filesel),
737 dialog->exec_cmd_entry);
738
739 sound_widgets = g_ptr_array_new();
740 g_ptr_array_add(sound_widgets,dialog->play_sound_entry);
741 g_ptr_array_add(sound_widgets,dialog->play_sound_browse);
742 g_ptr_array_add(sound_widgets,dialog->play_sound_test);
743
744 g_signal_connect(G_OBJECT(dialog->play_sound), "clicked",
745 G_CALLBACK(gaim_gtk_toggle_sensitive_array),
746 sound_widgets);
747 g_signal_connect(G_OBJECT(dialog->play_sound_browse), "clicked",
748 G_CALLBACK(filesel),
749 dialog->play_sound_entry);
750 g_signal_connect(G_OBJECT(dialog->play_sound_test), "clicked",
751 G_CALLBACK(pounce_test_sound),
752 dialog->play_sound_entry);
753
754 g_signal_connect(G_OBJECT(dialog->send_msg_entry), "activate",
755 G_CALLBACK(save_pounce_cb), dialog);
756 g_signal_connect(G_OBJECT(dialog->popup_entry), "activate",
757 G_CALLBACK(save_pounce_cb), dialog);
758 g_signal_connect(G_OBJECT(dialog->exec_cmd_entry), "activate",
759 G_CALLBACK(save_pounce_cb), dialog);
760 g_signal_connect(G_OBJECT(dialog->play_sound_entry), "activate",
761 G_CALLBACK(save_pounce_cb), dialog);
762
763 /* Create the "Options" frame. */
764 frame = gaim_gtk_make_frame(vbox2, _("Options"));
765
766 table = gtk_table_new(2, 1, FALSE);
767 gtk_container_add(GTK_CONTAINER(frame), table);
768 gtk_table_set_col_spacings(GTK_TABLE(table), GAIM_HIG_BORDER);
769 gtk_widget_show(table);
770
771 dialog->on_away =
772 gtk_check_button_new_with_mnemonic(_("P_ounce only when my status is not available"));
773 gtk_table_attach(GTK_TABLE(table), dialog->on_away, 0, 1, 0, 1,
774 GTK_FILL, 0, 0, 0);
775
776 dialog->save_pounce = gtk_check_button_new_with_mnemonic(
777 _("_Recurring"));
778 gtk_table_attach(GTK_TABLE(table), dialog->save_pounce, 0, 1, 1, 2,
779 GTK_FILL, 0, 0, 0);
780
781 gtk_widget_show(dialog->on_away);
782 gtk_widget_show(dialog->save_pounce);
783
784 /* Now the button box! */
785 bbox = gtk_hbutton_box_new();
786 gtk_box_set_spacing(GTK_BOX(bbox), GAIM_HIG_BOX_SPACE);
787 gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
788 gtk_box_pack_end(GTK_BOX(vbox1), bbox, FALSE, FALSE, 0);
789 gtk_widget_show(bbox);
790
791 /* Delete button */
792 button = gtk_button_new_from_stock(GTK_STOCK_DELETE);
793 gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
794 if (cur_pounce == NULL)
795 gtk_widget_set_sensitive(button, FALSE);
796 gtk_widget_show(button);
797
798 g_signal_connect(G_OBJECT(button), "clicked",
799 G_CALLBACK(delete_cb), dialog);
800
801 /* Cancel button */
802 button = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
803 gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
804 gtk_widget_show(button);
805
806 g_signal_connect(G_OBJECT(button), "clicked",
807 G_CALLBACK(cancel_cb), dialog);
808
809 /* Save button */
810 dialog->save_button = button = gtk_button_new_from_stock(GTK_STOCK_SAVE);
811 gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
812 gtk_widget_show(button);
813
814 g_signal_connect(G_OBJECT(button), "clicked",
815 G_CALLBACK(save_pounce_cb), dialog);
816
817 if (*gtk_entry_get_text(GTK_ENTRY(dialog->buddy_entry)) == '\0')
818 gtk_widget_set_sensitive(button, FALSE);
819
820 /* Setup drag-and-drop */
821 gtk_drag_dest_set(window,
822 GTK_DEST_DEFAULT_MOTION |
823 GTK_DEST_DEFAULT_DROP,
824 dnd_targets,
825 sizeof(dnd_targets) / sizeof(GtkTargetEntry),
826 GDK_ACTION_COPY);
827 gtk_drag_dest_set(dialog->buddy_entry,
828 GTK_DEST_DEFAULT_MOTION |
829 GTK_DEST_DEFAULT_DROP,
830 dnd_targets,
831 sizeof(dnd_targets) / sizeof(GtkTargetEntry),
832 GDK_ACTION_COPY);
833
834 g_signal_connect(G_OBJECT(window), "drag_data_received",
835 G_CALLBACK(pounce_dnd_recv), dialog);
836 g_signal_connect(G_OBJECT(dialog->buddy_entry), "drag_data_received",
837 G_CALLBACK(pounce_dnd_recv), dialog);
838
839 /* Set the values of stuff. */
840 if (cur_pounce != NULL)
841 {
842 GaimPounceEvent events = gaim_pounce_get_events(cur_pounce);
843 GaimPounceOption options = gaim_pounce_get_options(cur_pounce);
844 const char *value;
845
846 /* Options */
847 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dialog->on_away),
848 (options & GAIM_POUNCE_OPTION_AWAY));
849
850 /* Events */
851 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dialog->signon),
852 (events & GAIM_POUNCE_SIGNON));
853 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dialog->signoff),
854 (events & GAIM_POUNCE_SIGNOFF));
855 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dialog->away),
856 (events & GAIM_POUNCE_AWAY));
857 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dialog->away_return),
858 (events & GAIM_POUNCE_AWAY_RETURN));
859 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dialog->idle),
860 (events & GAIM_POUNCE_IDLE));
861 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dialog->idle_return),
862 (events & GAIM_POUNCE_IDLE_RETURN));
863 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dialog->typing),
864 (events & GAIM_POUNCE_TYPING));
865 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dialog->stop_typing),
866 (events & GAIM_POUNCE_TYPING_STOPPED));
867 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dialog->message_recv),
868 (events & GAIM_POUNCE_MESSAGE_RECEIVED));
869
870 /* Actions */
871 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dialog->open_win),
872 gaim_pounce_action_is_enabled(cur_pounce, "open-window"));
873 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dialog->popup),
874 gaim_pounce_action_is_enabled(cur_pounce, "popup-notify"));
875 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dialog->send_msg),
876 gaim_pounce_action_is_enabled(cur_pounce, "send-message"));
877 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dialog->exec_cmd),
878 gaim_pounce_action_is_enabled(cur_pounce, "execute-command"));
879 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dialog->play_sound),
880 gaim_pounce_action_is_enabled(cur_pounce, "play-sound"));
881
882 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dialog->save_pounce),
883 gaim_pounce_get_save(cur_pounce));
884
885 if ((value = gaim_pounce_action_get_attribute(cur_pounce,
886 "send-message",
887 "message")) != NULL)
888 {
889 gtk_entry_set_text(GTK_ENTRY(dialog->send_msg_entry), value);
890 }
891
892 if ((value = gaim_pounce_action_get_attribute(cur_pounce,
893 "popup-notify",
894 "reason")) != NULL)
895 {
896 gtk_entry_set_text(GTK_ENTRY(dialog->popup_entry), value);
897 }
898
899 if ((value = gaim_pounce_action_get_attribute(cur_pounce,
900 "execute-command",
901 "command")) != NULL)
902 {
903 gtk_entry_set_text(GTK_ENTRY(dialog->exec_cmd_entry), value);
904 }
905
906 if ((value = gaim_pounce_action_get_attribute(cur_pounce,
907 "play-sound",
908 "filename")) != NULL)
909 {
910 gtk_entry_set_text(GTK_ENTRY(dialog->play_sound_entry), value);
911 }
912 }
913 else
914 {
915 GaimBuddy *buddy = NULL;
916
917 if (name != NULL)
918 buddy = gaim_find_buddy(account, name);
919
920 /* Set some defaults */
921 if (buddy == NULL)
922 {
923 gtk_toggle_button_set_active(
924 GTK_TOGGLE_BUTTON(dialog->signon), TRUE);
925 }
926 else
927 {
928 if (!GAIM_BUDDY_IS_ONLINE(buddy))
929 {
930 gtk_toggle_button_set_active(
931 GTK_TOGGLE_BUTTON(dialog->signon), TRUE);
932 }
933 else
934 {
935 gboolean default_set = FALSE;
936 GaimPresence *presence = gaim_buddy_get_presence(buddy);
937
938 if (gaim_presence_is_idle(presence))
939 {
940 gtk_toggle_button_set_active(
941 GTK_TOGGLE_BUTTON(dialog->idle_return), TRUE);
942
943 default_set = TRUE;
944 }
945
946 if (!gaim_presence_is_available(presence))
947 {
948 gtk_toggle_button_set_active(
949 GTK_TOGGLE_BUTTON(dialog->away_return), TRUE);
950
951 default_set = TRUE;
952 }
953
954 if (!default_set)
955 {
956 gtk_toggle_button_set_active(
957 GTK_TOGGLE_BUTTON(dialog->signon), TRUE);
958 }
959 }
960 }
961
962 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dialog->open_win),
963 gaim_prefs_get_bool("/gaim/gtk/pounces/default_actions/open-window"));
964 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dialog->popup),
965 gaim_prefs_get_bool("/gaim/gtk/pounces/default_actions/popup-notify"));
966 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dialog->send_msg),
967 gaim_prefs_get_bool("/gaim/gtk/pounces/default_actions/send-message"));
968 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dialog->exec_cmd),
969 gaim_prefs_get_bool("/gaim/gtk/pounces/default_actions/execute-command"));
970 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dialog->play_sound),
971 gaim_prefs_get_bool("/gaim/gtk/pounces/default_actions/play-sound"));
972 }
973
974 gtk_widget_show_all(vbox2);
975 gtk_widget_show(window);
976 }
977
978 static gboolean
979 pounces_manager_configure_cb(GtkWidget *widget, GdkEventConfigure *event, PouncesManager *dialog)
980 {
981 if (GTK_WIDGET_VISIBLE(widget)) {
982 gaim_prefs_set_int("/gaim/gtk/pounces/dialog/width", event->width);
983 gaim_prefs_set_int("/gaim/gtk/pounces/dialog/height", event->height);
984 }
985
986 return FALSE;
987 }
988
989 static gboolean
990 pounces_manager_find_pounce(GtkTreeIter *iter, GaimPounce *pounce)
991 {
992 GtkTreeModel *model = GTK_TREE_MODEL(pounces_manager->model);
993 GaimPounce *p;
994
995 if (!gtk_tree_model_get_iter_first(model, iter))
996 return FALSE;
997
998 gtk_tree_model_get(model, iter, POUNCES_MANAGER_COLUMN_POUNCE, &p, -1);
999 if (pounce == p)
1000 return TRUE;
1001
1002 while (gtk_tree_model_iter_next(model, iter))
1003 {
1004 gtk_tree_model_get(model, iter, POUNCES_MANAGER_COLUMN_POUNCE, &p, -1);
1005 if (pounce == p)
1006 return TRUE;
1007 }
1008
1009 return FALSE;
1010 }
1011
1012 static gboolean
1013 pounces_manager_destroy_cb(GtkWidget *widget, GdkEvent *event, gpointer user_data)
1014 {
1015 PouncesManager *dialog = user_data;
1016
1017 dialog->window = NULL;
1018 gaim_gtk_pounces_manager_hide();
1019
1020 return FALSE;
1021 }
1022
1023 #if !GTK_CHECK_VERSION(2,2,0)
1024 static void
1025 count_selected_helper(GtkTreeModel *model, GtkTreePath *path,
1026 GtkTreeIter *iter, gpointer user_data)
1027 {
1028 (*(gint *)user_data)++;
1029 }
1030 #endif
1031
1032 static void
1033 pounces_manager_connection_cb(GaimConnection *gc, GtkWidget *add_button)
1034 {
1035 gtk_widget_set_sensitive(add_button, (gaim_connections_get_all() != NULL));
1036 }
1037
1038 static void
1039 pounces_manager_add_cb(GtkButton *button, gpointer user_data)
1040 {
1041 gaim_gtk_pounce_editor_show(NULL, NULL, NULL);
1042 }
1043
1044 static void
1045 pounces_manager_modify_foreach(GtkTreeModel *model, GtkTreePath *path,
1046 GtkTreeIter *iter, gpointer user_data)
1047 {
1048 GaimPounce *pounce;
1049
1050 gtk_tree_model_get(model, iter, POUNCES_MANAGER_COLUMN_POUNCE, &pounce, -1);
1051 gaim_gtk_pounce_editor_show(NULL, NULL, pounce);
1052 }
1053
1054 static void
1055 pounces_manager_modify_cb(GtkButton *button, gpointer user_data)
1056 {
1057 PouncesManager *dialog = user_data;
1058 GtkTreeSelection *selection;
1059
1060 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dialog->treeview));
1061
1062 gtk_tree_selection_selected_foreach(selection, pounces_manager_modify_foreach, user_data);
1063 }
1064
1065 static void
1066 pounces_manager_delete_confirm_cb(GaimPounce *pounce)
1067 {
1068 GtkTreeIter iter;
1069
1070 if (pounces_manager_find_pounce(&iter, pounce))
1071 gtk_list_store_remove(pounces_manager->model, &iter);
1072
1073 gaim_pounce_destroy(pounce);
1074 }
1075
1076 static void
1077 pounces_manager_delete_foreach(GtkTreeModel *model, GtkTreePath *path,
1078 GtkTreeIter *iter, gpointer user_data)
1079 {
1080 GaimPounce *pounce;
1081 GaimAccount *account;
1082 const char *pouncer, *pouncee;
1083 char *buf;
1084
1085 gtk_tree_model_get(model, iter, POUNCES_MANAGER_COLUMN_POUNCE, &pounce, -1);
1086 account = gaim_pounce_get_pouncer(pounce);
1087 pouncer = gaim_account_get_username(account);
1088 pouncee = gaim_pounce_get_pouncee(pounce);
1089
1090 buf = g_strdup_printf(_("Are you sure you want to delete the pounce on %s for %s?"), pouncee, pouncer);
1091 gaim_request_action(NULL, NULL, buf, NULL, 0, pounce, 2,
1092 _("Delete"), pounces_manager_delete_confirm_cb,
1093 _("Cancel"), g_free);
1094 g_free(buf);
1095 }
1096
1097 static void
1098 pounces_manager_delete_cb(GtkButton *button, gpointer user_data)
1099 {
1100 PouncesManager *dialog = user_data;
1101 GtkTreeSelection *selection;
1102
1103 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dialog->treeview));
1104
1105 gtk_tree_selection_selected_foreach(selection, pounces_manager_delete_foreach, user_data);
1106 }
1107
1108 static void
1109 pounces_manager_close_cb(GtkButton *button, gpointer user_data)
1110 {
1111 gaim_gtk_pounces_manager_hide();
1112 }
1113
1114 static void
1115 pounce_selected_cb(GtkTreeSelection *sel, gpointer user_data)
1116 {
1117 PouncesManager *dialog = user_data;
1118 int num_selected = 0;
1119
1120 #if GTK_CHECK_VERSION(2,2,0)
1121 num_selected = gtk_tree_selection_count_selected_rows(sel);
1122 #else
1123 gtk_tree_selection_selected_foreach(sel, count_selected_helper, &num_selected);
1124 #endif
1125
1126 gtk_widget_set_sensitive(dialog->modify_button, (num_selected > 0));
1127 gtk_widget_set_sensitive(dialog->delete_button, (num_selected > 0));
1128 }
1129
1130 static gboolean
1131 pounce_double_click_cb(GtkTreeView *treeview, GdkEventButton *event, gpointer user_data)
1132 {
1133 PouncesManager *dialog = user_data;
1134 GtkTreePath *path;
1135 GtkTreeIter iter;
1136 GaimPounce *pounce;
1137
1138 /* Figure out which node was clicked */
1139 if (!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(dialog->treeview), event->x, event->y, &path, NULL, NULL, NULL))
1140 return FALSE;
1141 gtk_tree_model_get_iter(GTK_TREE_MODEL(dialog->model), &iter, path);
1142 gtk_tree_path_free(path);
1143 gtk_tree_model_get(GTK_TREE_MODEL(dialog->model), &iter, POUNCES_MANAGER_COLUMN_POUNCE, &pounce, -1);
1144
1145 if ((pounce != NULL) && (event->button == 1) &&
1146 (event->type == GDK_2BUTTON_PRESS))
1147 {
1148 gaim_gtk_pounce_editor_show(NULL, NULL, pounce);
1149 return TRUE;
1150 }
1151
1152 return FALSE;
1153 }
1154
1155 static void
1156 pounces_manager_recurring_cb(GtkCellRendererToggle *renderer, gchar *path_str,
1157 gpointer user_data)
1158 {
1159 PouncesManager *dialog = user_data;
1160 GaimPounce *pounce;
1161 gboolean recurring;
1162 GtkTreeModel *model = GTK_TREE_MODEL(dialog->model);
1163 GtkTreeIter iter;
1164
1165 gtk_tree_model_get_iter_from_string(model, &iter, path_str);
1166 gtk_tree_model_get(model, &iter,
1167 POUNCES_MANAGER_COLUMN_POUNCE, &pounce,
1168 POUNCES_MANAGER_COLUMN_RECURRING, &recurring,
1169 -1);
1170
1171 gaim_pounce_set_save(pounce, !recurring);
1172
1173 update_pounces();
1174 }
1175
1176 static gboolean
1177 search_func(GtkTreeModel *model, gint column, const gchar *key, GtkTreeIter *iter, gpointer search_data)
1178 {
1179 gboolean result;
1180 char *haystack;
1181
1182 gtk_tree_model_get(model, iter, column, &haystack, -1);
1183
1184 result = (gaim_strcasestr(haystack, key) == NULL);
1185
1186 g_free(haystack);
1187
1188 return result;
1189 }
1190
1191 static GtkWidget *
1192 create_pounces_list(PouncesManager *dialog)
1193 {
1194 GtkWidget *sw;
1195 GtkWidget *treeview;
1196 GtkTreeSelection *sel;
1197 GtkTreeViewColumn *column;
1198 GtkCellRenderer *renderer;
1199
1200 /* Create the scrolled window */
1201 sw = gtk_scrolled_window_new(0, 0);
1202 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
1203 GTK_POLICY_AUTOMATIC,
1204 GTK_POLICY_ALWAYS);
1205 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
1206 GTK_SHADOW_IN);
1207 gtk_widget_show(sw);
1208
1209 /* Create the list model */
1210 dialog->model = gtk_list_store_new(POUNCES_MANAGER_NUM_COLUMNS,
1211 G_TYPE_POINTER,
1212 GDK_TYPE_PIXBUF,
1213 G_TYPE_STRING,
1214 G_TYPE_STRING,
1215 G_TYPE_BOOLEAN
1216 );
1217
1218 /* Create the treeview */
1219 treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(dialog->model));
1220 dialog->treeview = treeview;
1221 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(treeview), TRUE);
1222
1223 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
1224 gtk_tree_selection_set_mode(sel, GTK_SELECTION_MULTIPLE);
1225 g_signal_connect(G_OBJECT(sel), "changed",
1226 G_CALLBACK(pounce_selected_cb), dialog);
1227
1228 /* Handle double-clicking */
1229 g_signal_connect(G_OBJECT(treeview), "button_press_event",
1230 G_CALLBACK(pounce_double_click_cb), dialog);
1231
1232
1233 gtk_container_add(GTK_CONTAINER(sw), treeview);
1234 gtk_widget_show(treeview);
1235
1236 /* Pouncee Column */
1237 column = gtk_tree_view_column_new();
1238 gtk_tree_view_column_set_title(column, _("Pounce Target"));
1239 gtk_tree_view_column_set_resizable(column, TRUE);
1240 gtk_tree_view_column_set_min_width(column, 200);
1241 gtk_tree_view_column_set_sort_column_id(column,
1242 POUNCES_MANAGER_COLUMN_TARGET);
1243 gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column);
1244
1245 /* Icon */
1246 renderer = gtk_cell_renderer_pixbuf_new();
1247 gtk_tree_view_column_pack_start(column, renderer, FALSE);
1248 gtk_tree_view_column_add_attribute(column, renderer, "pixbuf",
1249 POUNCES_MANAGER_COLUMN_ICON);
1250
1251 /* Pouncee */
1252 renderer = gtk_cell_renderer_text_new();
1253 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1254 gtk_tree_view_column_add_attribute(column, renderer, "text",
1255 POUNCES_MANAGER_COLUMN_TARGET);
1256
1257
1258 /* Account Column */
1259 column = gtk_tree_view_column_new();
1260 gtk_tree_view_column_set_title(column, _("Account"));
1261 gtk_tree_view_column_set_resizable(column, TRUE);
1262 gtk_tree_view_column_set_min_width(column, 200);
1263 gtk_tree_view_column_set_sort_column_id(column,
1264 POUNCES_MANAGER_COLUMN_ACCOUNT);
1265 gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column);
1266 renderer = gtk_cell_renderer_text_new();
1267 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1268 gtk_tree_view_column_add_attribute(column, renderer, "text",
1269 POUNCES_MANAGER_COLUMN_ACCOUNT);
1270
1271 /* Recurring Column */
1272 renderer = gtk_cell_renderer_toggle_new();
1273 column = gtk_tree_view_column_new_with_attributes(_("Recurring"), renderer,
1274 "active", POUNCES_MANAGER_COLUMN_RECURRING, NULL);
1275 gtk_tree_view_column_set_sort_column_id(column,
1276 POUNCES_MANAGER_COLUMN_RECURRING);
1277 gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column);
1278 g_signal_connect(G_OBJECT(renderer), "toggled",
1279 G_CALLBACK(pounces_manager_recurring_cb), dialog);
1280
1281 /* Enable CTRL+F searching */
1282 gtk_tree_view_set_search_column(GTK_TREE_VIEW(treeview), POUNCES_MANAGER_COLUMN_TARGET);
1283 gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(treeview), search_func, NULL, NULL);
1284
1285 /* Sort the pouncee column by default */
1286 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(dialog->model),
1287 POUNCES_MANAGER_COLUMN_TARGET,
1288 GTK_SORT_ASCENDING);
1289
1290 /* Populate list */
1291 populate_pounces_list(dialog);
1292
1293 return sw;
1294 }
1295
1296 void
1297 gaim_gtk_pounces_manager_show(void)
1298 {
1299 PouncesManager *dialog;
1300 GtkWidget *bbox;
1301 GtkWidget *button;
1302 GtkWidget *list;
1303 GtkWidget *vbox;
1304 GtkWidget *win;
1305 int width, height;
1306
1307 if (pounces_manager != NULL) {
1308 gtk_window_present(GTK_WINDOW(pounces_manager->window));
1309 return;
1310 }
1311
1312 pounces_manager = dialog = g_new0(PouncesManager, 1);
1313
1314 width = gaim_prefs_get_int("/gaim/gtk/pounces/dialog/width");
1315 height = gaim_prefs_get_int("/gaim/gtk/pounces/dialog/height");
1316
1317 dialog->window = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1318 gtk_window_set_default_size(GTK_WINDOW(win), width, height);
1319 gtk_window_set_role(GTK_WINDOW(win), "pounces");
1320 gtk_window_set_title(GTK_WINDOW(win), _("Buddy Pounces"));
1321 gtk_container_set_border_width(GTK_CONTAINER(win), GAIM_HIG_BORDER);
1322
1323 g_signal_connect(G_OBJECT(win), "delete_event",
1324 G_CALLBACK(pounces_manager_destroy_cb), dialog);
1325 g_signal_connect(G_OBJECT(win), "configure_event",
1326 G_CALLBACK(pounces_manager_configure_cb), dialog);
1327
1328 /* Setup the vbox */
1329 vbox = gtk_vbox_new(FALSE, GAIM_HIG_BORDER);
1330 gtk_container_add(GTK_CONTAINER(win), vbox);
1331 gtk_widget_show(vbox);
1332
1333 /* List of saved buddy pounces */
1334 list = create_pounces_list(dialog);
1335 gtk_box_pack_start(GTK_BOX(vbox), list, TRUE, TRUE, 0);
1336
1337 /* Button box. */
1338 bbox = gtk_hbutton_box_new();
1339 gtk_box_set_spacing(GTK_BOX(bbox), GAIM_HIG_BOX_SPACE);
1340 gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
1341 gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, TRUE, 0);
1342 gtk_widget_show(bbox);
1343
1344 /* Add button */
1345 button = gtk_button_new_from_stock(GTK_STOCK_ADD);
1346 gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
1347 gtk_widget_set_sensitive(button, (gaim_connections_get_all() != NULL));
1348 gaim_signal_connect(gaim_connections_get_handle(), "signed-on",
1349 pounces_manager, GAIM_CALLBACK(pounces_manager_connection_cb), button);
1350 gaim_signal_connect(gaim_connections_get_handle(), "signed-off",
1351 pounces_manager, GAIM_CALLBACK(pounces_manager_connection_cb), button);
1352 gtk_widget_show(button);
1353
1354 g_signal_connect(G_OBJECT(button), "clicked",
1355 G_CALLBACK(pounces_manager_add_cb), dialog);
1356
1357 /* Modify button */
1358 button = gtk_button_new_from_stock(GAIM_STOCK_MODIFY);
1359 dialog->modify_button = button;
1360 gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
1361 gtk_widget_set_sensitive(button, FALSE);
1362 gtk_widget_show(button);
1363
1364 g_signal_connect(G_OBJECT(button), "clicked",
1365 G_CALLBACK(pounces_manager_modify_cb), dialog);
1366
1367 /* Delete button */
1368 button = gtk_button_new_from_stock(GTK_STOCK_DELETE);
1369 dialog->delete_button = button;
1370 gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
1371 gtk_widget_set_sensitive(button, FALSE);
1372 gtk_widget_show(button);
1373
1374 g_signal_connect(G_OBJECT(button), "clicked",
1375 G_CALLBACK(pounces_manager_delete_cb), dialog);
1376
1377 /* Close button */
1378 button = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
1379 gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
1380 gtk_widget_show(button);
1381
1382 g_signal_connect(G_OBJECT(button), "clicked",
1383 G_CALLBACK(pounces_manager_close_cb), dialog);
1384
1385 gtk_widget_show(win);
1386 }
1387
1388 void
1389 gaim_gtk_pounces_manager_hide(void)
1390 {
1391 if (pounces_manager == NULL)
1392 return;
1393
1394 if (pounces_manager->window != NULL)
1395 gtk_widget_destroy(pounces_manager->window);
1396
1397 gaim_signals_disconnect_by_handle(pounces_manager);
1398
1399 g_free(pounces_manager);
1400 pounces_manager = NULL;
1401 }
1402
1403 static void
1404 pounce_cb(GaimPounce *pounce, GaimPounceEvent events, void *data)
1405 {
1406 GaimConversation *conv;
1407 GaimAccount *account;
1408 GaimBuddy *buddy;
1409 const char *pouncee;
1410 const char *alias;
1411
1412 pouncee = gaim_pounce_get_pouncee(pounce);
1413 account = gaim_pounce_get_pouncer(pounce);
1414
1415 buddy = gaim_find_buddy(account, pouncee);
1416 if (buddy != NULL)
1417 {
1418 alias = gaim_buddy_get_alias(buddy);
1419 if (alias == NULL)
1420 alias = pouncee;
1421 }
1422 else
1423 alias = pouncee;
1424
1425 if (gaim_pounce_action_is_enabled(pounce, "open-window"))
1426 {
1427 conv = gaim_find_conversation_with_account(GAIM_CONV_TYPE_IM, pouncee, account);
1428
1429 if (conv == NULL)
1430 conv = gaim_conversation_new(GAIM_CONV_TYPE_IM, account, pouncee);
1431 }
1432
1433 if (gaim_pounce_action_is_enabled(pounce, "popup-notify"))
1434 {
1435 time_t now = time(NULL);
1436 char date[64];
1437 char *tmp;
1438 const char *name_shown;
1439 const char *reason;
1440 reason = gaim_pounce_action_get_attribute(pounce, "popup-notify",
1441 "reason");
1442
1443 /*
1444 * Here we place the protocol name in the pounce dialog to lessen
1445 * confusion about what protocol a pounce is for.
1446 */
1447 tmp = g_strdup_printf(
1448 (events & GAIM_POUNCE_TYPING) ?
1449 _("%s has started typing to you (%s)") :
1450 (events & GAIM_POUNCE_SIGNON) ?
1451 _("%s has signed on (%s)") :
1452 (events & GAIM_POUNCE_IDLE_RETURN) ?
1453 _("%s has returned from being idle (%s)") :
1454 (events & GAIM_POUNCE_AWAY_RETURN) ?
1455 _("%s has returned from being away (%s)") :
1456 (events & GAIM_POUNCE_TYPING_STOPPED) ?
1457 _("%s has stopped typing to you (%s)") :
1458 (events & GAIM_POUNCE_SIGNOFF) ?
1459 _("%s has signed off (%s)") :
1460 (events & GAIM_POUNCE_IDLE) ?
1461 _("%s has become idle (%s)") :
1462 (events & GAIM_POUNCE_AWAY) ?
1463 _("%s has gone away. (%s)") :
1464 (events & GAIM_POUNCE_MESSAGE_RECEIVED) ?
1465 _("%s has sent you a message. (%s)") :
1466 _("Unknown pounce event. Please report this!"),
1467 alias, gaim_account_get_protocol_name(account));
1468
1469 /*
1470 * Ok here is where I change the second argument, title, from
1471 * NULL to the account alias if we have it or the account
1472 * name if that's all we have
1473 */
1474 if ((name_shown = gaim_account_get_alias(account)) == NULL)
1475 name_shown = gaim_account_get_username(account);
1476
1477 strftime(date, sizeof(date), "%c", localtime(&now));
1478 if (reason == NULL)
1479 gaim_notify_info(NULL, name_shown, tmp, date);
1480 else
1481 {
1482 char *tmp2 = g_strdup_printf("%s\n\n%s", reason, date);
1483 gaim_notify_info(NULL, name_shown, tmp, tmp2);
1484 g_free(tmp2);
1485 }
1486 g_free(tmp);
1487 }
1488
1489 if (gaim_pounce_action_is_enabled(pounce, "send-message"))
1490 {
1491 const char *message;
1492
1493 message = gaim_pounce_action_get_attribute(pounce, "send-message",
1494 "message");
1495
1496 if (message != NULL)
1497 {
1498 conv = gaim_find_conversation_with_account(GAIM_CONV_TYPE_IM, pouncee, account);
1499
1500 if (conv == NULL)
1501 conv = gaim_conversation_new(GAIM_CONV_TYPE_IM, account, pouncee);
1502
1503 gaim_conversation_write(conv, NULL, message,
1504 GAIM_MESSAGE_SEND, time(NULL));
1505
1506 serv_send_im(account->gc, (char *)pouncee, (char *)message, 0);
1507 }
1508 }
1509
1510 if (gaim_pounce_action_is_enabled(pounce, "execute-command"))
1511 {
1512 const char *command;
1513
1514 command = gaim_pounce_action_get_attribute(pounce,
1515 "execute-command", "command");
1516
1517 if (command != NULL)
1518 {
1519 #ifndef _WIN32
1520 char *localecmd = g_locale_from_utf8(command, -1, NULL,
1521 NULL, NULL);
1522
1523 if (localecmd != NULL)
1524 {
1525 int pid = fork();
1526
1527 if (pid == 0) {
1528 char *args[4];
1529
1530 args[0] = "sh";
1531 args[1] = "-c";
1532 args[2] = (char *)localecmd;
1533 args[3] = NULL;
1534
1535 execvp(args[0], args);
1536
1537 _exit(0);
1538 }
1539 g_free(localecmd);
1540 }
1541 #else /* !_WIN32 */
1542 PROCESS_INFORMATION pi;
1543 BOOL retval;
1544 gchar *message = NULL;
1545
1546 memset(&pi, 0, sizeof(pi));
1547
1548 if (G_WIN32_HAVE_WIDECHAR_API ()) {
1549 STARTUPINFOW si;
1550 wchar_t *wc_cmd = g_utf8_to_utf16(command,
1551 -1, NULL, NULL, NULL);
1552
1553 memset(&si, 0 , sizeof(si));
1554 si.cb = sizeof(si);
1555
1556 retval = CreateProcessW(NULL, wc_cmd, NULL,
1557 NULL, 0, 0, NULL, NULL,
1558 &si, &pi);
1559 g_free(wc_cmd);
1560 } else {
1561 STARTUPINFOA si;
1562 char *l_cmd = g_locale_from_utf8(command,
1563 -1, NULL, NULL, NULL);
1564
1565 memset(&si, 0 , sizeof(si));
1566 si.cb = sizeof(si);
1567
1568 retval = CreateProcessA(NULL, l_cmd, NULL,
1569 NULL, 0, 0, NULL, NULL,
1570 &si, &pi);
1571 g_free(l_cmd);
1572 }
1573
1574 if (retval) {
1575 CloseHandle(pi.hProcess);
1576 CloseHandle(pi.hThread);
1577 } else {
1578 message = g_win32_error_message(GetLastError());
1579 }
1580
1581 gaim_debug_info("pounce",
1582 "Pounce execute command called for: "
1583 "%s\n%s%s%s",
1584 command,
1585 retval ? "" : "Error: ",
1586 retval ? "" : message,
1587 retval ? "" : "\n");
1588 g_free(message);
1589 #endif /* !_WIN32 */
1590 }
1591 }
1592
1593 if (gaim_pounce_action_is_enabled(pounce, "play-sound"))
1594 {
1595 const char *sound;
1596
1597 sound = gaim_pounce_action_get_attribute(pounce,
1598 "play-sound", "filename");
1599
1600 if (sound != NULL)
1601 gaim_sound_play_file(sound, account);
1602 else
1603 gaim_sound_play_event(GAIM_SOUND_POUNCE_DEFAULT, account);
1604 }
1605 }
1606
1607 static void
1608 free_pounce(GaimPounce *pounce)
1609 {
1610 update_pounces();
1611 }
1612
1613 static void
1614 new_pounce(GaimPounce *pounce)
1615 {
1616 gaim_pounce_action_register(pounce, "open-window");
1617 gaim_pounce_action_register(pounce, "popup-notify");
1618 gaim_pounce_action_register(pounce, "send-message");
1619 gaim_pounce_action_register(pounce, "execute-command");
1620 gaim_pounce_action_register(pounce, "play-sound");
1621
1622 update_pounces();
1623 }
1624
1625 void *
1626 gaim_gtk_pounces_get_handle() {
1627 static int handle;
1628
1629 return &handle;
1630 }
1631
1632 void
1633 gaim_gtk_pounces_init(void)
1634 {
1635 gaim_pounces_register_handler(GAIM_GTK_UI, pounce_cb, new_pounce,
1636 free_pounce);
1637
1638 gaim_prefs_add_none("/gaim/gtk/pounces");
1639 gaim_prefs_add_none("/gaim/gtk/pounces/default_actions");
1640 gaim_prefs_add_bool("/gaim/gtk/pounces/default_actions/open-window",
1641 FALSE);
1642 gaim_prefs_add_bool("/gaim/gtk/pounces/default_actions/popup-notify",
1643 TRUE);
1644 gaim_prefs_add_bool("/gaim/gtk/pounces/default_actions/send-message",
1645 FALSE);
1646 gaim_prefs_add_bool("/gaim/gtk/pounces/default_actions/execute-command",
1647 FALSE);
1648 gaim_prefs_add_bool("/gaim/gtk/pounces/default_actions/play-sound",
1649 FALSE);
1650 gaim_prefs_add_none("/gaim/gtk/pounces/dialog");
1651 gaim_prefs_add_int("/gaim/gtk/pounces/dialog/width", 520);
1652 gaim_prefs_add_int("/gaim/gtk/pounces/dialog/height", 321);
1653
1654 gaim_signal_connect(gaim_connections_get_handle(), "signed-on",
1655 gaim_gtk_pounces_get_handle(),
1656 GAIM_CALLBACK(signed_on_off_cb), NULL);
1657 gaim_signal_connect(gaim_connections_get_handle(), "signed-off",
1658 gaim_gtk_pounces_get_handle(),
1659 GAIM_CALLBACK(signed_on_off_cb), NULL);
1660 }

mercurial