comparison console/gntblist.c @ 14041:27182f83b79b

[gaim-migrate @ 16647] Statusbox comes in. It's now possible to change the account status. There's nothing yet for creating custom statuses. It's also possible now to delete accounts. committer: Tailor Script <tailor@pidgin.im>
author Sadrul Habib Chowdhury <imadil@gmail.com>
date Sat, 05 Aug 2006 11:31:54 +0000
parents 7109e6397a31
children 143474e2b1cb
comparison
equal deleted inserted replaced
14040:80891029f5dd 14041:27182f83b79b
1 #include <account.h> 1 #include <account.h>
2 #include <blist.h> 2 #include <blist.h>
3 #include <request.h> 3 #include <request.h>
4 #include <savedstatuses.h>
4 #include <server.h> 5 #include <server.h>
5 #include <signal.h> 6 #include <signal.h>
7 #include <status.h>
6 #include <util.h> 8 #include <util.h>
7 9
8 #include "gntgaim.h" 10 #include "gntgaim.h"
9 #include "gntbox.h" 11 #include "gntbox.h"
12 #include "gntcombobox.h"
13 #include "gntentry.h"
10 #include "gntlabel.h" 14 #include "gntlabel.h"
15 #include "gntline.h"
11 #include "gnttree.h" 16 #include "gnttree.h"
12 17
13 #include "gntblist.h" 18 #include "gntblist.h"
14 #include <string.h> 19 #include <string.h>
15 20
16 #define PREF_ROOT "/gaim/gnt/blist" 21 #define PREF_ROOT "/gaim/gnt/blist"
22 #define TYPING_TIMEOUT 4000
17 23
18 typedef struct 24 typedef struct
19 { 25 {
20 GntWidget *window; 26 GntWidget *window;
21 GntWidget *tree; 27 GntWidget *tree;
23 GntWidget *tooltip; 29 GntWidget *tooltip;
24 GaimBlistNode *tnode; /* Who is the tooltip being displayed for? */ 30 GaimBlistNode *tnode; /* Who is the tooltip being displayed for? */
25 31
26 GntWidget *context; 32 GntWidget *context;
27 GaimBlistNode *cnode; 33 GaimBlistNode *cnode;
34
35 /* XXX: I am KISSing */
36 GntWidget *status; /* Dropdown with the statuses */
37 GntWidget *statustext; /* Status message */
38 int typing;
28 } GGBlist; 39 } GGBlist;
40
41 typedef enum
42 {
43 STATUS_PRIMITIVE = 0,
44 STATUS_SAVED
45 } StatusType;
46
47 typedef struct
48 {
49 StatusType type;
50 union
51 {
52 GaimStatusPrimitive prim;
53 GaimSavedStatus *saved;
54 } u;
55 } StatusBoxItem;
29 56
30 GGBlist *ggblist; 57 GGBlist *ggblist;
31 58
32 static void add_buddy(GaimBuddy *buddy, GGBlist *ggblist); 59 static void add_buddy(GaimBuddy *buddy, GGBlist *ggblist);
33 static void add_group(GaimGroup *group, GGBlist *ggblist); 60 static void add_group(GaimGroup *group, GGBlist *ggblist);
34 static void add_chat(GaimChat *chat, GGBlist *ggblist); 61 static void add_chat(GaimChat *chat, GGBlist *ggblist);
35 static void add_node(GaimBlistNode *node, GGBlist *ggblist); 62 static void add_node(GaimBlistNode *node, GGBlist *ggblist);
36 static void draw_tooltip(GGBlist *ggblist); 63 static void draw_tooltip(GGBlist *ggblist);
64 static void remove_typing_cb(gpointer null);
37 static void remove_peripherals(GGBlist *ggblist); 65 static void remove_peripherals(GGBlist *ggblist);
38 static const char * get_display_name(GaimBlistNode *node); 66 static const char * get_display_name(GaimBlistNode *node);
39 67
40 static void 68 static void
41 new_node(GaimBlistNode *node) 69 new_node(GaimBlistNode *node)
111 static void 139 static void
112 new_list(GaimBuddyList *list) 140 new_list(GaimBuddyList *list)
113 { 141 {
114 } 142 }
115 143
116 static GaimBlistUiOps blist_ui_ops = 144 static GaimBlistUiOps blist_ui_ops =
117 { 145 {
118 new_list, 146 new_list,
119 new_node, 147 new_node,
120 NULL, 148 NULL,
121 node_update, /* This doesn't do crap */ 149 node_update, /* This doesn't do crap */
200 { 228 {
201 GaimGroup *group; 229 GaimGroup *group;
202 GaimBlistNode *node = (GaimBlistNode *)chat; 230 GaimBlistNode *node = (GaimBlistNode *)chat;
203 if (node->ui_data) 231 if (node->ui_data)
204 return; 232 return;
233 if (!gaim_account_is_connected(chat->account))
234 return;
205 235
206 group = gaim_chat_get_group(chat); 236 group = gaim_chat_get_group(chat);
207 add_node((GaimBlistNode*)group, ggblist); 237 add_node((GaimBlistNode*)group, ggblist);
208 238
209 gnt_tree_remove(GNT_TREE(ggblist->tree), chat); 239 gnt_tree_remove(GNT_TREE(ggblist->tree), chat);
288 gnt_append_menu_action(GntTree *tree, GaimMenuAction *action, gpointer parent) 318 gnt_append_menu_action(GntTree *tree, GaimMenuAction *action, gpointer parent)
289 { 319 {
290 GList *list; 320 GList *list;
291 if (action == NULL) 321 if (action == NULL)
292 return; 322 return;
293 323
294 gnt_tree_add_row_after(tree, action, 324 gnt_tree_add_row_after(tree, action,
295 gnt_tree_create_row(tree, action->label), parent, NULL); 325 gnt_tree_create_row(tree, action->label), parent, NULL);
296 for (list = action->children; list; list = list->next) 326 for (list = action->children; list; list = list->next)
297 gnt_append_menu_action(tree, list->data, action); 327 gnt_append_menu_action(tree, list->data, action);
298 } 328 }
365 } 395 }
366 396
367 add_custom_action(tree, _("View Log"), 397 add_custom_action(tree, _("View Log"),
368 GAIM_CALLBACK(gg_blist_view_log_cb)), buddy); 398 GAIM_CALLBACK(gg_blist_view_log_cb)), buddy);
369 #endif 399 #endif
370 400
371 /* Protocol actions */ 401 /* Protocol actions */
372 append_proto_menu(tree, 402 append_proto_menu(tree,
373 gaim_account_get_connection(gaim_buddy_get_account(buddy)), 403 gaim_account_get_connection(gaim_buddy_get_account(buddy)),
374 (GaimBlistNode*)buddy); 404 (GaimBlistNode*)buddy);
375 } 405 }
434 name = gaim_chat_get_name((GaimChat*)node); 464 name = gaim_chat_get_name((GaimChat*)node);
435 else if (GAIM_BLIST_NODE_IS_GROUP(node)) 465 else if (GAIM_BLIST_NODE_IS_GROUP(node))
436 name = ((GaimGroup*)node)->name; 466 name = ((GaimGroup*)node)->name;
437 else 467 else
438 g_return_if_reached(); 468 g_return_if_reached();
439 469
440 prompt = g_strdup_printf(_("Please enter the new name for %s"), name); 470 prompt = g_strdup_printf(_("Please enter the new name for %s"), name);
441 471
442 gaim_request_input(node, _("Rename"), prompt, _("Enter empty string to reset the name."), 472 gaim_request_input(node, _("Rename"), prompt, _("Enter empty string to reset the name."),
443 name, FALSE, FALSE, NULL, _("Rename"), G_CALLBACK(rename_blist_node), 473 name, FALSE, FALSE, NULL, _("Rename"), G_CALLBACK(rename_blist_node),
444 _("Cancel"), NULL, node); 474 _("Cancel"), NULL, node);
445 475
446 g_free(prompt); 476 g_free(prompt);
680 if (ggblist->context) 710 if (ggblist->context)
681 { 711 {
682 ret = gnt_widget_key_pressed(ggblist->context, text); 712 ret = gnt_widget_key_pressed(ggblist->context, text);
683 stop = TRUE; 713 stop = TRUE;
684 } 714 }
685 715
686 if (text[0] == 27) 716 if (text[0] == 27)
687 { 717 {
688 if (strcmp(text + 1, GNT_KEY_POPUP) == 0) 718 if (strcmp(text + 1, GNT_KEY_POPUP) == 0)
689 { 719 {
690 draw_context_menu(ggblist); 720 draw_context_menu(ggblist);
758 while (node) 788 while (node)
759 { 789 {
760 node->ui_data = NULL; 790 node->ui_data = NULL;
761 node = gaim_blist_node_next(node, TRUE); 791 node = gaim_blist_node_next(node, TRUE);
762 } 792 }
763 793
794 remove_typing_cb(NULL);
764 remove_peripherals(ggblist); 795 remove_peripherals(ggblist);
765 g_free(ggblist); 796 g_free(ggblist);
766 ggblist = NULL; 797 ggblist = NULL;
767 } 798 }
768 799
775 list = gaim_get_blist(); 806 list = gaim_get_blist();
776 node = gaim_blist_get_root(); 807 node = gaim_blist_get_root();
777 while (node) 808 while (node)
778 { 809 {
779 node_update(list, node); 810 node_update(list, node);
780 node = gaim_blist_node_next(node, TRUE); 811 node = gaim_blist_node_next(node, FALSE);
781 } 812 }
813 }
814
815 static void
816 destroy_status_list(GList *list)
817 {
818 g_list_foreach(list, (GFunc)g_free, NULL);
819 g_list_free(list);
820 }
821
822 static void
823 populate_status_dropdown()
824 {
825 int i;
826 GList *iter;
827 GList *items = NULL;
828
829 /* First the primitives */
830 GaimStatusPrimitive prims[] = {GAIM_STATUS_AVAILABLE, GAIM_STATUS_AWAY,
831 GAIM_STATUS_INVISIBLE, GAIM_STATUS_OFFLINE, GAIM_STATUS_UNSET};
832
833 for (i = 0; prims[i] != GAIM_STATUS_UNSET; i++)
834 {
835 StatusBoxItem *item = g_new0(StatusBoxItem, 1);
836 item->type = STATUS_PRIMITIVE;
837 item->u.prim = prims[i];
838 items = g_list_prepend(items, item);
839 gnt_combo_box_add_data(GNT_COMBO_BOX(ggblist->status), item,
840 gaim_primitive_get_name_from_type(prims[i]));
841 }
842
843 /* Now the popular statuses */
844 for (iter = gaim_savedstatuses_get_popular(6); iter; iter = iter->next)
845 {
846 StatusBoxItem *item = g_new0(StatusBoxItem, 1);
847 item->type = STATUS_SAVED;
848 item->u.saved = iter->data;
849 items = g_list_prepend(items, item);
850 gnt_combo_box_add_data(GNT_COMBO_BOX(ggblist->status), item,
851 gaim_savedstatus_get_title(iter->data));
852 }
853
854 /* The keys for the combobox are created here, and never used
855 * anywhere else. So make sure the keys are freed when the widget
856 * is destroyed. */
857 g_object_set_data_full(G_OBJECT(ggblist->status), "list of statuses", items, (GDestroyNotify)destroy_status_list);
782 } 858 }
783 859
784 void gg_blist_init() 860 void gg_blist_init()
785 { 861 {
786 gaim_prefs_add_none(PREF_ROOT); 862 gaim_prefs_add_none(PREF_ROOT);
794 gg_blist_show(); 870 gg_blist_show();
795 871
796 return; 872 return;
797 } 873 }
798 874
875 static void
876 remove_typing_cb(gpointer null)
877 {
878 GaimSavedStatus *current;
879 const char *message, *newmessage;
880 GaimStatusPrimitive prim, newprim;
881 StatusBoxItem *item;
882
883 current = gaim_savedstatus_get_current();
884 message = gaim_savedstatus_get_message(current);
885 prim = gaim_savedstatus_get_type(current);
886
887 newmessage = gnt_entry_get_text(GNT_ENTRY(ggblist->statustext));
888 item = gnt_combo_box_get_selected_data(GNT_COMBO_BOX(ggblist->status));
889 g_return_if_fail(item->type == STATUS_PRIMITIVE);
890 newprim = item->u.prim;
891
892 if (newprim != prim || ((message && !newmessage) ||
893 (!message && newmessage) ||
894 (message && newmessage && g_utf8_collate(message, newmessage) != 0)))
895 {
896 GaimSavedStatus *status = gaim_savedstatus_find_transient_by_type_and_message(newprim, newmessage);
897 /* Holy Crap! That's a LAWNG function name */
898 if (status == NULL)
899 {
900 status = gaim_savedstatus_new(NULL, newprim);
901 gaim_savedstatus_set_message(status, newmessage);
902 }
903
904 gaim_savedstatus_activate(status);
905 }
906
907 gnt_box_give_focus_to_child(GNT_BOX(ggblist->window), ggblist->tree);
908 g_source_remove(ggblist->typing);
909 ggblist->typing = 0;
910 }
911
912 static void
913 status_selection_changed(GntComboBox *box, StatusBoxItem *old, StatusBoxItem *now, gpointer null)
914 {
915 gnt_entry_set_text(GNT_ENTRY(ggblist->statustext), NULL);
916 if (now->type == STATUS_SAVED)
917 {
918 /* Set the status immediately */
919 gaim_savedstatus_activate(now->u.saved);
920 }
921 else if (now->type == STATUS_PRIMITIVE)
922 {
923 /* Move the focus to the entry box */
924 gnt_box_move_focus(GNT_BOX(ggblist->window), 1);
925 ggblist->typing = g_timeout_add(TYPING_TIMEOUT, (GSourceFunc)remove_typing_cb, NULL);
926 }
927 else
928 g_return_if_reached();
929 }
930
931 static gboolean
932 status_text_changed(GntEntry *entry, const char *text, gpointer null)
933 {
934 if (text[0] == 27 && ggblist->typing == 0)
935 return FALSE;
936
937 g_source_remove(ggblist->typing);
938 ggblist->typing = 0;
939
940 if (text[0] == '\r' && text[1] == 0)
941 {
942 /* Set the status only after you press 'Enter' */
943 remove_typing_cb(NULL);
944 return TRUE;
945 }
946
947 ggblist->typing = g_timeout_add(TYPING_TIMEOUT, (GSourceFunc)remove_typing_cb, NULL);
948 return FALSE;
949 }
950
951 static void
952 savedstatus_changed(GaimSavedStatus *now, GaimSavedStatus *old)
953 {
954 /* Block the signals we don't want to emit */
955 GList *list;
956 GaimStatusPrimitive prim;
957 const char *message;
958
959 if (!ggblist)
960 return;
961
962 g_signal_handlers_block_matched(ggblist->status, G_SIGNAL_MATCH_FUNC,
963 0, 0, NULL, status_selection_changed, NULL);
964 g_signal_handlers_block_matched(ggblist->statustext, G_SIGNAL_MATCH_FUNC,
965 0, 0, NULL, status_text_changed, NULL);
966
967 prim = gaim_savedstatus_get_type(now);
968 message = gaim_savedstatus_get_message(now);
969
970 list = g_object_get_data(G_OBJECT(ggblist->status), "list of statuses");
971 for (; list; list = list->next)
972 {
973 StatusBoxItem *item = list->data;
974 if (item->type == STATUS_PRIMITIVE && item->u.prim == prim)
975 {
976 gnt_combo_box_set_selected(GNT_COMBO_BOX(ggblist->status), item);
977 gnt_entry_set_text(GNT_ENTRY(ggblist->statustext), message);
978 gnt_widget_draw(ggblist->status);
979 break;
980 }
981 }
982
983 g_signal_handlers_unblock_matched(ggblist->status, G_SIGNAL_MATCH_FUNC,
984 0, 0, NULL, status_selection_changed, NULL);
985 g_signal_handlers_unblock_matched(ggblist->statustext, G_SIGNAL_MATCH_FUNC,
986 0, 0, NULL, status_text_changed, NULL);
987 }
988
799 void gg_blist_show() 989 void gg_blist_show()
800 { 990 {
801 if (ggblist) 991 if (ggblist)
802 return; 992 return;
803 993
804 ggblist = g_new0(GGBlist, 1); 994 ggblist = g_new0(GGBlist, 1);
805 995
806 gaim_get_blist()->ui_data = ggblist; 996 gaim_get_blist()->ui_data = ggblist;
807 997
808 ggblist->window = gnt_box_new(FALSE, FALSE); 998 ggblist->window = gnt_vbox_new(FALSE);
809 gnt_widget_set_name(ggblist->window, "buddylist"); 999 gnt_widget_set_name(ggblist->window, "buddylist");
810 gnt_box_set_toplevel(GNT_BOX(ggblist->window), TRUE); 1000 gnt_box_set_toplevel(GNT_BOX(ggblist->window), TRUE);
811 gnt_box_set_title(GNT_BOX(ggblist->window), _("Buddy List")); 1001 gnt_box_set_title(GNT_BOX(ggblist->window), _("Buddy List"));
812 gnt_box_set_pad(GNT_BOX(ggblist->window), 0); 1002 gnt_box_set_pad(GNT_BOX(ggblist->window), 0);
813 1003
818 gaim_prefs_get_int(PREF_ROOT "/size/height")); 1008 gaim_prefs_get_int(PREF_ROOT "/size/height"));
819 gnt_widget_set_position(ggblist->window, gaim_prefs_get_int(PREF_ROOT "/position/x"), 1009 gnt_widget_set_position(ggblist->window, gaim_prefs_get_int(PREF_ROOT "/position/x"),
820 gaim_prefs_get_int(PREF_ROOT "/position/y")); 1010 gaim_prefs_get_int(PREF_ROOT "/position/y"));
821 1011
822 gnt_box_add_widget(GNT_BOX(ggblist->window), ggblist->tree); 1012 gnt_box_add_widget(GNT_BOX(ggblist->window), ggblist->tree);
1013
1014 gnt_box_add_widget(GNT_BOX(ggblist->window), gnt_hline_new());
1015
1016 ggblist->status = gnt_combo_box_new();
1017 gnt_box_add_widget(GNT_BOX(ggblist->window), ggblist->status);
1018 ggblist->statustext = gnt_entry_new(NULL);
1019 gnt_box_add_widget(GNT_BOX(ggblist->window), ggblist->statustext);
1020
1021 populate_status_dropdown();
1022
823 gnt_widget_show(ggblist->window); 1023 gnt_widget_show(ggblist->window);
824 1024
825 gaim_signal_connect(gaim_blist_get_handle(), "buddy-status-changed", gg_blist_get_handle(), 1025 gaim_signal_connect(gaim_blist_get_handle(), "buddy-status-changed", gg_blist_get_handle(),
826 GAIM_CALLBACK(buddy_status_changed), ggblist); 1026 GAIM_CALLBACK(buddy_status_changed), ggblist);
827 gaim_signal_connect(gaim_blist_get_handle(), "buddy-idle-changed", gg_blist_get_handle(), 1027 gaim_signal_connect(gaim_blist_get_handle(), "buddy-idle-changed", gg_blist_get_handle(),
828 GAIM_CALLBACK(buddy_idle_changed), ggblist); 1028 GAIM_CALLBACK(buddy_idle_changed), ggblist);
829 1029
830 #if 0 1030 #if 0
831 gaim_signal_connect(gaim_blist_get_handle(), "buddy-signed-on", gg_blist_get_handle(), 1031 gaim_signal_connect(gaim_blist_get_handle(), "buddy-signed-on", gg_blist_get_handle(),
832 GAIM_CALLBACK(buddy_signed_on), ggblist); 1032 GAIM_CALLBACK(buddy_signed_on), ggblist);
833 gaim_signal_connect(gaim_blist_get_handle(), "buddy-signed-off", gg_blist_get_handle(), 1033 gaim_signal_connect(gaim_blist_get_handle(), "buddy-signed-off", gg_blist_get_handle(),
834 GAIM_CALLBACK(buddy_signed_off), ggblist); 1034 GAIM_CALLBACK(buddy_signed_off), ggblist);
852 ggblist, 0, G_CONNECT_AFTER | G_CONNECT_SWAPPED); 1052 ggblist, 0, G_CONNECT_AFTER | G_CONNECT_SWAPPED);
853 g_signal_connect(G_OBJECT(ggblist->tree), "size_changed", G_CALLBACK(size_changed_cb), NULL); 1053 g_signal_connect(G_OBJECT(ggblist->tree), "size_changed", G_CALLBACK(size_changed_cb), NULL);
854 g_signal_connect(G_OBJECT(ggblist->window), "position_set", G_CALLBACK(save_position_cb), NULL); 1054 g_signal_connect(G_OBJECT(ggblist->window), "position_set", G_CALLBACK(save_position_cb), NULL);
855 g_signal_connect(G_OBJECT(ggblist->window), "destroy", G_CALLBACK(reset_blist_window), NULL); 1055 g_signal_connect(G_OBJECT(ggblist->window), "destroy", G_CALLBACK(reset_blist_window), NULL);
856 1056
1057 /* Status signals */
1058 gaim_signal_connect(gaim_savedstatuses_get_handle(), "savedstatus-changed", gg_blist_get_handle(),
1059 GAIM_CALLBACK(savedstatus_changed), NULL);
1060 g_signal_connect(G_OBJECT(ggblist->status), "selection_changed",
1061 G_CALLBACK(status_selection_changed), NULL);
1062 g_signal_connect(G_OBJECT(ggblist->statustext), "key_pressed",
1063 G_CALLBACK(status_text_changed), NULL);
1064
857 populate_buddylist(); 1065 populate_buddylist();
1066
1067 savedstatus_changed(gaim_savedstatus_get_current(), NULL);
858 } 1068 }
859 1069
860 void gg_blist_uninit() 1070 void gg_blist_uninit()
861 { 1071 {
862 if (ggblist == NULL) 1072 if (ggblist == NULL)