src/conversation.c

changeset 11581
5c7f44be5dfe
parent 11552
674a2a79943a
child 11598
7d18b6a35746
equal deleted inserted replaced
11580:41ecd9e08031 11581:5c7f44be5dfe
29 #include "prefs.h" 29 #include "prefs.h"
30 #include "prpl.h" 30 #include "prpl.h"
31 #include "signals.h" 31 #include "signals.h"
32 #include "util.h" 32 #include "util.h"
33 33
34 typedef struct
35 {
36 char *id;
37 char *name;
38 GaimConvPlacementFunc fnc;
39
40 } ConvPlacementData;
41
42 #define SEND_TYPED_TIMEOUT 5000 34 #define SEND_TYPED_TIMEOUT 5000
43
44 static GaimConvWindowUiOps *win_ui_ops = NULL;
45 35
46 static GList *conversations = NULL; 36 static GList *conversations = NULL;
47 static GList *ims = NULL; 37 static GList *ims = NULL;
48 static GList *chats = NULL; 38 static GList *chats = NULL;
49 static GList *windows = NULL; 39 static GaimConversationUiOps *default_ops = NULL;
50 static GList *conv_placement_fncs = NULL; 40
51 static GaimConvPlacementFunc place_conv = NULL; 41
52 42 void
53 static void ensure_default_funcs(void); 43 gaim_conversations_set_ui_ops(GaimConversationUiOps *ops)
54 static void conv_placement_last_created_win(GaimConversation *conv); 44 {
45 default_ops = ops;
46 }
55 47
56 static gboolean 48 static gboolean
57 reset_typing(gpointer data) 49 reset_typing(gpointer data)
58 { 50 {
59 GaimConversation *c = (GaimConversation *)data; 51 GaimConversation *c = (GaimConversation *)data;
235 227
236 g_free(displayed); 228 g_free(displayed);
237 g_free(sent); 229 g_free(sent);
238 } 230 }
239 231
240 GaimConvWindow *
241 gaim_conv_window_new(void)
242 {
243 GaimConvWindow *win;
244
245 win = g_new0(GaimConvWindow, 1);
246 GAIM_DBUS_REGISTER_POINTER(win, GaimConvWindow);
247
248 windows = g_list_append(windows, win);
249
250 win->ui_ops = gaim_conversations_get_win_ui_ops();
251
252 if (win->ui_ops != NULL && win->ui_ops->new_window != NULL)
253 win->ui_ops->new_window(win);
254
255 return win;
256 }
257
258 void
259 gaim_conv_window_destroy(GaimConvWindow *win)
260 {
261 GaimConvWindowUiOps *ops;
262 GList *node;
263
264 g_return_if_fail(win != NULL);
265
266 ops = gaim_conv_window_get_ui_ops(win);
267
268 /*
269 * If there are any conversations in this, destroy them all. The last
270 * conversation will call gaim_conv_window_destroy(), but this time, this
271 * check will fail and the window will actually be destroyed.
272 *
273 * This is needed because chats may not close right away. They may
274 * wait for notification first. When they get that, the window is
275 * already destroyed, and gaim either crashes or spits out gtk warnings.
276 * The problem is fixed with this check.
277 */
278 if (gaim_conv_window_get_conversation_count(win) > 0) {
279
280 node = g_list_first(gaim_conv_window_get_conversations(win));
281 while(node != NULL)
282 {
283 GaimConversation *conv = node->data;
284
285 node = g_list_next(node);
286
287 gaim_conversation_destroy(conv);
288 }
289 }
290 else
291 {
292 if (ops != NULL && ops->destroy_window != NULL)
293 ops->destroy_window(win);
294
295 g_list_free(gaim_conv_window_get_conversations(win));
296
297 windows = g_list_remove(windows, win);
298
299 GAIM_DBUS_UNREGISTER_POINTER(win);
300 g_free(win);
301 }
302 }
303
304 void
305 gaim_conv_window_show(GaimConvWindow *win)
306 {
307 GaimConvWindowUiOps *ops;
308
309 g_return_if_fail(win != NULL);
310
311 ops = gaim_conv_window_get_ui_ops(win);
312
313 if (ops == NULL || ops->show == NULL)
314 return;
315
316 ops->show(win);
317 }
318
319 void
320 gaim_conv_window_hide(GaimConvWindow *win)
321 {
322 GaimConvWindowUiOps *ops;
323
324 g_return_if_fail(win != NULL);
325
326 ops = gaim_conv_window_get_ui_ops(win);
327
328 if (ops == NULL || ops->hide == NULL)
329 return;
330
331 ops->hide(win);
332 }
333
334 void
335 gaim_conv_window_raise(GaimConvWindow *win)
336 {
337 GaimConvWindowUiOps *ops;
338
339 g_return_if_fail(win != NULL);
340
341 ops = gaim_conv_window_get_ui_ops(win);
342
343 if (ops == NULL || ops->raise == NULL)
344 return;
345
346 ops->raise(win);
347 }
348
349 gboolean
350 gaim_conv_window_has_focus(GaimConvWindow *win)
351 {
352 gboolean ret = FALSE;
353 GaimConvWindowUiOps *ops;
354
355 g_return_val_if_fail(win != NULL, FALSE);
356
357 ops = gaim_conv_window_get_ui_ops(win);
358
359 if (ops != NULL && ops->has_focus != NULL)
360 ret = ops->has_focus(win);
361
362 return ret;
363 }
364
365 void
366 gaim_conv_window_set_ui_ops(GaimConvWindow *win, GaimConvWindowUiOps *ops)
367 {
368 GaimConversationUiOps *conv_ops = NULL;
369 GList *l;
370
371 g_return_if_fail(win != NULL);
372
373 if (win->ui_ops == ops)
374 return;
375
376 if (ops != NULL && ops->get_conversation_ui_ops != NULL)
377 conv_ops = ops->get_conversation_ui_ops();
378
379 if (win->ui_ops != NULL && win->ui_ops->destroy_window != NULL)
380 win->ui_ops->destroy_window(win);
381
382 win->ui_ops = ops;
383
384 if (win->ui_ops != NULL && win->ui_ops->new_window != NULL)
385 win->ui_ops->new_window(win);
386
387 for (l = gaim_conv_window_get_conversations(win);
388 l != NULL;
389 l = l->next) {
390
391 GaimConversation *conv = (GaimConversation *)l;
392
393 gaim_conversation_set_ui_ops(conv, conv_ops);
394
395 if (win->ui_ops != NULL && win->ui_ops->add_conversation != NULL)
396 win->ui_ops->add_conversation(win, conv);
397 }
398 }
399
400 GaimConvWindowUiOps *
401 gaim_conv_window_get_ui_ops(const GaimConvWindow *win)
402 {
403 g_return_val_if_fail(win != NULL, NULL);
404
405 return win->ui_ops;
406 }
407
408 int
409 gaim_conv_window_add_conversation(GaimConvWindow *win, GaimConversation *conv)
410 {
411 GaimConvWindowUiOps *ops;
412
413 g_return_val_if_fail(win != NULL, -1);
414 g_return_val_if_fail(conv != NULL, -1);
415
416 if (gaim_conversation_get_window(conv) != NULL) {
417 gaim_conv_window_remove_conversation(
418 gaim_conversation_get_window(conv),
419 conv);
420 }
421
422 ops = gaim_conv_window_get_ui_ops(win);
423
424 win->conversations = g_list_append(win->conversations, conv);
425 win->conversation_count++;
426
427 if (ops != NULL) {
428 conv->window = win;
429
430 if (ops->get_conversation_ui_ops != NULL)
431 gaim_conversation_set_ui_ops(conv, ops->get_conversation_ui_ops());
432
433 if (ops->add_conversation != NULL)
434 ops->add_conversation(win, conv);
435 }
436
437 return win->conversation_count - 1;
438 }
439
440 GaimConversation *
441 gaim_conv_window_remove_conversation(GaimConvWindow *win, GaimConversation *conv)
442 {
443 GaimConvWindowUiOps *ops;
444 GList *node;
445
446 g_return_val_if_fail(win != NULL, NULL);
447 g_return_val_if_fail(conv != NULL, NULL);
448
449 ops = gaim_conv_window_get_ui_ops(win);
450
451 node = g_list_find(gaim_conv_window_get_conversations(win), conv);
452
453 if (!node)
454 return NULL;
455
456 if (ops != NULL && ops->remove_conversation != NULL)
457 ops->remove_conversation(win, conv);
458
459 win->conversations = g_list_remove_link(win->conversations, node);
460
461 g_list_free_1(node);
462
463 win->conversation_count--;
464
465 conv->window = NULL;
466
467 if (gaim_conv_window_get_conversation_count(win) == 0)
468 gaim_conv_window_destroy(win);
469
470 return conv;
471 }
472
473 size_t
474 gaim_conv_window_get_conversation_count(const GaimConvWindow *win)
475 {
476 g_return_val_if_fail(win != NULL, 0);
477
478 return win->conversation_count;
479 }
480
481 void
482 gaim_conv_window_switch_conversation(GaimConvWindow *win, GaimConversation *conv)
483 {
484 GaimConvWindowUiOps *ops;
485 GaimConversation *old_conv;
486
487 g_return_if_fail(win != NULL);
488 g_return_if_fail(conv != NULL);
489
490 old_conv = gaim_conv_window_get_active_conversation(win);
491
492 gaim_signal_emit(gaim_conversations_get_handle(),
493 "conversation-switching", old_conv, conv);
494
495 ops = gaim_conv_window_get_ui_ops(win);
496
497 if (ops != NULL && ops->switch_conversation != NULL)
498 ops->switch_conversation(win, conv);
499
500 gaim_signal_emit(gaim_conversations_get_handle(),
501 "conversation-switched", old_conv, conv);
502 }
503
504 GaimConversation *
505 gaim_conv_window_get_active_conversation(const GaimConvWindow *win)
506 {
507 GaimConvWindowUiOps *ops;
508
509 g_return_val_if_fail(win != NULL, NULL);
510
511 if (gaim_conv_window_get_conversation_count(win) == 0)
512 return NULL;
513
514 ops = gaim_conv_window_get_ui_ops(win);
515
516 if (ops != NULL && ops->get_active_conversation != NULL)
517 return ops->get_active_conversation(win);
518
519 return NULL;
520 }
521
522 GList *
523 gaim_conv_window_get_conversations(const GaimConvWindow *win)
524 {
525 g_return_val_if_fail(win != NULL, NULL);
526
527 return win->conversations;
528 }
529
530 GList *
531 gaim_get_windows(void)
532 {
533 return windows;
534 }
535
536 GaimConvWindow *
537 gaim_get_first_window_with_type(GaimConversationType type)
538 {
539 GList *wins, *convs;
540 GaimConvWindow *win;
541 GaimConversation *conv;
542
543 if (type == GAIM_CONV_TYPE_UNKNOWN)
544 return NULL;
545
546 for (wins = gaim_get_windows(); wins != NULL; wins = wins->next) {
547 win = (GaimConvWindow *)wins->data;
548
549 for (convs = gaim_conv_window_get_conversations(win);
550 convs != NULL;
551 convs = convs->next) {
552
553 conv = (GaimConversation *)convs->data;
554
555 if (gaim_conversation_get_type(conv) == type)
556 return win;
557 }
558 }
559
560 return NULL;
561 }
562
563 GaimConvWindow *
564 gaim_get_last_window_with_type(GaimConversationType type)
565 {
566 GList *wins, *convs;
567 GaimConvWindow *win;
568 GaimConversation *conv;
569
570 if (type == GAIM_CONV_TYPE_UNKNOWN)
571 return NULL;
572
573 for (wins = g_list_last(gaim_get_windows());
574 wins != NULL;
575 wins = wins->prev) {
576
577 win = (GaimConvWindow *)wins->data;
578
579 for (convs = gaim_conv_window_get_conversations(win);
580 convs != NULL;
581 convs = convs->next) {
582
583 conv = (GaimConversation *)convs->data;
584
585 if (gaim_conversation_get_type(conv) == type)
586 return win;
587 }
588 }
589
590 return NULL;
591 }
592
593 /************************************************************************** 232 /**************************************************************************
594 * Conversation API 233 * Conversation API
595 **************************************************************************/ 234 **************************************************************************/
596 static void 235 static void
597 gaim_conversation_chat_cleanup_for_rejoin(GaimConversation *conv) 236 gaim_conversation_chat_cleanup_for_rejoin(GaimConversation *conv)
628 gaim_conversation_new(GaimConversationType type, GaimAccount *account, 267 gaim_conversation_new(GaimConversationType type, GaimAccount *account,
629 const char *name) 268 const char *name)
630 { 269 {
631 GaimConversation *conv; 270 GaimConversation *conv;
632 GaimConnection *gc; 271 GaimConnection *gc;
272 GaimConversationUiOps *ops;
633 273
634 g_return_val_if_fail(type != GAIM_CONV_TYPE_UNKNOWN, NULL); 274 g_return_val_if_fail(type != GAIM_CONV_TYPE_UNKNOWN, NULL);
635 g_return_val_if_fail(account != NULL, NULL); 275 g_return_val_if_fail(account != NULL, NULL);
636 g_return_val_if_fail(name != NULL, NULL); 276 g_return_val_if_fail(name != NULL, NULL);
637 277
664 conv->logs = g_list_append(NULL, gaim_log_new(type == GAIM_CONV_TYPE_CHAT ? GAIM_LOG_CHAT : 304 conv->logs = g_list_append(NULL, gaim_log_new(type == GAIM_CONV_TYPE_CHAT ? GAIM_LOG_CHAT :
665 GAIM_LOG_IM, conv->name, account, 305 GAIM_LOG_IM, conv->name, account,
666 conv, time(NULL))); 306 conv, time(NULL)));
667 /* copy features from the connection. */ 307 /* copy features from the connection. */
668 conv->features = gc->flags; 308 conv->features = gc->flags;
669 309
310 ops = conv->ui_ops = default_ops;
311 if (ops != NULL && ops->create_conversation != NULL)
312 ops->create_conversation(conv);
670 313
671 if (type == GAIM_CONV_TYPE_IM) 314 if (type == GAIM_CONV_TYPE_IM)
672 { 315 {
673 GaimBuddyIcon *icon; 316 GaimBuddyIcon *icon;
674 conv->u.im = g_new0(GaimConvIm, 1); 317 conv->u.im = g_new0(GaimConvIm, 1);
705 conversations = g_list_append(conversations, conv); 348 conversations = g_list_append(conversations, conv);
706 349
707 /* Auto-set the title. */ 350 /* Auto-set the title. */
708 gaim_conversation_autoset_title(conv); 351 gaim_conversation_autoset_title(conv);
709 352
710 /*
711 * Place the conversation somewhere. If there are no conversation
712 * windows open, or if tabbed conversations are not enabled, then
713 * place the conversation in a new window by itself. Otherwise use
714 * the chosen conversation placement function.
715 */
716 if ((windows == NULL) || (!gaim_prefs_get_bool("/gaim/gtk/conversations/tabs")))
717 {
718 GaimConvWindow *win;
719
720 win = gaim_conv_window_new();
721
722 gaim_conv_window_add_conversation(win, conv);
723
724 /* Ensure the window is visible. */
725 gaim_conv_window_show(win);
726 }
727 else
728 {
729 if (place_conv == NULL)
730 {
731 ensure_default_funcs();
732
733 place_conv = conv_placement_last_created_win;
734 }
735
736 if (place_conv == NULL)
737 gaim_debug(GAIM_DEBUG_ERROR, "conversation",
738 "This is about to suck.\n");
739
740 place_conv(conv);
741 }
742
743 gaim_signal_emit(gaim_conversations_get_handle(), 353 gaim_signal_emit(gaim_conversations_get_handle(),
744 "conversation-created", conv); 354 "conversation-created", conv);
745 355
746 return conv; 356 return conv;
747 } 357 }
748 358
749 void 359 void
750 gaim_conversation_destroy(GaimConversation *conv) 360 gaim_conversation_destroy(GaimConversation *conv)
751 { 361 {
752 GaimPluginProtocolInfo *prpl_info = NULL; 362 GaimPluginProtocolInfo *prpl_info = NULL;
753 GaimConvWindow *win;
754 GaimConversationUiOps *ops; 363 GaimConversationUiOps *ops;
755 GaimConnection *gc; 364 GaimConnection *gc;
756 const char *name; 365 const char *name;
757 GList *node; 366 GList *node;
758 367
759 g_return_if_fail(conv != NULL); 368 g_return_if_fail(conv != NULL);
760 369
761 win = gaim_conversation_get_window(conv);
762 ops = gaim_conversation_get_ui_ops(conv); 370 ops = gaim_conversation_get_ui_ops(conv);
763 gc = gaim_conversation_get_gc(conv); 371 gc = gaim_conversation_get_gc(conv);
764 name = gaim_conversation_get_name(conv); 372 name = gaim_conversation_get_name(conv);
765 373
766 if (gc != NULL) 374 if (gc != NULL)
897 } 505 }
898 506
899 g_hash_table_destroy(conv->data); 507 g_hash_table_destroy(conv->data);
900 conv->data = NULL; 508 conv->data = NULL;
901 509
902 if (win != NULL) {
903 gaim_conv_window_remove_conversation(win, conv);
904 }
905
906 if (ops != NULL && ops->destroy_conversation != NULL) 510 if (ops != NULL && ops->destroy_conversation != NULL)
907 ops->destroy_conversation(conv); 511 ops->destroy_conversation(conv);
908 512
909 g_list_foreach(conv->logs, (GFunc)gaim_log_free, NULL); 513 g_list_foreach(conv->logs, (GFunc)gaim_log_free, NULL);
910 g_list_free(conv->logs); 514 g_list_free(conv->logs);
924 528
925 conv->features = features; 529 conv->features = features;
926 530
927 ops = conv->ui_ops; 531 ops = conv->ui_ops;
928 if(ops && ops->updated) 532 if(ops && ops->updated)
929 ops->updated(conv, GAIM_CONV_UPDATE_FEATURES); 533 ops->updated(conv, GAIM_CONV_UPDATE_FEATURES);
930 } 534 }
931 535
932 536
933 GaimConnectionFlags 537 GaimConnectionFlags
934 gaim_conversation_get_features(GaimConversation *conv) 538 gaim_conversation_get_features(GaimConversation *conv)
1052 text = gaim_buddy_get_alias(b); 656 text = gaim_buddy_get_alias(b);
1053 } else if(gaim_conversation_get_type(conv) == GAIM_CONV_TYPE_CHAT) { 657 } else if(gaim_conversation_get_type(conv) == GAIM_CONV_TYPE_CHAT) {
1054 if(account && ((chat = gaim_blist_find_chat(account, name)) != NULL)) 658 if(account && ((chat = gaim_blist_find_chat(account, name)) != NULL))
1055 text = chat->alias; 659 text = chat->alias;
1056 } 660 }
1057 661
1058 662
1059 if(text == NULL) 663 if(text == NULL)
1060 text = name; 664 text = name;
1061 665
1062 gaim_conversation_set_title(conv, text); 666 gaim_conversation_set_title(conv, text);
1138 gaim_conversation_get_send_history(const GaimConversation *conv) 742 gaim_conversation_get_send_history(const GaimConversation *conv)
1139 { 743 {
1140 g_return_val_if_fail(conv != NULL, NULL); 744 g_return_val_if_fail(conv != NULL, NULL);
1141 745
1142 return conv->send_history; 746 return conv->send_history;
1143 }
1144
1145 GaimConvWindow *
1146 gaim_conversation_get_window(const GaimConversation *conv)
1147 {
1148 g_return_val_if_fail(conv != NULL, NULL);
1149
1150 return conv->window;
1151 } 747 }
1152 748
1153 GaimConvIm * 749 GaimConvIm *
1154 gaim_conversation_get_im_data(const GaimConversation *conv) 750 gaim_conversation_get_im_data(const GaimConversation *conv)
1155 { 751 {
1251 GaimPluginProtocolInfo *prpl_info = NULL; 847 GaimPluginProtocolInfo *prpl_info = NULL;
1252 GaimConnection *gc = NULL; 848 GaimConnection *gc = NULL;
1253 GaimAccount *account; 849 GaimAccount *account;
1254 GaimConversationUiOps *ops; 850 GaimConversationUiOps *ops;
1255 const char *alias; 851 const char *alias;
1256 GaimConvWindow *win;
1257 GaimBuddy *b; 852 GaimBuddy *b;
1258 GaimUnseenState unseen; 853 GaimUnseenState unseen;
1259 /* int logging_font_options = 0; */ 854 /* int logging_font_options = 0; */
1260 855
1261 g_return_if_fail(conv != NULL); 856 g_return_if_fail(conv != NULL);
1320 log = log->next; 915 log = log->next;
1321 } 916 }
1322 } 917 }
1323 ops->write_conv(conv, who, alias, message, flags, mtime); 918 ops->write_conv(conv, who, alias, message, flags, mtime);
1324 919
1325 win = gaim_conversation_get_window(conv);
1326 920
1327 /* Tab highlighting */ 921 /* Tab highlighting */
1328 if (!(flags & GAIM_MESSAGE_RECV) && !(flags & GAIM_MESSAGE_SYSTEM) && !(flags & GAIM_MESSAGE_ERROR)) 922 if (!(flags & GAIM_MESSAGE_RECV) && !(flags & GAIM_MESSAGE_SYSTEM) && !(flags & GAIM_MESSAGE_ERROR))
1329 return; 923 return;
1330 924
1331 if (gaim_conversation_get_type(conv) == GAIM_CONV_TYPE_IM) { 925 if (gaim_conversation_get_type(conv) == GAIM_CONV_TYPE_IM) {
1332 if ((flags & GAIM_MESSAGE_RECV) == GAIM_MESSAGE_RECV) 926 if ((flags & GAIM_MESSAGE_RECV) == GAIM_MESSAGE_RECV)
1333 gaim_conv_im_set_typing_state(GAIM_CONV_IM(conv), GAIM_NOT_TYPING); 927 gaim_conv_im_set_typing_state(GAIM_CONV_IM(conv), GAIM_NOT_TYPING);
1334 } 928 }
1335 929
1336 if (gaim_conv_window_has_focus(win) &&
1337 gaim_conv_window_get_active_conversation(win) == conv)
1338 {
1339 unseen = GAIM_UNSEEN_NONE;
1340 }
1341 else
1342 { 930 {
1343 if ((flags & GAIM_MESSAGE_NICK) == GAIM_MESSAGE_NICK || 931 if ((flags & GAIM_MESSAGE_NICK) == GAIM_MESSAGE_NICK ||
1344 gaim_conversation_get_unseen(conv) == GAIM_UNSEEN_NICK) 932 gaim_conversation_get_unseen(conv) == GAIM_UNSEEN_NICK)
1345 unseen = GAIM_UNSEEN_NICK; 933 unseen = GAIM_UNSEEN_NICK;
1346 else if ((((flags & GAIM_MESSAGE_SYSTEM) == GAIM_MESSAGE_SYSTEM) || 934 else if ((((flags & GAIM_MESSAGE_SYSTEM) == GAIM_MESSAGE_SYSTEM) ||
1350 else 938 else
1351 unseen = GAIM_UNSEEN_TEXT; 939 unseen = GAIM_UNSEEN_TEXT;
1352 } 940 }
1353 941
1354 gaim_conversation_set_unseen(conv, unseen); 942 gaim_conversation_set_unseen(conv, unseen);
1355
1356 /*
1357 * TODO: This is #if 0'ed out because we don't have a way of
1358 * telling if a conversation window is minimized. This
1359 * should probably be done in gtkconv.c anyway.
1360 */
1361 #if 0
1362 /*
1363 * This is auto-tab switching.
1364 *
1365 * If we received an IM, and the GaimConvWindow is not active,
1366 * then make this conversation the active tab in this GaimConvWindow.
1367 *
1368 * We do this so that, when the user comes back to the conversation
1369 * window, the first thing they'll see is the new message. This is
1370 * especially important when the IM window is flashing in their
1371 * taskbar--we want the title of the window to be set to the name
1372 * of the person that IMed them most recently.
1373 */
1374 if ((gaim_conversation_get_type(conv) == GAIM_CONV_TYPE_IM) &&
1375 (flags & (GAIM_MESSAGE_RECV | GAIM_MESSAGE_ERROR)) &&
1376 (!gaim_conv_window_has_focus(win)) &&
1377 (gaim_conv_window_is_minimized(win)))
1378 {
1379 gaim_conv_window_switch_conversation(win, conv);
1380 }
1381 #endif
1382 }
1383
1384 void
1385 gaim_conversation_update_progress(GaimConversation *conv, float percent)
1386 {
1387 GaimConversationUiOps *ops;
1388
1389 g_return_if_fail(conv != NULL);
1390 g_return_if_fail(percent >= 0 && percent <= 1);
1391
1392 /*
1393 * NOTE: A percent == 1 indicates that the progress bar should be
1394 * closed.
1395 */
1396 ops = gaim_conversation_get_ui_ops(conv);
1397
1398 if (ops != NULL && ops->update_progress != NULL)
1399 ops->update_progress(conv, percent);
1400 } 943 }
1401 944
1402 gboolean 945 gboolean
1403 gaim_conversation_has_focus(GaimConversation *conv) 946 gaim_conversation_has_focus(GaimConversation *conv)
1404 { 947 {
1405 gboolean ret = FALSE; 948 gboolean ret = FALSE;
1406 GaimConvWindow *win;
1407 GaimConversationUiOps *ops; 949 GaimConversationUiOps *ops;
1408 950
1409 g_return_val_if_fail(conv != NULL, FALSE); 951 g_return_val_if_fail(conv != NULL, FALSE);
1410
1411 win = gaim_conversation_get_window(conv);
1412 if (gaim_conv_window_get_active_conversation(win) != conv)
1413 return FALSE;
1414 952
1415 ops = gaim_conversation_get_ui_ops(conv); 953 ops = gaim_conversation_get_ui_ops(conv);
1416 954
1417 if (ops != NULL && ops->has_focus != NULL) 955 if (ops != NULL && ops->has_focus != NULL)
1418 ret = ops->has_focus(conv); 956 ret = ops->has_focus(conv);
1604 } 1142 }
1605 1143
1606 gboolean gaim_conv_present_error(const char *who, GaimAccount *account, const char *what) 1144 gboolean gaim_conv_present_error(const char *who, GaimAccount *account, const char *what)
1607 { 1145 {
1608 GaimConversation *conv; 1146 GaimConversation *conv;
1609 GaimConvWindow *window;
1610 1147
1611 g_return_val_if_fail(who != NULL, FALSE); 1148 g_return_val_if_fail(who != NULL, FALSE);
1612 g_return_val_if_fail(account !=NULL, FALSE); 1149 g_return_val_if_fail(account !=NULL, FALSE);
1613 g_return_val_if_fail(what != NULL, FALSE); 1150 g_return_val_if_fail(what != NULL, FALSE);
1614 1151
1615 conv = gaim_find_conversation_with_account(GAIM_CONV_TYPE_ANY, who, account); 1152 conv = gaim_find_conversation_with_account(GAIM_CONV_TYPE_ANY, who, account);
1616 if (conv != NULL) 1153 if (conv != NULL)
1617 gaim_conversation_write(conv, NULL, what, GAIM_MESSAGE_ERROR, time(NULL)); 1154 gaim_conversation_write(conv, NULL, what, GAIM_MESSAGE_ERROR, time(NULL));
1618 else 1155 else
1619 return FALSE; 1156 return FALSE;
1620 window = gaim_conversation_get_window(conv);
1621
1622 /*
1623 * Change the active conversation to this conversation unless the
1624 * user is already using this window.
1625 * TODO: There's a good chance this is no longer necessary
1626 */
1627 if (!gaim_conv_window_has_focus(window))
1628 gaim_conv_window_switch_conversation(window, conv);
1629
1630 gaim_conv_window_raise(window);
1631 1157
1632 return TRUE; 1158 return TRUE;
1633 } 1159 }
1634 1160
1635 void 1161 void
1912 gboolean new_arrival) 1438 gboolean new_arrival)
1913 { 1439 {
1914 GList *users = g_list_append(NULL, (char *)user); 1440 GList *users = g_list_append(NULL, (char *)user);
1915 GList *extra_msgs = g_list_append(NULL, (char *)extra_msg); 1441 GList *extra_msgs = g_list_append(NULL, (char *)extra_msg);
1916 GList *flags2 = g_list_append(NULL, GINT_TO_POINTER(flags)); 1442 GList *flags2 = g_list_append(NULL, GINT_TO_POINTER(flags));
1917 1443
1918 gaim_conv_chat_add_users(chat, users, extra_msgs, flags2, new_arrival); 1444 gaim_conv_chat_add_users(chat, users, extra_msgs, flags2, new_arrival);
1919 1445
1920 g_list_free(users); 1446 g_list_free(users);
1921 g_list_free(extra_msgs); 1447 g_list_free(extra_msgs);
1922 g_list_free(flags2); 1448 g_list_free(flags2);
2085 g_snprintf(tmp, sizeof(tmp), 1611 g_snprintf(tmp, sizeof(tmp),
2086 _("You are now known as %s"), new_user); 1612 _("You are now known as %s"), new_user);
2087 } else { 1613 } else {
2088 const char *old_alias = old_user; 1614 const char *old_alias = old_user;
2089 const char *new_alias = new_user; 1615 const char *new_alias = new_user;
2090 1616
2091 if (!(prpl_info->options & OPT_PROTO_UNIQUE_CHATNAME)) { 1617 if (!(prpl_info->options & OPT_PROTO_UNIQUE_CHATNAME)) {
2092 GaimBuddy *buddy; 1618 GaimBuddy *buddy;
2093 1619
2094 if ((buddy = gaim_find_buddy(gc->account, old_user)) != NULL) 1620 if ((buddy = gaim_find_buddy(gc->account, old_user)) != NULL)
2095 old_alias = gaim_buddy_get_contact_alias(buddy); 1621 old_alias = gaim_buddy_get_contact_alias(buddy);
2096 if ((buddy = gaim_find_buddy(gc->account, new_user)) != NULL) 1622 if ((buddy = gaim_find_buddy(gc->account, new_user)) != NULL)
2097 new_alias = gaim_buddy_get_contact_alias(buddy); 1623 new_alias = gaim_buddy_get_contact_alias(buddy);
2098 } 1624 }
2441 g_return_val_if_fail(cb != NULL, NULL); 1967 g_return_val_if_fail(cb != NULL, NULL);
2442 1968
2443 return cb->name; 1969 return cb->name;
2444 } 1970 }
2445 1971
2446 /**************************************************************************
2447 * Conversation placement functions
2448 **************************************************************************/
2449 /* This one places conversations in the last made window. */
2450 static void
2451 conv_placement_last_created_win(GaimConversation *conv)
2452 {
2453 GaimConvWindow *win;
2454
2455 win = g_list_last(gaim_get_windows())->data;
2456
2457 if (win == NULL) {
2458 win = gaim_conv_window_new();
2459
2460 gaim_conv_window_add_conversation(win, conv);
2461 gaim_conv_window_show(win);
2462 }
2463 else
2464 gaim_conv_window_add_conversation(win, conv);
2465 }
2466
2467 /* This one places conversations in the last made window of the same type. */
2468 static void
2469 conv_placement_last_created_win_type(GaimConversation *conv)
2470 {
2471 GaimConvWindow *win;
2472
2473 win = gaim_get_last_window_with_type(gaim_conversation_get_type(conv));
2474
2475 if (win == NULL) {
2476 win = gaim_conv_window_new();
2477
2478 gaim_conv_window_add_conversation(win, conv);
2479 gaim_conv_window_show(win);
2480 }
2481 else
2482 gaim_conv_window_add_conversation(win, conv);
2483 }
2484
2485 /* This one places each conversation in its own window. */
2486 static void
2487 conv_placement_new_window(GaimConversation *conv)
2488 {
2489 GaimConvWindow *win;
2490
2491 win = gaim_conv_window_new();
2492
2493 gaim_conv_window_add_conversation(win, conv);
2494
2495 gaim_conv_window_show(win);
2496 }
2497
2498 static GaimGroup *
2499 conv_get_group(GaimConversation *conv)
2500 {
2501 GaimGroup *group = NULL;
2502
2503 if (gaim_conversation_get_type(conv) == GAIM_CONV_TYPE_IM)
2504 {
2505 GaimBuddy *buddy;
2506
2507 buddy = gaim_find_buddy(gaim_conversation_get_account(conv),
2508 gaim_conversation_get_name(conv));
2509
2510 if (buddy != NULL)
2511 group = gaim_find_buddys_group(buddy);
2512
2513 }
2514 else if (gaim_conversation_get_type(conv) == GAIM_CONV_TYPE_CHAT)
2515 {
2516 GaimChat *chat;
2517
2518 chat = gaim_blist_find_chat(gaim_conversation_get_account(conv),
2519 gaim_conversation_get_name(conv));
2520
2521 if (chat != NULL)
2522 group = gaim_chat_get_group(chat);
2523 }
2524
2525 return group;
2526 }
2527
2528 /*
2529 * This groups things by, well, group. Buddies from groups will always be
2530 * grouped together, and a buddy from a group not belonging to any currently
2531 * open windows will get a new window.
2532 */
2533 static void
2534 conv_placement_by_group(GaimConversation *conv)
2535 {
2536 GaimConversationType type;
2537 GaimGroup *group = NULL;
2538 GList *wl, *cl;
2539
2540 type = gaim_conversation_get_type(conv);
2541
2542 group = conv_get_group(conv);
2543
2544 /* Go through the list of IMs and find one with this group. */
2545 for (wl = gaim_get_windows(); wl != NULL; wl = wl->next)
2546 {
2547 GaimConvWindow *win2;
2548 GaimConversation *conv2;
2549 GaimGroup *group2 = NULL;
2550
2551 win2 = (GaimConvWindow *)wl->data;
2552
2553 for (cl = gaim_conv_window_get_conversations(win2);
2554 cl != NULL;
2555 cl = cl->next)
2556 {
2557 conv2 = (GaimConversation *)cl->data;
2558
2559 group2 = conv_get_group(conv2);
2560
2561 if (group == group2)
2562 {
2563 gaim_conv_window_add_conversation(win2, conv);
2564
2565 return;
2566 }
2567 }
2568 }
2569
2570 /* Make a new window. */
2571 conv_placement_new_window(conv);
2572 }
2573
2574 /* This groups things by account. Otherwise, the same semantics as above */
2575 static void
2576 conv_placement_by_account(GaimConversation *conv)
2577 {
2578 GaimConversationType type;
2579 GList *wins, *convs;
2580 GaimAccount *account;
2581
2582 account = gaim_conversation_get_account(conv);
2583 type = gaim_conversation_get_type(conv);
2584
2585 /* Go through the list of IMs and find one with this group. */
2586 for (wins = gaim_get_windows(); wins != NULL; wins = wins->next)
2587 {
2588 GaimConvWindow *win2;
2589 GaimConversation *conv2;
2590
2591 win2 = (GaimConvWindow *)wins->data;
2592
2593 for (convs = gaim_conv_window_get_conversations(win2);
2594 convs != NULL;
2595 convs = convs->next)
2596 {
2597 conv2 = (GaimConversation *)convs->data;
2598
2599 if (account == gaim_conversation_get_account(conv2))
2600 {
2601 gaim_conv_window_add_conversation(win2, conv);
2602 return;
2603 }
2604 }
2605 }
2606
2607 /* Make a new window. */
2608 conv_placement_new_window(conv);
2609 }
2610
2611 static ConvPlacementData *
2612 get_conv_placement_data(const char *id)
2613 {
2614 ConvPlacementData *data = NULL;
2615 GList *n;
2616
2617 for(n = conv_placement_fncs; n; n = n->next) {
2618 data = n->data;
2619 if(!strcmp(data->id, id))
2620 return data;
2621 }
2622
2623 return NULL;
2624 }
2625
2626 static void
2627 add_conv_placement_fnc(const char *id, const char *name,
2628 GaimConvPlacementFunc fnc)
2629 {
2630 ConvPlacementData *data;
2631
2632 data = g_new(ConvPlacementData, 1);
2633
2634 data->id = g_strdup(id);
2635 data->name = g_strdup(name);
2636 data->fnc = fnc;
2637
2638 conv_placement_fncs = g_list_append(conv_placement_fncs, data);
2639 }
2640
2641 static void
2642 ensure_default_funcs(void)
2643 {
2644 if (conv_placement_fncs == NULL)
2645 {
2646 add_conv_placement_fnc("last", _("Last created window"),
2647 conv_placement_last_created_win);
2648 add_conv_placement_fnc("im_chat", _("Separate IM and Chat windows"),
2649 conv_placement_last_created_win_type);
2650 add_conv_placement_fnc("new", _("New window"),
2651 conv_placement_new_window);
2652 add_conv_placement_fnc("group", _("By group"),
2653 conv_placement_by_group);
2654 add_conv_placement_fnc("account", _("By account"),
2655 conv_placement_by_account);
2656 }
2657 }
2658
2659 GList *
2660 gaim_conv_placement_get_options(void)
2661 {
2662 GList *n, *list = NULL;
2663 ConvPlacementData *data;
2664
2665 ensure_default_funcs();
2666
2667 for (n = conv_placement_fncs; n; n = n->next) {
2668 data = n->data;
2669 list = g_list_append(list, data->name);
2670 list = g_list_append(list, data->id);
2671 }
2672
2673 return list;
2674 }
2675
2676
2677 void
2678 gaim_conv_placement_add_fnc(const char *id, const char *name,
2679 GaimConvPlacementFunc fnc)
2680 {
2681 g_return_if_fail(id != NULL);
2682 g_return_if_fail(name != NULL);
2683 g_return_if_fail(fnc != NULL);
2684
2685 ensure_default_funcs();
2686
2687 add_conv_placement_fnc(id, name, fnc);
2688 }
2689
2690 void
2691 gaim_conv_placement_remove_fnc(const char *id)
2692 {
2693 ConvPlacementData *data = get_conv_placement_data(id);
2694
2695 if (data == NULL)
2696 return;
2697
2698 conv_placement_fncs = g_list_remove(conv_placement_fncs, data);
2699
2700 g_free(data->id);
2701 g_free(data->name);
2702 g_free(data);
2703 }
2704
2705 const char *
2706 gaim_conv_placement_get_name(const char *id)
2707 {
2708 ConvPlacementData *data;
2709
2710 ensure_default_funcs();
2711
2712 data = get_conv_placement_data(id);
2713
2714 if (data == NULL)
2715 return NULL;
2716
2717 return data->name;
2718 }
2719
2720 GaimConvPlacementFunc
2721 gaim_conv_placement_get_fnc(const char *id)
2722 {
2723 ConvPlacementData *data;
2724
2725 ensure_default_funcs();
2726
2727 data = get_conv_placement_data(id);
2728
2729 if (data == NULL)
2730 return NULL;
2731
2732 return data->fnc;
2733 }
2734
2735 void
2736 gaim_conv_placement_set_current_func(GaimConvPlacementFunc func)
2737 {
2738 g_return_if_fail(func != NULL);
2739
2740 place_conv = func;
2741 }
2742
2743 GaimConvPlacementFunc
2744 gaim_conv_placement_get_current_func(void)
2745 {
2746 return place_conv;
2747 }
2748
2749 void
2750 gaim_conversations_set_win_ui_ops(GaimConvWindowUiOps *ops)
2751 {
2752 win_ui_ops = ops;
2753 }
2754
2755 GaimConvWindowUiOps *
2756 gaim_conversations_get_win_ui_ops(void)
2757 {
2758 return win_ui_ops;
2759 }
2760
2761 void * 1972 void *
2762 gaim_conversations_get_handle(void) 1973 gaim_conversations_get_handle(void)
2763 { 1974 {
2764 static int handle; 1975 static int handle;
2765 1976

mercurial