| 675 |
663 |
| 676 gtk_widget_set_sensitive(req_data->ok_button, |
664 gtk_widget_set_sensitive(req_data->ok_button, |
| 677 gaim_request_fields_all_required_filled(field->group->fields_list)); |
665 gaim_request_fields_all_required_filled(field->group->fields_list)); |
| 678 } |
666 } |
| 679 |
667 |
| 680 #ifndef NEW_STYLE_COMPLETION |
|
| 681 static gboolean |
|
| 682 completion_entry_event(GtkEditable *entry, GdkEventKey *event, |
|
| 683 GaimGtkCompletionData *data) |
|
| 684 { |
|
| 685 int pos, end_pos; |
|
| 686 |
|
| 687 if (event->type == GDK_KEY_PRESS && event->keyval == GDK_Tab) |
|
| 688 { |
|
| 689 gtk_editable_get_selection_bounds(entry, &pos, &end_pos); |
|
| 690 |
|
| 691 if (data->completion_started && |
|
| 692 pos != end_pos && pos > 1 && |
|
| 693 end_pos == strlen(gtk_entry_get_text(GTK_ENTRY(entry)))) |
|
| 694 { |
|
| 695 gtk_editable_select_region(entry, 0, 0); |
|
| 696 gtk_editable_set_position(entry, -1); |
|
| 697 |
|
| 698 return TRUE; |
|
| 699 } |
|
| 700 } |
|
| 701 else if (event->type == GDK_KEY_PRESS && event->length > 0) |
|
| 702 { |
|
| 703 char *prefix, *nprefix; |
|
| 704 |
|
| 705 gtk_editable_get_selection_bounds(entry, &pos, &end_pos); |
|
| 706 |
|
| 707 if (data->completion_started && |
|
| 708 pos != end_pos && pos > 1 && |
|
| 709 end_pos == strlen(gtk_entry_get_text(GTK_ENTRY(entry)))) |
|
| 710 { |
|
| 711 char *temp; |
|
| 712 |
|
| 713 temp = gtk_editable_get_chars(entry, 0, pos); |
|
| 714 prefix = g_strconcat(temp, event->string, NULL); |
|
| 715 g_free(temp); |
|
| 716 } |
|
| 717 else if (pos == end_pos && pos > 1 && |
|
| 718 end_pos == strlen(gtk_entry_get_text(GTK_ENTRY(entry)))) |
|
| 719 { |
|
| 720 prefix = g_strconcat(gtk_entry_get_text(GTK_ENTRY(entry)), |
|
| 721 event->string, NULL); |
|
| 722 } |
|
| 723 else |
|
| 724 return FALSE; |
|
| 725 |
|
| 726 pos = strlen(prefix); |
|
| 727 nprefix = NULL; |
|
| 728 |
|
| 729 g_completion_complete(data->completion, prefix, &nprefix); |
|
| 730 |
|
| 731 if (nprefix != NULL) |
|
| 732 { |
|
| 733 gtk_entry_set_text(GTK_ENTRY(entry), nprefix); |
|
| 734 gtk_editable_set_position(entry, pos); |
|
| 735 gtk_editable_select_region(entry, pos, -1); |
|
| 736 |
|
| 737 data->completion_started = TRUE; |
|
| 738 |
|
| 739 g_free(nprefix); |
|
| 740 g_free(prefix); |
|
| 741 |
|
| 742 return TRUE; |
|
| 743 } |
|
| 744 |
|
| 745 g_free(prefix); |
|
| 746 } |
|
| 747 |
|
| 748 return FALSE; |
|
| 749 } |
|
| 750 |
|
| 751 static void |
|
| 752 destroy_completion_data(GtkWidget *w, GaimGtkCompletionData *data) |
|
| 753 { |
|
| 754 g_list_foreach(data->completion->items, (GFunc)g_free, NULL); |
|
| 755 g_completion_free(data->completion); |
|
| 756 |
|
| 757 g_free(data); |
|
| 758 } |
|
| 759 #endif /* !NEW_STYLE_COMPLETION */ |
|
| 760 |
|
| 761 #ifdef NEW_STYLE_COMPLETION |
|
| 762 static gboolean screenname_completion_match_func(GtkEntryCompletion *completion, |
|
| 763 const gchar *key, GtkTreeIter *iter, gpointer user_data) |
|
| 764 { |
|
| 765 GtkTreeModel *model; |
|
| 766 GValue val1; |
|
| 767 GValue val2; |
|
| 768 const char *tmp; |
|
| 769 |
|
| 770 model = gtk_entry_completion_get_model (completion); |
|
| 771 |
|
| 772 val1.g_type = 0; |
|
| 773 gtk_tree_model_get_value(model, iter, 2, &val1); |
|
| 774 tmp = g_value_get_string(&val1); |
|
| 775 if (tmp != NULL && gaim_str_has_prefix(tmp, key)) |
|
| 776 { |
|
| 777 g_value_unset(&val1); |
|
| 778 return TRUE; |
|
| 779 } |
|
| 780 g_value_unset(&val1); |
|
| 781 |
|
| 782 val2.g_type = 0; |
|
| 783 gtk_tree_model_get_value(model, iter, 3, &val2); |
|
| 784 tmp = g_value_get_string(&val2); |
|
| 785 if (tmp != NULL && gaim_str_has_prefix(tmp, key)) |
|
| 786 { |
|
| 787 g_value_unset(&val2); |
|
| 788 return TRUE; |
|
| 789 } |
|
| 790 g_value_unset(&val2); |
|
| 791 |
|
| 792 return FALSE; |
|
| 793 } |
|
| 794 |
|
| 795 static gboolean screenname_completion_match_selected_cb(GtkEntryCompletion *completion, |
|
| 796 GtkTreeModel *model, GtkTreeIter *iter, gpointer *user_data) |
|
| 797 { |
|
| 798 GValue val; |
|
| 799 GaimRequestField *screen_field = user_data[1]; |
|
| 800 GList *fields = screen_field->group->fields; |
|
| 801 GaimAccount *account; |
|
| 802 |
|
| 803 val.g_type = 0; |
|
| 804 gtk_tree_model_get_value(model, iter, 1, &val); |
|
| 805 gtk_entry_set_text(GTK_ENTRY(user_data[0]), g_value_get_string(&val)); |
|
| 806 g_value_unset(&val); |
|
| 807 |
|
| 808 gtk_tree_model_get_value(model, iter, 4, &val); |
|
| 809 account = g_value_get_pointer(&val); |
|
| 810 g_value_unset(&val); |
|
| 811 |
|
| 812 if (account == NULL) |
|
| 813 return TRUE; |
|
| 814 |
|
| 815 do { |
|
| 816 GaimRequestField *field = fields->data; |
|
| 817 |
|
| 818 if (gaim_request_field_get_type(field) == GAIM_REQUEST_FIELD_ACCOUNT) { |
|
| 819 const char *type_hint = gaim_request_field_get_type_hint(field); |
|
| 820 |
|
| 821 if (type_hint != NULL && !strcmp(type_hint, "account")) { |
|
| 822 /* We found the corresponding account field. */ |
|
| 823 GtkOptionMenu *optmenu = GTK_OPTION_MENU(field->ui_data); |
|
| 824 |
|
| 825 /* Set the account in the request API. */ |
|
| 826 gaim_request_field_account_set_value(field, account); |
|
| 827 |
|
| 828 if (optmenu != NULL) { |
|
| 829 GList *items = GTK_MENU_SHELL(gtk_option_menu_get_menu(optmenu))->children; |
|
| 830 guint index = 0; |
|
| 831 |
|
| 832 do { |
|
| 833 if (account == g_object_get_data(G_OBJECT(items->data), "account")) { |
|
| 834 /* Set the account in the GUI. */ |
|
| 835 gtk_option_menu_set_history(GTK_OPTION_MENU(field->ui_data), index); |
|
| 836 return TRUE; |
|
| 837 } |
|
| 838 index++; |
|
| 839 } while ((items = items->next) != NULL); |
|
| 840 } |
|
| 841 |
|
| 842 return TRUE; |
|
| 843 } |
|
| 844 } |
|
| 845 |
|
| 846 } while ((fields = fields->next) != NULL); |
|
| 847 |
|
| 848 return TRUE; |
|
| 849 } |
|
| 850 |
|
| 851 static void |
|
| 852 add_screenname_autocomplete_entry(GtkListStore *store, const char *buddy_alias, const char *contact_alias, |
|
| 853 const GaimAccount *account, const char *screenname) |
|
| 854 { |
|
| 855 GtkTreeIter iter; |
|
| 856 gboolean completion_added = FALSE; |
|
| 857 gchar *normalized_screenname; |
|
| 858 gchar *tmp; |
|
| 859 |
|
| 860 tmp = g_utf8_normalize(screenname, -1, G_NORMALIZE_DEFAULT); |
|
| 861 normalized_screenname = g_utf8_casefold(tmp, -1); |
|
| 862 g_free(tmp); |
|
| 863 |
|
| 864 /* There's no sense listing things like: 'xxx "xxx"' |
|
| 865 when the screenname and buddy alias match. */ |
|
| 866 if (buddy_alias && strcmp(buddy_alias, screenname)) { |
|
| 867 char *completion_entry = g_strdup_printf("%s \"%s\"", screenname, buddy_alias); |
|
| 868 char *tmp2 = g_utf8_normalize(buddy_alias, -1, G_NORMALIZE_DEFAULT); |
|
| 869 |
|
| 870 tmp = g_utf8_casefold(tmp2, -1); |
|
| 871 g_free(tmp2); |
|
| 872 |
|
| 873 gtk_list_store_append(store, &iter); |
|
| 874 gtk_list_store_set(store, &iter, |
|
| 875 0, completion_entry, |
|
| 876 1, screenname, |
|
| 877 2, normalized_screenname, |
|
| 878 3, tmp, |
|
| 879 4, account, |
|
| 880 -1); |
|
| 881 g_free(completion_entry); |
|
| 882 g_free(tmp); |
|
| 883 completion_added = TRUE; |
|
| 884 } |
|
| 885 |
|
| 886 /* There's no sense listing things like: 'xxx "xxx"' |
|
| 887 when the screenname and contact alias match. */ |
|
| 888 if (contact_alias && strcmp(contact_alias, screenname)) { |
|
| 889 /* We don't want duplicates when the contact and buddy alias match. */ |
|
| 890 if (!buddy_alias || strcmp(contact_alias, buddy_alias)) { |
|
| 891 char *completion_entry = g_strdup_printf("%s \"%s\"", |
|
| 892 screenname, contact_alias); |
|
| 893 char *tmp2 = g_utf8_normalize(contact_alias, -1, G_NORMALIZE_DEFAULT); |
|
| 894 |
|
| 895 tmp = g_utf8_casefold(tmp2, -1); |
|
| 896 g_free(tmp2); |
|
| 897 |
|
| 898 gtk_list_store_append(store, &iter); |
|
| 899 gtk_list_store_set(store, &iter, |
|
| 900 0, completion_entry, |
|
| 901 1, screenname, |
|
| 902 2, normalized_screenname, |
|
| 903 3, tmp, |
|
| 904 4, account, |
|
| 905 -1); |
|
| 906 g_free(completion_entry); |
|
| 907 g_free(tmp); |
|
| 908 completion_added = TRUE; |
|
| 909 } |
|
| 910 } |
|
| 911 |
|
| 912 if (completion_added == FALSE) { |
|
| 913 /* Add the buddy's screenname. */ |
|
| 914 gtk_list_store_append(store, &iter); |
|
| 915 gtk_list_store_set(store, &iter, |
|
| 916 0, screenname, |
|
| 917 1, screenname, |
|
| 918 2, normalized_screenname, |
|
| 919 3, NULL, |
|
| 920 4, account, |
|
| 921 -1); |
|
| 922 } |
|
| 923 |
|
| 924 g_free(normalized_screenname); |
|
| 925 } |
|
| 926 #endif /* NEW_STYLE_COMPLETION */ |
|
| 927 |
|
| 928 static void get_log_set_name(GaimLogSet *set, gpointer value, gpointer **set_hash_data) |
|
| 929 { |
|
| 930 /* 1. Don't show buddies because we will have gotten them already. |
|
| 931 * 2. Only show those with non-NULL accounts that are currently connected. |
|
| 932 * 3. The boxes that use this autocomplete code handle only IMs. */ |
|
| 933 if (!set->buddy && |
|
| 934 (GPOINTER_TO_INT(set_hash_data[1]) || |
|
| 935 (set->account != NULL && gaim_account_is_connected(set->account))) && |
|
| 936 set->type == GAIM_LOG_IM) { |
|
| 937 #ifdef NEW_STYLE_COMPLETION |
|
| 938 add_screenname_autocomplete_entry((GtkListStore *)set_hash_data[0], |
|
| 939 NULL, NULL, set->account, set->name); |
|
| 940 #else |
|
| 941 GList **items = ((GList **)set_hash_data[0]); |
|
| 942 /* Steal the name for the GCompletion. */ |
|
| 943 *items = g_list_append(*items, set->name); |
|
| 944 set->name = set->normalized_name = NULL; |
|
| 945 #endif /* NEW_STYLE_COMPLETION */ |
|
| 946 } |
|
| 947 } |
|
| 948 |
|
| 949 static void |
|
| 950 setup_screenname_autocomplete(GtkWidget *entry, GaimRequestField *field, gboolean all) |
|
| 951 { |
|
| 952 #ifdef NEW_STYLE_COMPLETION |
|
| 953 /* Store the displayed completion value, the screenname, the UTF-8 normalized & casefolded screenname, |
|
| 954 * the UTF-8 normalized & casefolded value for comparison, and the account. */ |
|
| 955 GtkListStore *store = gtk_list_store_new(5, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER); |
|
| 956 |
|
| 957 GaimBlistNode *gnode, *cnode, *bnode; |
|
| 958 GHashTable *sets; |
|
| 959 gpointer set_hash_data[] = {store, GINT_TO_POINTER(all)}; |
|
| 960 GtkEntryCompletion *completion; |
|
| 961 gpointer *data; |
|
| 962 |
|
| 963 for (gnode = gaim_get_blist()->root; gnode != NULL; gnode = gnode->next) |
|
| 964 { |
|
| 965 if (!GAIM_BLIST_NODE_IS_GROUP(gnode)) |
|
| 966 continue; |
|
| 967 |
|
| 968 for (cnode = gnode->child; cnode != NULL; cnode = cnode->next) |
|
| 969 { |
|
| 970 if (!GAIM_BLIST_NODE_IS_CONTACT(cnode)) |
|
| 971 continue; |
|
| 972 |
|
| 973 for (bnode = cnode->child; bnode != NULL; bnode = bnode->next) |
|
| 974 { |
|
| 975 GaimBuddy *buddy = (GaimBuddy *)bnode; |
|
| 976 |
|
| 977 if (!all && !gaim_account_is_connected(buddy->account)) |
|
| 978 continue; |
|
| 979 |
|
| 980 add_screenname_autocomplete_entry(store, |
|
| 981 ((GaimContact *)cnode)->alias, |
|
| 982 gaim_buddy_get_contact_alias(buddy), |
|
| 983 buddy->account, |
|
| 984 buddy->name |
|
| 985 ); |
|
| 986 } |
|
| 987 } |
|
| 988 } |
|
| 989 |
|
| 990 sets = gaim_log_get_log_sets(); |
|
| 991 g_hash_table_foreach(sets, (GHFunc)get_log_set_name, &set_hash_data); |
|
| 992 g_hash_table_destroy(sets); |
|
| 993 |
|
| 994 |
|
| 995 /* Sort the completion list by screenname. */ |
|
| 996 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(store), |
|
| 997 1, GTK_SORT_ASCENDING); |
|
| 998 |
|
| 999 completion = gtk_entry_completion_new(); |
|
| 1000 gtk_entry_completion_set_match_func(completion, screenname_completion_match_func, NULL, NULL); |
|
| 1001 |
|
| 1002 data = g_new0(gpointer, 2); |
|
| 1003 data[0] = entry; |
|
| 1004 data[1] = field; |
|
| 1005 g_signal_connect(G_OBJECT(completion), "match-selected", |
|
| 1006 G_CALLBACK(screenname_completion_match_selected_cb), data); |
|
| 1007 |
|
| 1008 gtk_entry_set_completion(GTK_ENTRY(entry), completion); |
|
| 1009 g_object_unref(completion); |
|
| 1010 |
|
| 1011 gtk_entry_completion_set_model(completion, GTK_TREE_MODEL(store)); |
|
| 1012 g_object_unref(store); |
|
| 1013 |
|
| 1014 gtk_entry_completion_set_text_column(completion, 0); |
|
| 1015 |
|
| 1016 #else /* !NEW_STYLE_COMPLETION */ |
|
| 1017 GaimGtkCompletionData *data; |
|
| 1018 GaimBlistNode *gnode, *cnode, *bnode; |
|
| 1019 GList *item = g_list_append(NULL, NULL); |
|
| 1020 GHashTable *sets; |
|
| 1021 gpointer set_hash_data[2]; |
|
| 1022 |
|
| 1023 data = g_new0(GaimGtkCompletionData, 1); |
|
| 1024 |
|
| 1025 data->completion = g_completion_new(NULL); |
|
| 1026 |
|
| 1027 g_completion_set_compare(data->completion, g_ascii_strncasecmp); |
|
| 1028 |
|
| 1029 for (gnode = gaim_get_blist()->root; gnode != NULL; gnode = gnode->next) |
|
| 1030 { |
|
| 1031 if (!GAIM_BLIST_NODE_IS_GROUP(gnode)) |
|
| 1032 continue; |
|
| 1033 |
|
| 1034 for (cnode = gnode->child; cnode != NULL; cnode = cnode->next) |
|
| 1035 { |
|
| 1036 if (!GAIM_BLIST_NODE_IS_CONTACT(cnode)) |
|
| 1037 continue; |
|
| 1038 |
|
| 1039 for (bnode = cnode->child; bnode != NULL; bnode = bnode->next) |
|
| 1040 { |
|
| 1041 GaimBuddy *buddy = (GaimBuddy *)bnode; |
|
| 1042 |
|
| 1043 if (!all && !gaim_account_is_connected(buddy->account)) |
|
| 1044 continue; |
|
| 1045 |
|
| 1046 item->data = g_strdup(buddy->name); |
|
| 1047 g_completion_add_items(data->completion, item); |
|
| 1048 } |
|
| 1049 } |
|
| 1050 } |
|
| 1051 g_list_free(item); |
|
| 1052 |
|
| 1053 sets = gaim_log_get_log_sets(); |
|
| 1054 item = NULL; |
|
| 1055 set_hash_data[0] = &item; |
|
| 1056 set_hash_data[1] = GINT_TO_POINTER(all); |
|
| 1057 g_hash_table_foreach(sets, (GHFunc)get_log_set_name, &set_hash_data); |
|
| 1058 g_hash_table_destroy(sets); |
|
| 1059 g_completion_add_items(data->completion, item); |
|
| 1060 g_list_free(item); |
|
| 1061 |
|
| 1062 g_signal_connect(G_OBJECT(entry), "event", |
|
| 1063 G_CALLBACK(completion_entry_event), data); |
|
| 1064 g_signal_connect(G_OBJECT(entry), "destroy", |
|
| 1065 G_CALLBACK(destroy_completion_data), data); |
|
| 1066 |
|
| 1067 #endif /* !NEW_STYLE_COMPLETION */ |
|
| 1068 } |
|
| 1069 |
|
| 1070 static void |
668 static void |
| 1071 setup_entry_field(GtkWidget *entry, GaimRequestField *field) |
669 setup_entry_field(GtkWidget *entry, GaimRequestField *field) |
| 1072 { |
670 { |
| 1073 const char *type_hint; |
671 const char *type_hint; |
| 1074 |
672 |