comparison src/gtkconv.c @ 12116:e75ef7aa913e

[gaim-migrate @ 14416] " This patch implements a replacement for the queuing system from 1.x. It also obsoletes a previous patch [#1338873] I submitted to prioritize the unseen states in gtk conversations. The attached envelope.png is ripped from the msgunread.png already included in gaim. It should be dropped in the pixmaps directory (Makefile.am is updated accordingly in this patch). The two separate queuing preferences from 1.x, queuing messages while away and queuing all new messages (from docklet), are replaced with a single 3-way preference for conversations. The new preference is "Hide new IM conversations". This preference can be set to never, away and always. When a gtk conversation is created, it may be placed in a hidden conversation window instead of being placed normally. This decision is based upon the preference and possibly the away state of the account the conversation is being created for. This *will* effect conversations the user explicitly requests to be created, so in these cases the caller must be sure to present the conversation to the user, using gaim_gtkconv_present_conversation(). This is done already in gtkdialogs.c which handles creating conversations requested by the user from gaim proper (menus, double-clicking on budy in blist, etc.). The main advantage to not queuing messages is that the conversations exist, the message is written to the conversation (and logged if appropriate) and the unseen state is set on the conversation. This means no additional features are needed to track whether there are queued messages or not, just use the unseen state on conversations. Since conversations may not be visible (messages "queued"), gaim proper needs some notification that there are messages waiting. I opted for a menutray icon that shows up when an im conversation has an unseen message. Clicking this icon will focus (and show if hidden) the first conversation with an unseen message. This is essentially the same behavior of the docklet in cvs right now, except that the icon is only visible when there is a conversation with an unread message. The api that is added is flexible enough to allow either the docklet or the new blist menutray icon to be visible for conversations of any/all types and for unseen messages >= any state. Currently they are set to only IM conversations and only unseen states >= TEXT (system messages and no log messages will not trigger blinking the docklet or showing the blist tray icon), but these could be made preferences relatively easily in the future. Other plugins could probably benefit as well: gaim_gtk_conversations_get_first_unseen(). There is probably some limit to comment size, so I'll stop rambling now. If anyone has more questions/comments, catch me in #gaim, here or on gaim-devel." committer: Tailor Script <tailor@pidgin.im>
author Luke Schierer <lschiere@pidgin.im>
date Wed, 16 Nov 2005 18:17:01 +0000
parents 8c6ea55c84a4
children cb7ccb5048cf
comparison
equal deleted inserted replaced
12115:e9790eb93216 12116:e75ef7aa913e
111 } InviteBuddyInfo; 111 } InviteBuddyInfo;
112 112
113 static GtkWidget *invite_dialog = NULL; 113 static GtkWidget *invite_dialog = NULL;
114 static GtkWidget *warn_close_dialog = NULL; 114 static GtkWidget *warn_close_dialog = NULL;
115 115
116 static GaimGtkWindow *hidden_convwin = NULL;
117 static GList *window_list = NULL;
118
119
116 static gboolean update_send_to_selection(GaimGtkWindow *win); 120 static gboolean update_send_to_selection(GaimGtkWindow *win);
117 static void generate_send_to_items(GaimGtkWindow *win); 121 static void generate_send_to_items(GaimGtkWindow *win);
118 122
119 /* Prototypes. <-- because Paco-Paco hates this comment. */ 123 /* Prototypes. <-- because Paco-Paco hates this comment. */
120 static void got_typing_keypress(GaimGtkConversation *gtkconv, gboolean first); 124 static void got_typing_keypress(GaimGtkConversation *gtkconv, gboolean first);
126 static void gaim_gtkconv_updated(GaimConversation *conv, GaimConvUpdateType type); 130 static void gaim_gtkconv_updated(GaimConversation *conv, GaimConvUpdateType type);
127 static void gtkconv_set_unseen(GaimGtkConversation *gtkconv, GaimUnseenState state); 131 static void gtkconv_set_unseen(GaimGtkConversation *gtkconv, GaimUnseenState state);
128 static void update_typing_icon(GaimGtkConversation *gtkconv); 132 static void update_typing_icon(GaimGtkConversation *gtkconv);
129 static char *item_factory_translate_func (const char *path, gpointer func_data); 133 static char *item_factory_translate_func (const char *path, gpointer func_data);
130 gboolean gaim_gtkconv_has_focus(GaimConversation *conv); 134 gboolean gaim_gtkconv_has_focus(GaimConversation *conv);
135 static void private_remove_gtkconv(GaimGtkWindow *win, GaimGtkConversation *gtkconv,
136 gboolean destroy_empty);
131 137
132 static GdkColor *get_nick_color(GaimGtkConversation *gtkconv, const char *name) { 138 static GdkColor *get_nick_color(GaimGtkConversation *gtkconv, const char *name) {
133 static GdkColor col; 139 static GdkColor col;
134 GtkStyle *style = gtk_widget_get_style(gtkconv->imhtml); 140 GtkStyle *style = gtk_widget_get_style(gtkconv->imhtml);
135 float scale; 141 float scale;
2094 gtk_image_set_from_pixbuf(GTK_IMAGE(gtkconv->menu_icon), status); 2100 gtk_image_set_from_pixbuf(GTK_IMAGE(gtkconv->menu_icon), status);
2095 2101
2096 if (status != NULL) 2102 if (status != NULL)
2097 g_object_unref(status); 2103 g_object_unref(status);
2098 2104
2099 if (gaim_gtk_conv_window_get_active_conversation(win) == conv && 2105 if (gaim_gtk_conv_window_is_active_conversation(conv) &&
2100 (gaim_conversation_get_type(conv) != GAIM_CONV_TYPE_IM || 2106 (gaim_conversation_get_type(conv) != GAIM_CONV_TYPE_IM ||
2101 gtkconv->u.im->anim == NULL)) 2107 gtkconv->u.im->anim == NULL))
2102 { 2108 {
2103 status = gaim_gtkconv_get_tab_icon(conv, FALSE); 2109 status = gaim_gtkconv_get_tab_icon(conv, FALSE);
2104 2110
2352 } 2358 }
2353 2359
2354 /************************************************************************** 2360 /**************************************************************************
2355 * End of the bunch of buddy icon functions 2361 * End of the bunch of buddy icon functions
2356 **************************************************************************/ 2362 **************************************************************************/
2363 void
2364 gaim_gtkconv_present_conversation(GaimConversation *conv)
2365 {
2366 GaimGtkConversation *gtkconv = GAIM_GTK_CONVERSATION(conv);
2367
2368 if(gtkconv->win==hidden_convwin) {
2369 private_remove_gtkconv(hidden_convwin, gtkconv, FALSE);
2370 gaim_gtkconv_placement_place(gtkconv);
2371 }
2372
2373 gaim_gtkconv_set_active_conversation(conv);
2374 gaim_gtk_conv_window_switch_gtkconv(gtkconv->win, gtkconv);
2375 gaim_gtk_conv_window_raise(gtkconv->win);
2376 gtk_window_present(GTK_WINDOW(gtkconv->win->window));
2377 }
2378
2357 GaimConversation * 2379 GaimConversation *
2358 gaim_gtk_conversations_get_first_unseen(GaimConversationType type, 2380 gaim_gtk_conversations_get_first_unseen(GaimConversationType type,
2359 GaimUnseenState min_state) 2381 GaimUnseenState min_state)
2360 { 2382 {
2361 GList *l; 2383 GList *l;
3956 3978
3957 g_signal_connect_swapped(G_OBJECT(pane), "focus", 3979 g_signal_connect_swapped(G_OBJECT(pane), "focus",
3958 G_CALLBACK(gtk_widget_grab_focus), 3980 G_CALLBACK(gtk_widget_grab_focus),
3959 gtkconv->entry); 3981 gtkconv->entry);
3960 3982
3983 if (conv_type == GAIM_CONV_TYPE_IM) {
3984 /* put conv in hidden_convwin if hide_new pref is always */
3985 if(strcmp(gaim_prefs_get_string("/gaim/gtk/conversations/im/hide_new"), "always")==0) {
3986 gaim_gtk_conv_window_add_gtkconv(hidden_convwin, gtkconv);
3987 return;
3988 }
3989
3990 /* put conv in hidden_convwin if hide_new pref is away and account is away */
3991 if(strcmp(gaim_prefs_get_string("/gaim/gtk/conversations/im/hide_new"), "away")==0
3992 && gaim_status_type_get_primitive(
3993 gaim_status_get_type(gaim_account_get_active_status(
3994 gaim_conversation_get_account(conv)))) == GAIM_STATUS_AWAY) {
3995 gaim_gtk_conv_window_add_gtkconv(hidden_convwin, gtkconv);
3996 return;
3997 }
3998 }
3999
3961 gaim_gtkconv_placement_place(gtkconv); 4000 gaim_gtkconv_placement_place(gtkconv);
3962 } 4001 }
3963 4002
3964 static void 4003 static void
3965 gaim_gtkconv_destroy(GaimConversation *conv) 4004 gaim_gtkconv_destroy(GaimConversation *conv)
4620 4659
4621 win = gtkconv->win; 4660 win = gtkconv->win;
4622 4661
4623 g_object_get(G_OBJECT(win->window), "has-toplevel-focus", &has_focus, NULL); 4662 g_object_get(G_OBJECT(win->window), "has-toplevel-focus", &has_focus, NULL);
4624 4663
4625 if (has_focus) 4664 if (has_focus && gaim_gtk_conv_window_is_active_conversation(conv))
4626 { 4665 return TRUE;
4627 GaimConversation *c = gaim_gtk_conv_window_get_active_conversation(win); 4666
4628 if (GAIM_GTK_CONVERSATION(c) == gtkconv)
4629 return TRUE;
4630 }
4631 return FALSE; 4667 return FALSE;
4632 } 4668 }
4633 4669
4634 static gboolean 4670 static gboolean
4635 gaim_gtkconv_custom_smiley_add(GaimConversation *conv, const char *smile) 4671 gaim_gtkconv_custom_smiley_add(GaimConversation *conv, const char *smile)
5076 g_free(label); 5112 g_free(label);
5077 } 5113 }
5078 else 5114 else
5079 gtk_label_set_text(GTK_LABEL(gtkconv->tab_label), title); 5115 gtk_label_set_text(GTK_LABEL(gtkconv->tab_label), title);
5080 5116
5081 if (conv == gaim_gtk_conv_window_get_active_conversation(win)) 5117 if (gaim_gtk_conv_window_is_active_conversation(conv))
5082 update_typing_icon(gtkconv); 5118 update_typing_icon(gtkconv);
5083 5119
5084 if (type == GAIM_CONV_UPDATE_TITLE) { 5120 if (type == GAIM_CONV_UPDATE_TITLE) {
5085 gtk_label_set_text(GTK_LABEL(gtkconv->menu_label), title); 5121 gtk_label_set_text(GTK_LABEL(gtkconv->menu_label), title);
5086 if (conv == gaim_gtk_conv_window_get_active_conversation(win)) 5122 if (gaim_gtk_conv_window_is_active_conversation(conv))
5087 gtk_window_set_title(GTK_WINDOW(win->window), title); 5123 gtk_window_set_title(GTK_WINDOW(win->window), title);
5088 } 5124 }
5089 5125
5090 g_free(title); 5126 g_free(title);
5091 } 5127 }
5306 gtk_widget_show(gtkconv->u.im->icon_container); 5342 gtk_widget_show(gtkconv->u.im->icon_container);
5307 gtk_widget_show(frame); 5343 gtk_widget_show(frame);
5308 5344
5309 /* The buddy icon code needs badly to be fixed. */ 5345 /* The buddy icon code needs badly to be fixed. */
5310 buf = gdk_pixbuf_animation_get_static_image(gtkconv->u.im->anim); 5346 buf = gdk_pixbuf_animation_get_static_image(gtkconv->u.im->anim);
5311 if(conv == gaim_gtk_conv_window_get_active_conversation(gtkconv->win)) 5347 if(gaim_gtk_conv_window_is_active_conversation(conv))
5312 gtk_window_set_icon(GTK_WINDOW(win->window), buf); 5348 gtk_window_set_icon(GTK_WINDOW(win->window), buf);
5313 } 5349 }
5314 5350
5315 void 5351 void
5316 gaim_gtkconv_update_buttons_by_protocol(GaimConversation *conv) 5352 gaim_gtkconv_update_buttons_by_protocol(GaimConversation *conv)
5530 conv_placement_usetabs_cb(const char *name, GaimPrefType type, 5566 conv_placement_usetabs_cb(const char *name, GaimPrefType type,
5531 gpointer value, gpointer data) 5567 gpointer value, gpointer data)
5532 { 5568 {
5533 gaim_prefs_trigger_callback("/gaim/gtk/conversations/placement"); 5569 gaim_prefs_trigger_callback("/gaim/gtk/conversations/placement");
5534 } 5570 }
5571
5572 static void
5573 account_status_changed_cb(GaimAccount *account, GaimStatus *oldstatus,
5574 GaimStatus *newstatus)
5575 {
5576 GList *l;
5577 GaimConversation *conv = NULL;
5578 GaimGtkConversation *gtkconv;
5579
5580 if(strcmp(gaim_prefs_get_string("/gaim/gtk/conversations/im/hide_new"), "away")!=0)
5581 return;
5582
5583 if(gaim_status_type_get_primitive(gaim_status_get_type(oldstatus))!=GAIM_STATUS_AWAY)
5584 return;
5585
5586 if(gaim_status_type_get_primitive(gaim_status_get_type(newstatus))==GAIM_STATUS_AWAY)
5587 return;
5588
5589 for (l = hidden_convwin->gtkconvs; l != NULL; l = l->next) {
5590 gtkconv = l->data;
5591
5592 conv = gtkconv->active_conv;
5593
5594 if(gaim_status_type_get_primitive(
5595 gaim_status_get_type(gaim_account_get_active_status(
5596 gaim_conversation_get_account(conv)))) == GAIM_STATUS_AWAY)
5597 continue;
5598
5599 private_remove_gtkconv(hidden_convwin, gtkconv, FALSE);
5600 gaim_gtkconv_placement_place(gtkconv);
5601 }
5602 }
5603
5604 static void
5605 hide_new_pref_cb(const char *name, GaimPrefType type, gpointer value,
5606 gpointer data)
5607 {
5608 GList *l;
5609 GaimConversation *conv = NULL;
5610 GaimGtkConversation *gtkconv;
5611 gboolean when_away = FALSE;
5612
5613 if(!hidden_convwin)
5614 return;
5615
5616 if(strcmp(gaim_prefs_get_string("/gaim/gtk/conversations/im/hide_new"), "always")==0)
5617 return;
5618
5619 if(strcmp(gaim_prefs_get_string("/gaim/gtk/conversations/im/hide_new"), "away")==0)
5620 when_away = TRUE;
5621
5622 for (l = hidden_convwin->gtkconvs; l != NULL; l = l->next) {
5623 gtkconv = l->data;
5624
5625 conv = gtkconv->active_conv;
5626
5627 if(when_away && gaim_status_type_get_primitive(
5628 gaim_status_get_type(gaim_account_get_active_status(
5629 gaim_conversation_get_account(conv)))) == GAIM_STATUS_AWAY)
5630 continue;
5631
5632 private_remove_gtkconv(hidden_convwin, gtkconv, FALSE);
5633 gaim_gtkconv_placement_place(gtkconv);
5634 }
5635 }
5636
5535 5637
5536 static void 5638 static void
5537 conv_placement_pref_cb(const char *name, GaimPrefType type, 5639 conv_placement_pref_cb(const char *name, GaimPrefType type,
5538 gpointer value, gpointer data) 5640 gpointer value, gpointer data)
5539 { 5641 {
5600 gaim_prefs_add_int("/gaim/gtk/conversations/im/default_width", 410); 5702 gaim_prefs_add_int("/gaim/gtk/conversations/im/default_width", 410);
5601 gaim_prefs_add_int("/gaim/gtk/conversations/im/default_height", 160); 5703 gaim_prefs_add_int("/gaim/gtk/conversations/im/default_height", 160);
5602 gaim_prefs_add_int("/gaim/gtk/conversations/im/entry_height", 50); 5704 gaim_prefs_add_int("/gaim/gtk/conversations/im/entry_height", 50);
5603 gaim_prefs_add_bool("/gaim/gtk/conversations/im/show_buddy_icons", TRUE); 5705 gaim_prefs_add_bool("/gaim/gtk/conversations/im/show_buddy_icons", TRUE);
5604 5706
5707 /* convert old queuing prefs to hide_new 3-way pref */
5708 if(gaim_prefs_exists("/plugins/gtk/docklet/queue_messages") &&
5709 gaim_prefs_get_bool("/plugins/gtk/docklet/queue_messages")) {
5710 gaim_prefs_add_string("/gaim/gtk/conversations/im/hide_new", "always");
5711 }
5712 else if(gaim_prefs_exists("/gaim/gtk/away/queue_messages") &&
5713 gaim_prefs_get_bool("/gaim/gtk/away/queue_messages")) {
5714 gaim_prefs_add_string("/gaim/gtk/conversations/im/hide_new", "away");
5715 }
5716 else {
5717 gaim_prefs_add_string("/gaim/gtk/conversations/im/hide_new", "never");
5718 }
5719
5605 /* Connect callbacks. */ 5720 /* Connect callbacks. */
5606 gaim_prefs_connect_callback(handle, "/gaim/gtk/conversations/close_on_tabs", 5721 gaim_prefs_connect_callback(handle, "/gaim/gtk/conversations/close_on_tabs",
5607 close_on_tabs_pref_cb, NULL); 5722 close_on_tabs_pref_cb, NULL);
5608 gaim_prefs_connect_callback(handle, "/gaim/gtk/conversations/show_formatting_toolbar", 5723 gaim_prefs_connect_callback(handle, "/gaim/gtk/conversations/show_formatting_toolbar",
5609 show_formatting_toolbar_pref_cb, NULL); 5724 show_formatting_toolbar_pref_cb, NULL);
5622 /* IM callbacks */ 5737 /* IM callbacks */
5623 gaim_prefs_connect_callback(handle, "/gaim/gtk/conversations/im/animate_buddy_icons", 5738 gaim_prefs_connect_callback(handle, "/gaim/gtk/conversations/im/animate_buddy_icons",
5624 animate_buddy_icons_pref_cb, NULL); 5739 animate_buddy_icons_pref_cb, NULL);
5625 gaim_prefs_connect_callback(handle, "/gaim/gtk/conversations/im/show_buddy_icons", 5740 gaim_prefs_connect_callback(handle, "/gaim/gtk/conversations/im/show_buddy_icons",
5626 show_buddy_icons_pref_cb, NULL); 5741 show_buddy_icons_pref_cb, NULL);
5742 gaim_prefs_connect_callback(handle, "/gaim/gtk/conversations/im/hide_new",
5743 hide_new_pref_cb, NULL);
5744
5627 5745
5628 5746
5629 /********************************************************************** 5747 /**********************************************************************
5630 * Register signals 5748 * Register signals
5631 **********************************************************************/ 5749 **********************************************************************/
5663 G_CALLBACK(buddy_update_cb), NULL); 5781 G_CALLBACK(buddy_update_cb), NULL);
5664 gaim_signal_connect(blist_handle, "buddy-removed", handle, 5782 gaim_signal_connect(blist_handle, "buddy-removed", handle,
5665 G_CALLBACK(buddy_update_cb), NULL); 5783 G_CALLBACK(buddy_update_cb), NULL);
5666 5784
5667 gaim_conversations_set_ui_ops(&conversation_ui_ops); 5785 gaim_conversations_set_ui_ops(&conversation_ui_ops);
5786
5787 hidden_convwin = gaim_gtk_conv_window_new();
5788 window_list = g_list_remove(window_list, hidden_convwin);
5789
5790 gaim_signal_connect(gaim_accounts_get_handle(), "account-status-changed",
5791 handle, GAIM_CALLBACK(account_status_changed_cb), NULL);
5668 } 5792 }
5669 5793
5670 void 5794 void
5671 gaim_gtk_conversations_uninit(void) 5795 gaim_gtk_conversations_uninit(void)
5672 { 5796 {
5673 gaim_prefs_disconnect_by_handle(gaim_gtk_conversations_get_handle()); 5797 gaim_prefs_disconnect_by_handle(gaim_gtk_conversations_get_handle());
5674 gaim_signals_disconnect_by_handle(gaim_gtk_conversations_get_handle()); 5798 gaim_signals_disconnect_by_handle(gaim_gtk_conversations_get_handle());
5675 gaim_signals_unregister_by_instance(gaim_gtk_conversations_get_handle()); 5799 gaim_signals_unregister_by_instance(gaim_gtk_conversations_get_handle());
5800 gaim_gtk_conv_window_destroy(hidden_convwin);
5801 hidden_convwin=NULL;
5676 } 5802 }
5677 5803
5678 5804
5679 5805
5680 5806
6397 6523
6398 /************************************************************************** 6524 /**************************************************************************
6399 * GTK+ window ops 6525 * GTK+ window ops
6400 **************************************************************************/ 6526 **************************************************************************/
6401 6527
6402 static GList *window_list = NULL;
6403
6404 GList * 6528 GList *
6405 gaim_gtk_conv_windows_get_list() 6529 gaim_gtk_conv_windows_get_list()
6406 { 6530 {
6407 return window_list; 6531 return window_list;
6408 } 6532 }
6679 6803
6680 if (gaim_gtk_conv_window_get_gtkconv_count(win) == 1) 6804 if (gaim_gtk_conv_window_get_gtkconv_count(win) == 1)
6681 update_send_to_selection(win); 6805 update_send_to_selection(win);
6682 } 6806 }
6683 6807
6684 void 6808 static void
6685 gaim_gtk_conv_window_remove_gtkconv(GaimGtkWindow *win, GaimGtkConversation *gtkconv) 6809 private_remove_gtkconv(GaimGtkWindow *win, GaimGtkConversation *gtkconv, gboolean destroy_empty)
6686 { 6810 {
6687 unsigned int index; 6811 unsigned int index;
6688 GaimConversationType conv_type; 6812 GaimConversationType conv_type;
6689 6813
6690 conv_type = gaim_conversation_get_type(gtkconv->active_conv); 6814 conv_type = gaim_conversation_get_type(gtkconv->active_conv);
6701 gaim_prefs_get_bool("/gaim/gtk/conversations/tabs")); 6825 gaim_prefs_get_bool("/gaim/gtk/conversations/tabs"));
6702 } 6826 }
6703 6827
6704 win->gtkconvs = g_list_remove(win->gtkconvs, gtkconv); 6828 win->gtkconvs = g_list_remove(win->gtkconvs, gtkconv);
6705 6829
6706 if (!win->gtkconvs) 6830 if (destroy_empty && !win->gtkconvs)
6707 gaim_gtk_conv_window_destroy(win); 6831 gaim_gtk_conv_window_destroy(win);
6832 }
6833 void
6834 gaim_gtk_conv_window_remove_gtkconv(GaimGtkWindow *win, GaimGtkConversation *gtkconv)
6835 {
6836 private_remove_gtkconv(win, gtkconv, TRUE);
6708 } 6837 }
6709 6838
6710 GaimGtkConversation * 6839 GaimGtkConversation *
6711 gaim_gtk_conv_window_get_gtkconv_at_index(const GaimGtkWindow *win, int index) 6840 gaim_gtk_conv_window_get_gtkconv_at_index(const GaimGtkWindow *win, int index)
6712 { 6841 {