Mercurial > pidgin
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) |