Mercurial > pidgin
comparison src/gtkrequest.c @ 11177:3924db2b1ca8
[gaim-migrate @ 13285]
Performance optimizing the log set and screenname autocompletion code.
committer: Tailor Script <tailor@pidgin.im>
author | Richard Laager <rlaager@wiktel.com> |
---|---|
date | Mon, 01 Aug 2005 04:08:27 +0000 |
parents | e6b4badef34d |
children | 28ac54de3024 |
comparison
equal
deleted
inserted
replaced
11176:6932df31225f | 11177:3924db2b1ca8 |
---|---|
42 typedef struct | 42 typedef struct |
43 { | 43 { |
44 GaimRequestType type; | 44 GaimRequestType type; |
45 | 45 |
46 void *user_data; | 46 void *user_data; |
47 GtkWidget *dialog; | 47 GtkWidget *dialog; |
48 | 48 |
49 GtkWidget *ok_button; | 49 GtkWidget *ok_button; |
50 | 50 |
51 size_t cb_count; | 51 size_t cb_count; |
52 GCallback *cbs; | 52 GCallback *cbs; |
645 | 645 |
646 gtk_widget_set_sensitive(req_data->ok_button, | 646 gtk_widget_set_sensitive(req_data->ok_button, |
647 gaim_request_fields_all_required_filled(field->group->fields_list)); | 647 gaim_request_fields_all_required_filled(field->group->fields_list)); |
648 } | 648 } |
649 | 649 |
650 static GList * | |
651 get_screenname_completion_data(gboolean all) | |
652 { | |
653 GList *names = NULL; | |
654 GaimBlistNode *gnode, *cnode, *bnode; | |
655 GList *log_sets; | |
656 GList *log_set; | |
657 | |
658 for (gnode = gaim_get_blist()->root; gnode != NULL; gnode = gnode->next) | |
659 { | |
660 if (!GAIM_BLIST_NODE_IS_GROUP(gnode)) | |
661 continue; | |
662 | |
663 for (cnode = gnode->child; cnode != NULL; cnode = cnode->next) | |
664 { | |
665 if (!GAIM_BLIST_NODE_IS_CONTACT(cnode)) | |
666 continue; | |
667 | |
668 for (bnode = cnode->child; bnode != NULL; bnode = bnode->next) | |
669 { | |
670 GaimBuddy *buddy = (GaimBuddy *)bnode; | |
671 | |
672 if (!all && !gaim_account_is_connected(buddy->account)) | |
673 continue; | |
674 | |
675 #ifdef NEW_STYLE_COMPLETION | |
676 names = g_list_append(names, ((GaimContact *)cnode)->alias); | |
677 names = g_list_append(names, | |
678 (gpointer)gaim_buddy_get_contact_alias(buddy)); | |
679 names = g_list_append(names, buddy->account); | |
680 #endif /* NEW_STYLE_COMPLETION */ | |
681 | |
682 names = g_list_append(names, g_strdup(buddy->name)); | |
683 } | |
684 } | |
685 } | |
686 | |
687 if (all) | |
688 { | |
689 log_sets = gaim_log_get_log_sets(); | |
690 for (log_set = log_sets ; log_set != NULL ; log_set = log_set->next) { | |
691 GaimLogSet *set = log_set->data; | |
692 | |
693 /* 1. Don't show buddies because we will have gotten them above. | |
694 * 2. Only show those with non-NULL accounts that are currently connected. | |
695 * 3. The boxes that use this autocomplete code handle only IMs. */ | |
696 if (!set->buddy && | |
697 (set->account != NULL && gaim_account_is_connected(set->account)) && | |
698 set->type != GAIM_LOG_CHAT) { | |
699 #ifdef NEW_STYLE_COMPLETION | |
700 names = g_list_append(names, NULL); | |
701 names = g_list_append(names, NULL); | |
702 names = g_list_append(names, set->account); | |
703 #endif /* NEW_STYLE_COMPLETION */ | |
704 | |
705 names = g_list_append(names, set->name); | |
706 } | |
707 | |
708 g_free(set); | |
709 } | |
710 } | |
711 | |
712 return names; | |
713 } | |
714 | |
715 #ifndef NEW_STYLE_COMPLETION | 650 #ifndef NEW_STYLE_COMPLETION |
716 static gboolean | 651 static gboolean |
717 completion_entry_event(GtkEditable *entry, GdkEventKey *event, | 652 completion_entry_event(GtkEditable *entry, GdkEventKey *event, |
718 GaimGtkCompletionData *data) | 653 GaimGtkCompletionData *data) |
719 { | 654 { |
795 | 730 |
796 #ifdef NEW_STYLE_COMPLETION | 731 #ifdef NEW_STYLE_COMPLETION |
797 static gboolean screenname_completion_match_func(GtkEntryCompletion *completion, | 732 static gboolean screenname_completion_match_func(GtkEntryCompletion *completion, |
798 const gchar *key, GtkTreeIter *iter, gpointer user_data) { | 733 const gchar *key, GtkTreeIter *iter, gpointer user_data) { |
799 | 734 |
800 GValue val = { 0, }; | 735 GValue val1 = { 0, }; |
736 GValue val2 = { 0, }; | |
801 GtkTreeModel *model; | 737 GtkTreeModel *model; |
802 char *screenname = NULL; | |
803 char *alias = NULL; | |
804 char *temp; | |
805 gboolean ret = FALSE; | |
806 | 738 |
807 model = gtk_entry_completion_get_model (completion); | 739 model = gtk_entry_completion_get_model (completion); |
808 | 740 |
809 gtk_tree_model_get_value(model, iter, 1, &val); | 741 gtk_tree_model_get_value(model, iter, 2, &val1); |
810 temp = (gchar *)g_value_get_string(&val); | 742 if (g_str_has_prefix(g_value_get_string(&val1), key)) |
811 if (temp) { | 743 { |
812 temp = g_utf8_normalize(temp, -1, G_NORMALIZE_DEFAULT); | 744 g_value_unset(&val1); |
813 screenname = g_utf8_casefold(temp, -1); | 745 return TRUE; |
814 g_free(temp); | 746 } |
815 } | 747 g_value_unset(&val1); |
816 g_value_unset(&val); | 748 |
817 | 749 gtk_tree_model_get_value(model, iter, 3, &val2); |
818 gtk_tree_model_get_value(model, iter, 2, &val); | 750 if (g_str_has_prefix(g_value_get_string(&val2), key)) |
819 temp = (gchar *)g_value_get_string(&val); | 751 { |
820 if (temp) { | 752 g_value_unset(&val2); |
821 temp = g_utf8_normalize(temp, -1, G_NORMALIZE_DEFAULT); | 753 return TRUE; |
822 alias = g_utf8_casefold(temp, -1); | 754 } |
823 g_free(temp); | 755 g_value_unset(&val2); |
824 } | 756 |
825 g_value_unset(&val); | 757 return FALSE; |
826 | |
827 if (g_str_has_prefix(screenname, key) || | |
828 g_str_has_prefix(alias, key)) | |
829 ret = TRUE; | |
830 | |
831 g_free(screenname); | |
832 g_free(alias); | |
833 | |
834 return ret; | |
835 } | 758 } |
836 | 759 |
837 static gboolean screenname_completion_match_selected_cb(GtkEntryCompletion *completion, | 760 static gboolean screenname_completion_match_selected_cb(GtkEntryCompletion *completion, |
838 GtkTreeModel *model, GtkTreeIter *iter, gpointer *user_data) { | 761 GtkTreeModel *model, GtkTreeIter *iter, gpointer *user_data) { |
839 | 762 |
854 if (type_hint != NULL && !strcmp(type_hint, "account")) { | 777 if (type_hint != NULL && !strcmp(type_hint, "account")) { |
855 /* We found the corresponding account field. */ | 778 /* We found the corresponding account field. */ |
856 GaimAccount *account; | 779 GaimAccount *account; |
857 GtkOptionMenu *optmenu = GTK_OPTION_MENU(field->ui_data); | 780 GtkOptionMenu *optmenu = GTK_OPTION_MENU(field->ui_data); |
858 | 781 |
859 gtk_tree_model_get_value(model, iter, 3, &val); | 782 gtk_tree_model_get_value(model, iter, 4, &val); |
860 account = g_value_get_pointer(&val); | 783 account = g_value_get_pointer(&val); |
861 g_value_unset(&val); | 784 g_value_unset(&val); |
862 | 785 |
863 /* Set the account in the request API. */ | 786 /* Set the account in the request API. */ |
864 gaim_request_field_account_set_value(field, account); | 787 gaim_request_field_account_set_value(field, account); |
884 | 807 |
885 } while ((fields = fields->next) != NULL); | 808 } while ((fields = fields->next) != NULL); |
886 | 809 |
887 return TRUE; | 810 return TRUE; |
888 } | 811 } |
889 #endif /* !NEW_STYLE_COMPLETION */ | 812 |
890 | 813 static void |
891 static void | 814 add_screenname_autocomplete_entry(GtkListStore *store, const char *buddy_alias, const char *contact_alias, |
892 setup_screenname_autocomplete(GtkWidget *entry, GaimRequestField *field, gboolean all) | 815 const GaimAccount *account, const char *screenname) |
893 { | 816 { |
894 #ifdef NEW_STYLE_COMPLETION | |
895 GtkListStore *store; | |
896 GtkTreeIter iter; | 817 GtkTreeIter iter; |
897 GtkEntryCompletion *completion; | 818 gboolean completion_added = FALSE; |
898 GList *screenname_completion_data; | 819 gchar *normalized_screenname; |
899 GList *l; | 820 gchar *tmp; |
900 gpointer *data; | 821 |
901 | 822 tmp = g_utf8_normalize(screenname, -1, G_NORMALIZE_DEFAULT); |
902 /* Store the displayed completion value, the screenname, the value for comparison, and the account. */ | 823 normalized_screenname = g_utf8_casefold(tmp, -1); |
903 store = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER); | 824 g_free(tmp); |
904 | 825 |
905 screenname_completion_data = get_screenname_completion_data(all); | 826 /* There's no sense listing things like: 'xxx "xxx"' |
906 | 827 when the screenname and buddy alias match. */ |
907 /* Loop through the list four elements at a time. */ | 828 if (buddy_alias && strcmp(buddy_alias, screenname)) { |
908 for (l = screenname_completion_data; l != NULL; l = l->next->next->next->next) | 829 char *completion_entry = g_strdup_printf("%s \"%s\"", screenname, buddy_alias); |
909 { | 830 char *tmp2 = g_utf8_normalize(buddy_alias, -1, G_NORMALIZE_DEFAULT); |
910 char *contact_alias = l->data; | 831 |
911 char *buddy_alias = l->next->data; | 832 tmp = g_utf8_casefold(tmp2, -1); |
912 GaimAccount *account = l->next->next->data; | 833 g_free(tmp2); |
913 char *screenname = l->next->next->next->data; | 834 |
914 gboolean completion_added = FALSE; | 835 gtk_list_store_append(store, &iter); |
915 | 836 gtk_list_store_set(store, &iter, |
916 /* There's no sense listing things like: 'xxx "xxx"' | 837 0, completion_entry, |
917 when the screenname and buddy alias match. */ | 838 1, screenname, |
918 if (buddy_alias && strcmp(buddy_alias, screenname)) { | 839 2, normalized_screenname, |
919 char *completion_entry = g_strdup_printf("%s \"%s\"", screenname, buddy_alias); | 840 3, tmp, |
841 4, account, | |
842 -1); | |
843 g_free(completion_entry); | |
844 g_free(tmp); | |
845 completion_added = TRUE; | |
846 } | |
847 | |
848 /* There's no sense listing things like: 'xxx "xxx"' | |
849 when the screenname and contact alias match. */ | |
850 if (contact_alias && strcmp(contact_alias, screenname)) { | |
851 /* We don't want duplicates when the contact and buddy alias match. */ | |
852 if (!buddy_alias || strcmp(contact_alias, buddy_alias)) { | |
853 char *completion_entry = g_strdup_printf("%s \"%s\"", | |
854 screenname, contact_alias); | |
855 char *tmp2 = g_utf8_normalize(contact_alias, -1, G_NORMALIZE_DEFAULT); | |
856 | |
857 tmp = g_utf8_casefold(tmp2, -1); | |
858 g_free(tmp2); | |
859 | |
920 gtk_list_store_append(store, &iter); | 860 gtk_list_store_append(store, &iter); |
921 gtk_list_store_set(store, &iter, | 861 gtk_list_store_set(store, &iter, |
922 0, completion_entry, | 862 0, completion_entry, |
923 1, screenname, | 863 1, screenname, |
924 2, buddy_alias, | 864 2, normalized_screenname, |
925 3, account, | 865 3, tmp, |
866 4, account, | |
926 -1); | 867 -1); |
927 g_free(completion_entry); | 868 g_free(completion_entry); |
869 g_free(tmp); | |
928 completion_added = TRUE; | 870 completion_added = TRUE; |
929 } | 871 } |
930 | 872 } |
931 /* There's no sense listing things like: 'xxx "xxx"' | 873 |
932 when the screenname and contact alias match. */ | 874 if (completion_added == FALSE) { |
933 if (contact_alias && strcmp(contact_alias, screenname)) { | 875 /* Add the buddy's screenname. */ |
934 /* We don't want duplicates when the contact and buddy alias match. */ | 876 gtk_list_store_append(store, &iter); |
935 if (strcmp(contact_alias, buddy_alias)) { | 877 gtk_list_store_set(store, &iter, |
936 char *completion_entry = g_strdup_printf("%s \"%s\"", | 878 0, screenname, |
937 screenname, contact_alias); | 879 1, screenname, |
938 gtk_list_store_append(store, &iter); | 880 2, normalized_screenname, |
939 gtk_list_store_set(store, &iter, | 881 3, NULL, |
940 0, completion_entry, | 882 4, account, |
941 1, screenname, | 883 -1); |
942 2, contact_alias, | 884 } |
943 3, account, | 885 |
944 -1); | 886 g_free(normalized_screenname); |
945 g_free(completion_entry); | 887 } |
946 completion_added = TRUE; | 888 #endif /* NEW_STYLE_COMPLETION */ |
889 | |
890 static void get_log_set_name(GaimLogSet *set, gpointer value, gpointer **set_hash_data) | |
891 { | |
892 /* 1. Don't show buddies because we will have gotten them already. | |
893 * 2. Only show those with non-NULL accounts that are currently connected. | |
894 * 3. The boxes that use this autocomplete code handle only IMs. */ | |
895 if (!set->buddy && | |
896 (GPOINTER_TO_INT(set_hash_data[1]) || | |
897 (set->account != NULL && gaim_account_is_connected(set->account))) && | |
898 set->type == GAIM_LOG_IM) { | |
899 #if NEW_STYLE_COMPLETION | |
900 add_screenname_autocomplete_entry((GtkListStore *)set_hash_data[0], | |
901 NULL, NULL, set->account, set->name); | |
902 #else | |
903 GList **items = ((GList **)set_hash_data[0]); | |
904 /* Steal the name for the GCompletion. */ | |
905 *items = g_list_append(*items, set->name); | |
906 set->name = set->normalized_name = NULL; | |
907 #endif /* NEW_STYLE_COMPLETION */ | |
908 } | |
909 } | |
910 | |
911 static void | |
912 setup_screenname_autocomplete(GtkWidget *entry, GaimRequestField *field, gboolean all) | |
913 { | |
914 #ifdef NEW_STYLE_COMPLETION | |
915 /* Store the displayed completion value, the screenname, the UTF-8 normalized & casefolded screenname, | |
916 * the UTF-8 normalized & casefolded value for comparison, and the account. */ | |
917 GtkListStore *store = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER); | |
918 | |
919 GaimBlistNode *gnode, *cnode, *bnode; | |
920 GHashTable *sets; | |
921 gpointer set_hash_data[] = {store, GINT_TO_POINTER(all)}; | |
922 GtkEntryCompletion *completion; | |
923 gpointer *data; | |
924 | |
925 for (gnode = gaim_get_blist()->root; gnode != NULL; gnode = gnode->next) | |
926 { | |
927 if (!GAIM_BLIST_NODE_IS_GROUP(gnode)) | |
928 continue; | |
929 | |
930 for (cnode = gnode->child; cnode != NULL; cnode = cnode->next) | |
931 { | |
932 if (!GAIM_BLIST_NODE_IS_CONTACT(cnode)) | |
933 continue; | |
934 | |
935 for (bnode = cnode->child; bnode != NULL; bnode = bnode->next) | |
936 { | |
937 GaimBuddy *buddy = (GaimBuddy *)bnode; | |
938 | |
939 if (!all && !gaim_account_is_connected(buddy->account)) | |
940 continue; | |
941 | |
942 add_screenname_autocomplete_entry(store, | |
943 ((GaimContact *)cnode)->alias, | |
944 gaim_buddy_get_contact_alias(buddy), | |
945 buddy->account, | |
946 buddy->name | |
947 ); | |
947 } | 948 } |
948 } | 949 } |
949 | 950 } |
950 if (completion_added == FALSE) { | 951 |
951 /* Add the buddy's screenname. */ | 952 sets = gaim_log_get_log_sets(); |
952 gtk_list_store_append(store, &iter); | 953 g_hash_table_foreach(sets, (GHFunc)get_log_set_name, &set_hash_data); |
953 gtk_list_store_set(store, &iter, | 954 g_hash_table_destroy(sets); |
954 0, screenname, | 955 |
955 1, screenname, | |
956 2, NULL, | |
957 3, account, | |
958 -1); | |
959 } | |
960 | |
961 g_free(screenname); | |
962 } | |
963 | |
964 g_list_free(screenname_completion_data); | |
965 | 956 |
966 /* Sort the completion list by screenname. */ | 957 /* Sort the completion list by screenname. */ |
967 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(store), | 958 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(store), |
968 1, GTK_SORT_ASCENDING); | 959 1, GTK_SORT_ASCENDING); |
969 | 960 |
984 | 975 |
985 gtk_entry_completion_set_text_column(completion, 0); | 976 gtk_entry_completion_set_text_column(completion, 0); |
986 | 977 |
987 #else /* !NEW_STYLE_COMPLETION */ | 978 #else /* !NEW_STYLE_COMPLETION */ |
988 GaimGtkCompletionData *data; | 979 GaimGtkCompletionData *data; |
989 GList *screennames; | 980 GaimBlistNode *gnode, *cnode, *bnode; |
981 GList *item = g_list_append(NULL, NULL); | |
982 GHashTable *sets; | |
983 gpointer set_hash_data[2]; | |
990 | 984 |
991 data = g_new0(GaimGtkCompletionData, 1); | 985 data = g_new0(GaimGtkCompletionData, 1); |
992 | 986 |
993 data->completion = g_completion_new(NULL); | 987 data->completion = g_completion_new(NULL); |
994 | 988 |
995 g_completion_set_compare(data->completion, g_ascii_strncasecmp); | 989 g_completion_set_compare(data->completion, g_ascii_strncasecmp); |
996 | 990 |
997 screennames = get_screenname_completion_data(all); | 991 for (gnode = gaim_get_blist()->root; gnode != NULL; gnode = gnode->next) |
998 | 992 { |
999 g_completion_add_items(data->completion, screennames); | 993 if (!GAIM_BLIST_NODE_IS_GROUP(gnode)) |
1000 | 994 continue; |
1001 g_list_free(screennames); | 995 |
996 for (cnode = gnode->child; cnode != NULL; cnode = cnode->next) | |
997 { | |
998 if (!GAIM_BLIST_NODE_IS_CONTACT(cnode)) | |
999 continue; | |
1000 | |
1001 for (bnode = cnode->child; bnode != NULL; bnode = bnode->next) | |
1002 { | |
1003 GaimBuddy *buddy = (GaimBuddy *)bnode; | |
1004 | |
1005 if (!all && !gaim_account_is_connected(buddy->account)) | |
1006 continue; | |
1007 | |
1008 item->data = g_strdup(buddy->name); | |
1009 g_completion_add_items(data->completion, item); | |
1010 } | |
1011 } | |
1012 } | |
1013 g_list_free(item); | |
1014 | |
1015 sets = gaim_log_get_log_sets(); | |
1016 item = NULL; | |
1017 set_hash_data[0] = &item; | |
1018 set_hash_data[1] = GINT_TO_POINTER(all); | |
1019 g_hash_table_foreach(sets, (GHFunc)get_log_set_name, &set_hash_data); | |
1020 g_hash_table_destroy(sets); | |
1021 g_completion_add_items(data->completion, item); | |
1022 g_list_free(item); | |
1002 | 1023 |
1003 g_signal_connect(G_OBJECT(entry), "event", | 1024 g_signal_connect(G_OBJECT(entry), "event", |
1004 G_CALLBACK(completion_entry_event), data); | 1025 G_CALLBACK(completion_entry_event), data); |
1005 g_signal_connect(G_OBJECT(entry), "destroy", | 1026 g_signal_connect(G_OBJECT(entry), "destroy", |
1006 G_CALLBACK(destroy_completion_data), data); | 1027 G_CALLBACK(destroy_completion_data), data); |