From: <mar...@us...> - 2006-07-24 13:39:39
|
Revision: 16562 Author: markhuetsch Date: 2006-07-24 06:39:12 -0700 (Mon, 24 Jul 2006) ViewCVS: http://svn.sourceforge.net/gaim/?rev=16562&view=rev Log Message: ----------- *Eliminated all Gtk-related code from the prpl. Notably, this included the group ("Qun") administrative dialog and a dialog for setting and viewing personal information. Code for the latter now uses the gaim UI, while the former is currently disabled. *Disabled a few non-functional/non-essential menu actions. These included: IP lookup, system logging, about dialog, and qq_buddy_menu. Modified Paths: -------------- trunk/src/protocols/qq/Makefile.am trunk/src/protocols/qq/Makefile.mingw trunk/src/protocols/qq/buddy_info.c trunk/src/protocols/qq/buddy_info.h trunk/src/protocols/qq/buddy_list.c trunk/src/protocols/qq/buddy_status.c trunk/src/protocols/qq/buddy_status.h trunk/src/protocols/qq/group_opt.c trunk/src/protocols/qq/qq.c trunk/src/protocols/qq/qq_proxy.c trunk/src/protocols/qq/qq_proxy.h trunk/src/protocols/qq/utils.c trunk/src/protocols/qq/utils.h Removed Paths: ------------- trunk/src/protocols/qq/TODO trunk/src/protocols/qq/group_admindlg.c trunk/src/protocols/qq/group_admindlg.h trunk/src/protocols/qq/infodlg.c trunk/src/protocols/qq/infodlg.h trunk/src/protocols/qq/show.c trunk/src/protocols/qq/show.h Modified: trunk/src/protocols/qq/Makefile.am =================================================================== --- trunk/src/protocols/qq/Makefile.am 2006-07-24 09:25:48 UTC (rev 16561) +++ trunk/src/protocols/qq/Makefile.am 2006-07-24 13:39:12 UTC (rev 16562) @@ -21,8 +21,6 @@ char_conv.h \ crypt.c \ crypt.h \ - group_admindlg.c \ - group_admindlg.h \ group.c \ group_conv.c \ group_conv.h \ @@ -52,8 +50,6 @@ header_info.h \ im.c \ im.h \ - infodlg.c \ - infodlg.h \ ip_location.c \ ip_location.h \ keep_alive.c \ @@ -68,8 +64,6 @@ send_core.h \ sendqueue.c \ sendqueue.h \ - show.c \ - show.h \ sys_msg.c \ sys_msg.h \ udp_proxy_s5.c \ @@ -104,6 +98,5 @@ -DLOCALEDIR=\"${datadir}/locale\" \ -DVERSION=\"$(VERSION)\" \ $(DEBUG_CFLAGS) \ - $(GTK_CFLAGS) \ $(GLIB_CFLAGS) \ $(GAIM_CFLAGS) Modified: trunk/src/protocols/qq/Makefile.mingw =================================================================== --- trunk/src/protocols/qq/Makefile.mingw 2006-07-24 09:25:48 UTC (rev 16561) +++ trunk/src/protocols/qq/Makefile.mingw 2006-07-24 13:39:12 UTC (rev 16562) @@ -82,7 +82,6 @@ crypt.c \ file_trans.c \ group.c \ - group_admindlg.c \ group_conv.c \ group_find.c \ group_free.c \ @@ -96,7 +95,6 @@ group_search.c \ header_info.c \ im.c \ - infodlg.c \ ip_location.c \ keep_alive.c \ login_logout.c \ Deleted: trunk/src/protocols/qq/TODO =================================================================== --- trunk/src/protocols/qq/TODO 2006-07-24 09:25:48 UTC (rev 16561) +++ trunk/src/protocols/qq/TODO 2006-07-24 13:39:12 UTC (rev 16562) @@ -1,18 +0,0 @@ -* Decide how to handle QQ status icons, which are customizable and legion. -* Give "Online Offline" and "My Offline" statuses appropriate titles. -* Handle emoticon at beginning of message via passthrough_unknown_commands. -* Fix file transfer. -* QQ protocol currently breaks Chinese localization of Gaim. Fix this. -* Fix ability to insert images into a conversation via the menubar. -* Fix _qq_menu_block_buddy in qq.c -* Eliminate all references to QQWry.dat and the geolocation lookup feature previously present OpenQ. -* Give smileys verbal instead of numerical titles. -* Clean up signedness warnings. -* Clean up mixed declaration and code warnings. -* Better decomposition: -** some of these functions are _really_ long -** buddy_status.c has a helper function which seems to belong in buddy_status.c -** consider cleaning up qq_encrypt (nested function declarations) -** eliminate group_misc.c -** investigate whether all of these externs are appropriate -* Check for memory leaks. Modified: trunk/src/protocols/qq/buddy_info.c =================================================================== --- trunk/src/protocols/qq/buddy_info.c 2006-07-24 09:25:48 UTC (rev 16561) +++ trunk/src/protocols/qq/buddy_info.c 2006-07-24 13:39:12 UTC (rev 16562) @@ -20,11 +20,10 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -// START OF FILE -/*****************************************************************************/ #include "internal.h" // strlen, _("get_text) #include "debug.h" // gaim_debug #include "notify.h" // gaim_notify +#include "request.h" // gaim_request_fields_new #include "utils.h" // uid_to_gaim_name #include "packet_parse.h" // MAX_PACKET_SIZE @@ -32,68 +31,295 @@ #include "char_conv.h" // qq_to_utf8 #include "crypt.h" // qq_crypt #include "header_info.h" // cmd alias -#include "infodlg.h" // info_window #include "keep_alive.h" // qq_update_buddy_contact #include "send_core.h" // qq_send_cmd -// amount of fiedls in user info -#define QQ_CONTACT_FIELDS 37 +// Below is all of the information necessary to reconstruct the various +// information fields that one can set in the official client. When we need +// to know about a specific field (e.g., should "city" be a choice +// or text field?), we can simply look it up from the template. Note that +// there are a number of unidentified fields. -// There is no user id stored in the reply packet for information query -// we have to manually store the query, so that we know the query source -typedef struct _qq_info_query { - guint32 uid; - gboolean show_window; - contact_info *ret_info; -} qq_info_query; +typedef struct _info_field { + gchar *title; + gchar *id; // used by gaim_request fields + gint pos; + gchar *group; + gint group_pos; // for display order in the UI + gint choice; // indicates which character array contains the choices + gboolean customizable; // whether a user can enter any text as a value, regardless of choice arrays + gchar *value; +} info_field; -/*****************************************************************************/ -// send a packet to get detailed information of uid, -// if show_window, a info window will be display upon receiving a reply +static const info_field info_template_data[] = { + { N_("User ID"), "uid", 0, QQ_MAIN_INFO, 0, QQ_NO_CHOICE, TRUE, NULL }, + { N_("Nickname"), "nick", 1, QQ_MAIN_INFO, 1, QQ_NO_CHOICE, TRUE, NULL }, + { N_("Country/Region"), "country", 2, QQ_MAIN_INFO, 5, QQ_COUNTRY, TRUE, NULL }, + { N_("Province/State"), "province", 3, QQ_MAIN_INFO, 6, QQ_PROVINCE, TRUE, NULL }, + { N_("Zipcode"), "zipcode", 4, QQ_EXTRA_INFO, 7, QQ_NO_CHOICE, TRUE, NULL }, + { N_("Address"), "address", 5, QQ_EXTRA_INFO, 6, QQ_NO_CHOICE, TRUE, NULL }, + { N_("Phone Number"), "tel", 6, QQ_EXTRA_INFO, 9, QQ_NO_CHOICE, TRUE, NULL }, + { N_("Age"), "age", 7, QQ_MAIN_INFO, 3, QQ_NO_CHOICE, TRUE, NULL }, + { N_("Gender"), "gender", 8, QQ_MAIN_INFO, 4, QQ_GENDER, FALSE, NULL }, + { N_("Name"), "name", 9, QQ_MAIN_INFO, 2, QQ_NO_CHOICE, TRUE, NULL }, + { N_("Email"), "email", 10, QQ_EXTRA_INFO, 5, QQ_NO_CHOICE, TRUE, NULL }, + { "pager_sn", "pager_sn", 11, QQ_MISC, 0, QQ_NO_CHOICE, TRUE, NULL }, + { "pager_num", "pager_num", 12, QQ_MISC, 1, QQ_NO_CHOICE, TRUE, NULL }, + { "pager_sp", "pager_sp", 13, QQ_MISC, 2, QQ_NO_CHOICE, TRUE, NULL }, + { "pager_base_num", "pager_base_num", 14, QQ_MISC, 3, QQ_NO_CHOICE, TRUE, NULL }, + { "pager_type", "pager_type", 15, QQ_MISC, 4, QQ_NO_CHOICE, TRUE, NULL }, + { N_("Occupation"), "occupation", 16, QQ_EXTRA_INFO, 1, QQ_OCCUPATION, TRUE, NULL }, + { N_("Homepage"), "homepage", 17, QQ_EXTRA_INFO, 10, QQ_NO_CHOICE, TRUE, NULL }, + { "auth_type", "auth_type", 18, QQ_MISC, 5, QQ_NO_CHOICE, TRUE, NULL }, + { "unknown1", "unknown1", 19, QQ_MISC, 6, QQ_NO_CHOICE, TRUE, NULL }, + { "unknown2", "unknown2", 20, QQ_MISC, 7, QQ_NO_CHOICE, TRUE, NULL }, + { "face", "face", 21, QQ_MISC, 8, QQ_NO_CHOICE, TRUE, NULL }, + { N_("Cellphone Number"), "hp_num", 22, QQ_EXTRA_INFO, 8, QQ_NO_CHOICE, TRUE, NULL }, + { "hp_type", "hp_type", 23, QQ_MISC, 9, QQ_NO_CHOICE, TRUE, NULL }, + { N_("Personal Introduction"), "intro", 24, QQ_PERSONAL_INTRO, 0, QQ_NO_CHOICE, TRUE, NULL }, + { N_("City"), "city", 25, QQ_MAIN_INFO, 7, QQ_NO_CHOICE, TRUE, NULL }, + { "unknown3", "unknown3", 26, QQ_MISC, 10, QQ_NO_CHOICE, TRUE, NULL }, + { "unknown4", "unknown4", 27, QQ_MISC, 11, QQ_NO_CHOICE, TRUE, NULL }, + { "unknown5", "unknown5", 28, QQ_MISC, 12, QQ_NO_CHOICE, TRUE, NULL }, + { "is_open_hp", "is_open_hp", 29, QQ_MISC, 13, QQ_NO_CHOICE, TRUE, NULL }, + { "is_open_contact", "is_open_contact", 30, QQ_MISC, 14, QQ_NO_CHOICE, TRUE, NULL }, + { N_("College"), "college", 31, QQ_EXTRA_INFO, 4, QQ_NO_CHOICE, TRUE, NULL }, + { N_("Horoscope Symbol"), "horoscope", 32, QQ_EXTRA_INFO, 0, QQ_HOROSCOPE, FALSE, NULL }, + { N_("Zodiac Symbol"), "zodiac", 33, QQ_EXTRA_INFO, 2, QQ_ZODIAC, FALSE, NULL }, + { N_("Blood Type"), "blood", 34, QQ_EXTRA_INFO, 3, QQ_BLOOD, FALSE, NULL }, + { "qq_show", "qq_show", 35, QQ_MISC, 15, QQ_NO_CHOICE, TRUE, NULL }, + { "unknown6", "unknown6", 36, QQ_MISC, 16, QQ_NO_CHOICE, TRUE, NULL }, + { NULL, NULL, 0, NULL, 0, 0, 0, NULL } //NULL termination +}; + +//TODO: translate these arrays to their English equivalents +// and move these characters to the zh_CN po file +static const gchar *horoscope_names[] = { + "-", "水瓶座", "双鱼座", "牡羊座", "金牛座", + "双子座", "巨蟹座", "狮子座", "处女座", "天秤座", + "天蝎座", "射手座", "魔羯座", NULL +}; + +static const gchar *zodiac_names[] = { + "-", "鼠", "牛", "虎", "兔", + "龙", "蛇", "马", "羊", "猴", + "鸡", "狗", "猪", NULL +}; + +static const gchar *blood_types[] = { + "其它", "A型", "B型", "O型", "AB型", NULL +}; + +static const gchar *genders[] = { + N_("Male"), + N_("Female"), + NULL +}; + +static const gchar *country_names[] = { + "中国", "中国香港", "中国澳门", "中国台湾", + "新加坡", "马来西亚", "美国", NULL +}; + +static const gchar *province_names[] = { + "北京", "天津", "上海", "重庆", "香港", + "河北", "山西", "内蒙古", "辽宁", "吉林", + "黑龙江", "江西", "浙江", "江苏", "安徽", + "福建", "山东", "河南", "湖北", "湖南", + "广东", "广西", "海南", "四川", "贵州", + "云南", "西藏", "陕西", "甘肃", "宁夏", + "青海", "新疆", "台湾", "澳门", NULL +}; + +static const gchar *occupation_names[] = { + "全职", "兼职", "制造业", "商业", "失业中", + "学生", "工程师", "政府部门", "教育业", "服务行业", + "老板", "计算机业", "退休", "金融业", + "销售/广告/市场", NULL +}; + +static const gint choice_sizes[] = { 0, 13, 13, 5, 2, 7, 34, 15 }; + + +static const gchar *info_group_headers[] = { + QQ_MAIN_INFO, + QQ_EXTRA_INFO, + QQ_PERSONAL_INTRO, + QQ_MISC +}; + +static const gchar **choices[] = { + NULL, + horoscope_names, + zodiac_names, + blood_types, + genders, + country_names, + province_names, + occupation_names +}; + +/*************** info and info_field methods *****************/ + +// Given an id, return the template for that field. +// Returns NULL if the id is not found. +static const info_field *info_field_get_template(const gchar *id) +{ + const info_field *cur_field; + const gchar *cur_id; + + cur_field = info_template_data; + cur_id = cur_field->id; + while(cur_id != NULL) { + if (g_ascii_strcasecmp(cur_id, id) == 0) return cur_field; + cur_field++; + cur_id = cur_field->id; + } + gaim_debug(GAIM_DEBUG_WARNING, "QQ", "Info field with id %s not found!", id); + return NULL; +} + +// info_fields are compared by their group positions +static gint info_field_compare(gconstpointer a, gconstpointer b, gpointer unused) +{ + return ((info_field *) a)->group_pos - ((info_field *) b)->group_pos; +} + +static void info_field_free(info_field *i) +{ + g_free(i->value); + g_free(i); +} + +// Parses the info_template_data above and returns a newly-allocated list +// containing the desired fields from segments. This list is ordered by +// group_pos. +static GList *info_get_group(const gchar **info, const gchar *group_name) +{ + const info_field *cur; + info_field *entry; + GList *group = NULL; + + cur = info_template_data; + while (cur->id != NULL) { + if (g_ascii_strcasecmp(group_name, cur->group) == 0) { + entry = g_memdup(cur, sizeof(info_field)); + entry->value = g_strdup(info[entry->pos]); + group = g_list_insert_sorted_with_data(group, entry, info_field_compare, NULL); + } + cur++; + } + + return group; +} + +// determines if the given text value and choice group require +// a lookup from the choice arrays +static gboolean is_valid_index(gchar *value, gint choice) +{ + gint len, i; + + if (choice == 0) return FALSE; + len = strlen(value); + // the server sends us an ascii index and none of arrays has more than 99 + // elements + if (len > 3 || len == 0) return FALSE; + for (i = 0; i < len; i++) + if (!g_ascii_isdigit(value[i])) return FALSE; + i = atoi(value); + if (i < 0 || i >= choice_sizes[choice]) return FALSE; + return TRUE; +} + +// formats a field for printing +static void append_field_to_str(gpointer field, gpointer str) +{ + info_field *f; + gint choice; + gboolean valid_index; + gchar *value; + + f = (info_field *) field; + choice = f->choice; + valid_index = is_valid_index(f->value, choice); + if (choice && valid_index) value = g_strdup(choices[choice][atoi(f->value)]); + else value = qq_to_utf8(f->value, QQ_CHARSET_DEFAULT); + g_string_append_printf((GString *) str, "<b>%s:</b> %s<br />", + f->title, value); + g_free(value); + info_field_free(f); +} + +// formats a group of information for printing +static void append_group_to_str(GString *str, const gchar *group_name, const gchar **info) +{ + GList *group; + + group = info_get_group(info, group_name); + g_string_append_printf(str, "<b>%s</b><br /><br />", (*(info_field *) group->data).group); + g_list_foreach(group, append_field_to_str, str); + g_list_free(group); + g_string_append_printf(str, "<br />"); +} + +// takes a contact_info struct and outputs the appropriate fields in +// a printable format for our upcoming call to gaim_notify_userinfo +static GString *info_to_str(const gchar **info) +{ + GString *info_text; + + info_text = g_string_new(""); + append_group_to_str(info_text, QQ_MAIN_INFO, info); + append_group_to_str(info_text, QQ_EXTRA_INFO, info); + append_group_to_str(info_text, QQ_PERSONAL_INTRO, info); + //if (QQ_DEBUG) append_group_to_str(info_text, QQ_MISC, info); + + return info_text; +} + +/*************** packets and UI management *****************/ + +// send a packet to get detailed information of uid void qq_send_packet_get_info(GaimConnection * gc, guint32 uid, gboolean show_window) { qq_data *qd; gchar *uid_str; - GList *list; qq_info_query *query; - gboolean is_exist; - contact_info_window *info_window; g_return_if_fail(gc != NULL && gc->proto_data != NULL && uid != 0); qd = (qq_data *) gc->proto_data; uid_str = g_strdup_printf("%d", uid); - qq_send_cmd(gc, QQ_CMD_GET_USER_INFO, TRUE, 0, TRUE, uid_str, strlen(uid_str)); + qq_send_cmd(gc, QQ_CMD_GET_USER_INFO, TRUE, 0, TRUE, (guint8 *) uid_str, strlen(uid_str)); - if (show_window) { // prepare the window - is_exist = FALSE; // see if there is already a window for this uid - list = qd->contact_info_window; - while (list != NULL) { - info_window = (contact_info_window *) list->data; - if (uid == info_window->uid) { - is_exist = TRUE; - break; - } else - list = list->next; - } // while list - if (!is_exist) { // create a new one - info_window = g_new0(contact_info_window, 1); - info_window->uid = uid; - qd->contact_info_window = g_list_append(qd->contact_info_window, info_window); - } // if !is_exist - } // if show_window - query = g_new0(qq_info_query, 1); query->uid = uid; query->show_window = show_window; + query->modify_info = FALSE; qd->info_query = g_list_append(qd->info_query, query); g_free(uid_str); -} // qq_send_packet_get_info +} -/*****************************************************************************/ +// set up the fields requesting personal information and send a get_info packet +// for myself +void qq_prepare_modify_info(GaimConnection *gc) +{ + qq_data *qd; + GList *ql; + qq_info_query *query; + + qd = (qq_data *) gc->proto_data; + qq_send_packet_get_info(gc, qd->uid, FALSE); + // traverse backwards so we get the most recent info_query + for (ql = g_list_last(qd->info_query); ql != NULL; ql = g_list_previous(ql)) { + query = ql->data; + if (query->uid == qd->uid) query->modify_info = TRUE; + } +} + // send packet to modify personal information, and/or change password -void qq_send_packet_modify_info(GaimConnection * gc, contact_info * info, gchar * new_passwd) +void qq_send_packet_modify_info(GaimConnection *gc, contact_info *info, gchar *new_passwd) { GaimAccount *a; gchar *old_passwd, *info_field[QQ_CONTACT_FIELDS]; @@ -112,27 +338,175 @@ if (new_passwd == NULL || strlen(new_passwd) == 0) create_packet_b(raw_data, &cursor, bar); - else { // we gonna change passwd - create_packet_data(raw_data, &cursor, old_passwd, strlen(old_passwd)); + else { // we're gonna change passwd + create_packet_data(raw_data, &cursor, (guint8 *) old_passwd, strlen(old_passwd)); create_packet_b(raw_data, &cursor, bar); - create_packet_data(raw_data, &cursor, new_passwd, strlen(new_passwd)); + create_packet_data(raw_data, &cursor, (guint8 *) new_passwd, strlen(new_passwd)); } // important!, skip the first uid entry for (i = 1; i < QQ_CONTACT_FIELDS; i++) { create_packet_b(raw_data, &cursor, bar); - create_packet_data(raw_data, &cursor, info_field[i], strlen(info_field[i])); + create_packet_data(raw_data, &cursor, (guint8 *) info_field[i], strlen(info_field[i])); } create_packet_b(raw_data, &cursor, bar); qq_send_cmd(gc, QQ_CMD_UPDATE_INFO, TRUE, 0, TRUE, raw_data, cursor - raw_data); -} // qq_send_packet_modify_info +} -/*****************************************************************************/ -// process the reply of modidy_info packet -void qq_process_modify_info_reply(guint8 * buf, gint buf_len, GaimConnection * gc) +static void modify_info_cancel_cb(modify_info_data *mid) { + g_list_free(mid->misc); + g_free(mid); +} + +// runs through all of the fields in the modify info UI and put +// their values into the outgoing packet +static void parse_field(gpointer field, gpointer outgoing_info) +{ + GaimRequestField *f; + gchar **segments, *value; + const info_field *ft; + const gchar *id; + + f = (GaimRequestField *) field; + segments = (gchar **) outgoing_info; + id = gaim_request_field_get_id(f); + ft = info_field_get_template(id); + if (ft->choice && !ft->customizable) + value = g_strdup_printf("%d", gaim_request_field_choice_get_value(f)); + else { + value = (gchar *) gaim_request_field_string_get_value(f); + if (value == NULL) value = g_strdup(""); + else value = utf8_to_qq(value, QQ_CHARSET_DEFAULT); + } + segments[ft->pos] = value; +} + +// dumps the uneditable information straight into the outgoing packet +static void parse_misc_field(gpointer field, gpointer outgoing_info) +{ + info_field *f; + gchar **segments; + + f = (info_field *) field; + segments = (gchar **) outgoing_info; + segments[f->pos] = g_strdup(f->value); + info_field_free(f); +} + +// runs through all of the information fields and copies them into an +// outgoing packet, then sends that packet +static void modify_info_ok_cb(modify_info_data *mid, GaimRequestFields *fields) +{ + GaimConnection *gc; + GList *list, *groups, *group_node; + gchar *info_field[QQ_CONTACT_FIELDS]; + contact_info *info; + gint i; + + gc = mid->gc; + list = mid->misc; + g_list_foreach(list, parse_misc_field, info_field); + g_list_free(list); + groups = gaim_request_fields_get_groups(fields); + while(groups) { + group_node = groups; + list = gaim_request_field_group_get_fields(group_node->data); + g_list_foreach(list, parse_field, info_field); + groups = g_list_remove_link(groups, group_node); + } + info = (contact_info *) info_field; + + qq_send_packet_modify_info(gc, info, NULL); + g_free(mid); + for (i = 0; i < QQ_CONTACT_FIELDS; i++) + g_free(info_field[i]); +} + +// Sets up the display for one group of information. This includes +// managing which fields in the UI should be textfields and +// which choices, and also mapping ints to choice values when appropriate. +static void setup_group(gpointer field, gpointer group) +{ + info_field *f; + GaimRequestFieldGroup *g; + GaimRequestField *rf; + gint choice, index, j; + gboolean customizable, valid_index, multiline; + gchar *id, *value; + + f = (info_field *) field; + g = (GaimRequestFieldGroup *) group; + choice = f->choice; + customizable = f->customizable; + id = f->id; + valid_index = TRUE; + + if (!choice || customizable) { + valid_index = is_valid_index(f->value, choice); + multiline = id == "intro"; + if (valid_index) { + index = atoi(f->value); + value = (gchar *) choices[choice][index]; + } else value = qq_to_utf8(f->value, QQ_CHARSET_DEFAULT); + rf = gaim_request_field_string_new(id, f->title, value, multiline); + } else { + index = atoi(f->value); + value = (gchar *) choices[choice][index]; + rf = gaim_request_field_choice_new(id, f->title, index); + j = 0; + while(choices[choice][j] != NULL) + gaim_request_field_choice_add(rf, choices[choice][j++]); + } + gaim_request_field_group_add_field(g, rf); + if (!valid_index) g_free(value); + info_field_free(f); +} + +// Takes the info returned by a get_info packet for the user and sets up +// a form using those values and the info_template. +static void create_modify_info_dialogue(GaimConnection *gc, const gchar **info) +{ + GaimRequestFields *fields; + GaimRequestFieldGroup *group; + GaimRequestField *field; + GList *group_list; + modify_info_data *mid; + gint i; + + fields = gaim_request_fields_new(); + + // we only care about the first 3 groups, not the miscellaneous stuff + for (i = 0; i < 3; i++) { + group = gaim_request_field_group_new(info_group_headers[i]); + gaim_request_fields_add_group(fields, group); + group_list = info_get_group(info, info_group_headers[i]); + g_list_foreach(group_list, setup_group, group); + g_list_free(group_list); + } + + //set this manually here instead of generating a new template column + field = gaim_request_fields_get_field(fields, "uid"); + gaim_request_field_string_set_editable(field, FALSE); + + //we need to pass the info that doesn't get modified as aux data + //because we'll still need it when we send the modify_info packet + mid = g_new0(modify_info_data, 1); + mid->gc = gc; + mid->misc = info_get_group(info, info_group_headers[3]); + + gaim_request_fields(gc, _("Modify my information"), + _("Modify my information"), NULL, fields, + _("Update my information"), G_CALLBACK(modify_info_ok_cb), + _("Cancel"), G_CALLBACK(modify_info_cancel_cb), + mid); +} + +// process the reply of modify_info packet +void qq_process_modify_info_reply(guint8 *buf, gint buf_len, GaimConnection *gc) +{ qq_data *qd; gint len; guint8 *data; @@ -145,18 +519,17 @@ data = g_newa(guint8, len); if (qq_crypt(DECRYPT, buf, buf_len, qd->session_key, data, &len)) { - if (qd->uid == atoi(data)) { // return should be my uid + if (qd->uid == atoi((gchar *) data)) { // return should be my uid gaim_debug(GAIM_DEBUG_INFO, "QQ", "Update info ACK OK\n"); - gaim_notify_info(gc, NULL, _("You information have been updated"), NULL); + gaim_notify_info(gc, NULL, _("Your information has been updated"), NULL); } } else gaim_debug(GAIM_DEBUG_ERROR, "QQ", "Error decrypt modify info reply\n"); -} // qq_process_modify_info_reply +} -/*****************************************************************************/ // after getting info or modify myself, refresh the buddy list accordingly -void qq_refresh_buddy_and_myself(contact_info * info, GaimConnection * gc) +void qq_refresh_buddy_and_myself(contact_info *info, GaimConnection *gc) { GaimBuddy *b; qq_data *qd; @@ -182,13 +555,16 @@ if (alias_utf8 != NULL) q_bud->nickname = g_strdup(alias_utf8); qq_update_buddy_contact(gc, q_bud); - } // if q_bud + } g_free(alias_utf8); -} // qq_refresh_buddy_and_myself +} -/*****************************************************************************/ +// XXX When we don't have any immediate response, we send duplicate get info packets +// to the server. If the server ends up responding to multiple packets, we get multiple +// modify info dialogues, which is annoying. Fix this. + // process reply to get_info packet -void qq_process_get_info_reply(guint8 * buf, gint buf_len, GaimConnection * gc) +void qq_process_get_info_reply(guint8 *buf, gint buf_len, GaimConnection *gc) { gint len; guint8 *data; @@ -196,9 +572,8 @@ qq_info_query *query; qq_data *qd; contact_info *info; - contact_info_window *info_window; - gboolean show_window; GList *list, *query_list; + GString *info_text; g_return_if_fail(gc != NULL && gc->proto_data != NULL); g_return_if_fail(buf != NULL && buf_len != 0); @@ -217,42 +592,30 @@ qq_refresh_buddy_and_myself(info, gc); query_list = qd->info_query; - show_window = FALSE; - while (query_list != NULL) { + // ensure we're processing the right query + while (query_list) { query = (qq_info_query *) query_list->data; if (query->uid == atoi(info->uid)) { - show_window = query->show_window; + if (query->show_window) { + info_text = info_to_str((const gchar **) segments); + gaim_notify_userinfo(gc, info->uid, info_text->str, NULL, NULL); + g_string_free(info_text, TRUE); + } else if (query->modify_info) { + create_modify_info_dialogue(gc, (const gchar **) segments); + } qd->info_query = g_list_remove(qd->info_query, qd->info_query->data); g_free(query); break; } query_list = query_list->next; - } // while query_list + } - if (!show_window) { - g_strfreev(segments); - return; - } - // if not show_window, we can not find the window here either - list = qd->contact_info_window; - while (list != NULL) { - info_window = (contact_info_window *) (list->data); - if (info_window->uid == atoi(info->uid)) { - if (info_window->window) - qq_refresh_contact_info_dialog(info, gc, info_window); - else - qq_show_contact_info_dialog(info, gc, info_window); - break; - } else - list = list->next; - } // while list g_strfreev(segments); } else gaim_debug(GAIM_DEBUG_ERROR, "QQ", "Error decrypt get info reply\n"); -} // qq_process_get_info_reply +} -/*****************************************************************************/ void qq_info_query_free(qq_data * qd) { gint i; @@ -268,7 +631,4 @@ i++; } gaim_debug(GAIM_DEBUG_INFO, "QQ", "%d info queries are freed!\n", i); -} // qq_add_buddy_request_free - -/*****************************************************************************/ -// END OF FILE +} Modified: trunk/src/protocols/qq/buddy_info.h =================================================================== --- trunk/src/protocols/qq/buddy_info.h 2006-07-24 09:25:48 UTC (rev 16561) +++ trunk/src/protocols/qq/buddy_info.h 2006-07-24 13:39:12 UTC (rev 16562) @@ -40,9 +40,7 @@ #define QQ_BUDDY_GENDER_MM 0x01 #define QQ_BUDDY_GENDER_UNKNOWN 0xff -typedef struct _contact_info contact_info; - -struct _contact_info { +typedef struct _contact_info { gchar *uid; //0 gchar *nick; //1 gchar *country; //2 @@ -80,15 +78,46 @@ gchar *blood; //34 gchar *qq_show; //35 gchar *unknown6; //36, always 0x2D -}; +} contact_info; -void qq_refresh_buddy_and_myself(contact_info * info, GaimConnection * gc); -void qq_send_packet_get_info(GaimConnection * gc, guint32 uid, gboolean show_window); -void qq_send_packet_modify_info(GaimConnection * gc, contact_info * info, gchar * new_passwd); -void qq_process_modify_info_reply(guint8 * buf, gint buf_len, GaimConnection * gc); -void qq_process_get_info_reply(guint8 * buf, gint buf_len, GaimConnection * gc); -void qq_info_query_free(qq_data * qd); +// There is no user id stored in the reply packet for information query +// we have to manually store the query, so that we know the query source +typedef struct _qq_info_query { + guint32 uid; + gboolean show_window; + gboolean modify_info; +} qq_info_query; +// We get an info packet on ourselves before we modify our information. +// Even though not all of the information is currently modifiable, it still +// all needs to be there when we send out the modify info packet +typedef struct _modify_info_data { + GaimConnection *gc; + GList *misc, *node; +} modify_info_data; + +#define QQ_CONTACT_FIELDS 37 + +#define QQ_MAIN_INFO "Primary Information" +#define QQ_EXTRA_INFO "Detailed Information" +#define QQ_PERSONAL_INTRO "Personal Introduction" +#define QQ_MISC "Miscellaneous" + +#define QQ_NO_CHOICE 0 +#define QQ_HOROSCOPE 1 +#define QQ_ZODIAC 2 +#define QQ_BLOOD 3 +#define QQ_GENDER 4 +#define QQ_COUNTRY 5 +#define QQ_PROVINCE 6 +#define QQ_OCCUPATION 7 + +void qq_refresh_buddy_and_myself(contact_info *info, GaimConnection *gc); +void qq_send_packet_get_info(GaimConnection *gc, guint32 uid, gboolean show_window); +void qq_send_packet_modify_info(GaimConnection *gc, contact_info *info, gchar *new_passwd); +void qq_prepare_modify_info(GaimConnection *gc); +void qq_process_modify_info_reply(guint8 *buf, gint buf_len, GaimConnection *gc); +void qq_process_get_info_reply(guint8 *buf, gint buf_len, GaimConnection *gc); +void qq_info_query_free(qq_data *qd); + #endif -/*****************************************************************************/ -// END OF FILE Modified: trunk/src/protocols/qq/buddy_list.c =================================================================== --- trunk/src/protocols/qq/buddy_list.c 2006-07-24 09:25:48 UTC (rev 16561) +++ trunk/src/protocols/qq/buddy_list.c 2006-07-24 13:39:12 UTC (rev 16562) @@ -42,6 +42,8 @@ #include "group_hash.h" //qq_group_create_by_id #include "group_info.h" //qq_send_cmd_group_get_group_info +#include "qq_proxy.h" + #define QQ_GET_ONLINE_BUDDY_02 0x02 #define QQ_GET_ONLINE_BUDDY_03 0x03 // unknown function @@ -56,13 +58,6 @@ guint8 ending; //0x00 } qq_friends_online_entry; -//TODO: defined in qq_buddy_status.c, but only used here. Check decomposition. -extern void // defined in qq_buddy_status.c - _qq_buddy_status_dump_unclear(qq_buddy_status * s); - -extern gint // defined in qq_buddy_status.c - _qq_buddy_status_read(guint8 * data, guint8 ** cursor, gint len, qq_buddy_status * s); - /*****************************************************************************/ // get a list of online_buddies void qq_send_packet_get_buddies_online(GaimConnection * gc, guint8 position) @@ -148,7 +143,7 @@ g_return_if_fail(fe != NULL); - _qq_buddy_status_dump_unclear(fe->s); + qq_buddy_status_dump_unclear(fe->s); dump = g_string_new(""); g_string_append_printf(dump, "unclear fields for [%d]:\n", fe->s->uid); @@ -180,9 +175,14 @@ data = g_newa(guint8, len); cursor = data; + gaim_debug(GAIM_DEBUG_INFO, "QQ", "processing get_buddies_online_reply\n"); + if (qq_crypt(DECRYPT, buf, buf_len, qd->session_key, data, &len)) { + _qq_show_packet("Get buddies online reply packet", data, len); + read_packet_b(data, &cursor, len, &position); + fe = g_newa(qq_friends_online_entry, 1); fe->s = g_newa(qq_buddy_status, 1); @@ -190,7 +190,7 @@ // based on one online buddy entry bytes = 0; // 000-030 qq_buddy_status - bytes += _qq_buddy_status_read(data, &cursor, len, fe->s); + bytes += qq_buddy_status_read(data, &cursor, len, fe->s); // 031-032: unknown4 bytes += read_packet_w(data, &cursor, len, &fe->unknown1); // 033-033: flag1 @@ -209,8 +209,8 @@ continue; } // check if it is a valid entry -// if (QQ_DEBUG) -// _qq_buddies_online_reply_dump_unclear(fe); + if (QQ_DEBUG) + _qq_buddies_online_reply_dump_unclear(fe); // update buddy information b = gaim_find_buddy(gaim_connection_get_account(gc), uid_to_gaim_name(fe->s->uid)); @@ -219,10 +219,10 @@ if (q_bud != NULL) { // we find one and update qq_buddy if(0 != fe->s->client_version) q_bud->client_version = fe->s->client_version; //by gfhuang - if(0 != *((guint32 *)fe->s->ip)) { // by gfhuang + // if(0 != *((guint32 *)fe->s->ip)) { // by gfhuang g_memmove(q_bud->ip, fe->s->ip, 4); q_bud->port = fe->s->port; - } + // } q_bud->status = fe->s->status; q_bud->flag1 = fe->flag1; q_bud->comm_flag = fe->comm_flag; Modified: trunk/src/protocols/qq/buddy_status.c =================================================================== --- trunk/src/protocols/qq/buddy_status.c 2006-07-24 09:25:48 UTC (rev 16561) +++ trunk/src/protocols/qq/buddy_status.c 2006-07-24 13:39:12 UTC (rev 16562) @@ -34,6 +34,8 @@ #include "keep_alive.h" // qq_update_buddy_contact #include "send_core.h" // qq_send_cmd +#include "qq_proxy.h" + #define QQ_MISC_STATUS_HAVING_VIIDEO 0x00000001 #define QQ_ICON_SUFFIX_DEFAULT QQ_ICON_SUFFIX_OFFLINE @@ -46,7 +48,7 @@ }; /*****************************************************************************/ -static void _qq_buddy_status_dump_unclear(qq_buddy_status * s) +void qq_buddy_status_dump_unclear(qq_buddy_status * s) { GString *dump; @@ -55,14 +57,23 @@ dump = g_string_new(""); g_string_append_printf(dump, "unclear fields for [%d]:\n", s->uid); g_string_append_printf(dump, "004: %02x (unknown)\n", s->unknown1); + //g_string_append_printf(dump, "005-008: %09x (ip)\n", *(s->ip)); + g_string_append_printf(dump, "009-010: %04x (port)\n", s->port); g_string_append_printf(dump, "011: %02x (unknown)\n", s->unknown2); + g_string_append_printf(dump, "012: %02x (status)\n", s->status); + g_string_append_printf(dump, "013-014: %04x (client_version)\n", s->client_version); + //g_string_append_printf(dump, "015-030: %s (unknown key)\n", s->unknown_key); gaim_debug(GAIM_DEBUG_INFO, "QQ", "Buddy status entry, %s", dump->str); + _qq_show_packet("Unknown key", s->unknown_key, QQ_KEY_LENGTH); g_string_free(dump, TRUE); -} // _qq_buddy_status_dump_unclear +} /*****************************************************************************/ +// TODO: figure out what's going on with the IP region. Sometimes I get things which +// may be valid IP addresses, but the port number's weird, other times I get 0s. +// Note: I get these simultaneously on the same buddy, using different accounts to get info. // parse the data into qq_buddy_status -gint _qq_buddy_status_read(guint8 * data, guint8 ** cursor, gint len, qq_buddy_status * s) { +gint qq_buddy_status_read(guint8 * data, guint8 ** cursor, gint len, qq_buddy_status * s) { gint bytes; g_return_val_if_fail(data != NULL && *cursor != NULL && s != NULL, -1); @@ -73,9 +84,12 @@ bytes += read_packet_dw(data, cursor, len, &s->uid); // 004-004: 0x01 bytes += read_packet_b(data, cursor, len, &s->unknown1); + // this is no longer the IP, it seems QQ (as of 2006) no longer sends + // the buddy's IP in this packet. all 0s // 005-008: ip s->ip = g_new0(guint8, 4); bytes += read_packet_data(data, cursor, len, s->ip, 4); + // port info is no longer here either // 009-010: port bytes += read_packet_w(data, cursor, len, &s->port); // 011-011: 0x00 @@ -93,7 +107,7 @@ return bytes; -} // _qq_buddy_status_read +} /*****************************************************************************/ // check if status means online or offline @@ -159,7 +173,7 @@ break; default: away_cmd = QQ_BUDDY_ONLINE_NORMAL; - } // switch + } raw_data = g_new0(guint8, 5); cursor = raw_data; @@ -175,7 +189,7 @@ qq_send_cmd(gc, QQ_CMD_CHANGE_ONLINE_STATUS, TRUE, 0, TRUE, raw_data, 5); g_free(raw_data); -} // qq_send_packet_change_status +} /*****************************************************************************/ // parse the reply packet for change_status @@ -201,7 +215,7 @@ } else gaim_debug(GAIM_DEBUG_ERROR, "QQ", "Error decrypt chg status reply\n"); -} // qq_process_change_status_reply +} /*****************************************************************************/ // it is a server message @@ -228,7 +242,7 @@ s = g_new0(qq_buddy_status, 1); bytes = 0; // 000-030: qq_buddy_status; - bytes += _qq_buddy_status_read(data, &cursor, len, s); + bytes += qq_buddy_status_read(data, &cursor, len, s); // 031-034: my uid bytes += read_packet_dw(data, &cursor, len, &my_uid); @@ -242,19 +256,19 @@ // if (QQ_DEBUG) gfhuang // _qq_buddy_status_dump_unclear(s); - name = uid_to_gaim_name(s->uid); //by gfhuang + name = uid_to_gaim_name(s->uid); b = gaim_find_buddy(gc->account, name); g_free(name); q_bud = (b == NULL) ? NULL : (qq_buddy *) b->proto_data; if (q_bud) { gaim_debug(GAIM_DEBUG_INFO, "QQ", "s->uid = %d, q_bud->uid = %d\n", s->uid , q_bud->uid); - if(0 != *((guint32 *)s->ip)) { //by gfhuang + if(0 != *((guint32 *)s->ip)) { g_memmove(q_bud->ip, s->ip, 4); q_bud->port = s->port; } q_bud->status = s->status; if(0 != s->client_version) - q_bud->client_version = s->client_version; //gfhuang + q_bud->client_version = s->client_version; qq_update_buddy_contact(gc, q_bud); } else @@ -265,8 +279,7 @@ g_free(s); } else gaim_debug(GAIM_DEBUG_ERROR, "QQ", "Error decrypt buddy status change packet\n"); +} -} // qq_process_friend_change_status - /*****************************************************************************/ // END OF FILE Modified: trunk/src/protocols/qq/buddy_status.h =================================================================== --- trunk/src/protocols/qq/buddy_status.h 2006-07-24 09:25:48 UTC (rev 16561) +++ trunk/src/protocols/qq/buddy_status.h 2006-07-24 13:39:12 UTC (rev 16562) @@ -56,9 +56,11 @@ QQ_SELF_STATUS_CUSTOM = 0x14, QQ_SELF_STATUS_IDLE = 0x15, }; - + +void qq_buddy_status_dump_unclear(qq_buddy_status * s); gboolean is_online(guint8 status); +gint qq_buddy_status_read(guint8 * data, guint8 ** cursor, gint len, qq_buddy_status * s); gchar get_suffix_from_status(guint8 status); void qq_send_packet_change_status(GaimConnection * gc); Deleted: trunk/src/protocols/qq/group_admindlg.c =================================================================== --- trunk/src/protocols/qq/group_admindlg.c 2006-07-24 09:25:48 UTC (rev 16561) +++ trunk/src/protocols/qq/group_admindlg.c 2006-07-24 13:39:12 UTC (rev 16562) @@ -1,659 +0,0 @@ -/** -* The QQ2003C protocol plugin - * - * for gaim - * - * Copyright (C) 2004 Puzzlebird - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -// START OF FILE -/*****************************************************************************/ -#include "debug.h" // gaim_debug -#include "blist.h" // GAIM_BLIST_NODE_IS_BUDDY -#include "notify.h" // gaim_notify_warning - -#include "utils.h" // gaim_name_to_uid -#include "group_admindlg.h" -#include "group_find.h" // qq_group_find_by_internal_group_id -#include "group_join.h" // auth_type -#include "group_opt.h" // QQ_GROUP_TYPE_PERMANENT - -enum { - COLUMN_SELECTED = 0, - COLUMN_UID, - COLUMN_NICKNAME, - NUM_COLUMNS -}; - -enum { - PAGE_INFO = 0, - PAGE_MEMBER, -}; - -typedef struct _qun_info_window { - guint32 internal_group_id; - GaimConnection *gc; - GtkWidget *window; - GtkWidget *notebook; - GtkWidget *lbl_external_group_id; - GtkWidget *lbl_admin_uid; - GtkWidget *ent_group_name; - GtkWidget *cmb_group_category; - GtkWidget *txt_group_desc; - GtkWidget *txt_group_notice; - GtkWidget *rad_auth[3]; - GtkWidget *btn_mod; - GtkWidget *btn_close; - GtkWidget *tre_members; -} qun_info_window; - -const gchar *qq_group_category[] = { - "同学", "朋友", "同事", "其他", -}; // qq_group_category - -const gchar *qq_group_auth_type_desc[] = { - "无须认证", "需要认证", "不可添加", -}; // qq_group_auth_type_desc - -/*****************************************************************************/ -static void _qq_group_info_window_deleteevent(GtkWidget * widget, GdkEvent * event, gpointer data) { - gtk_widget_destroy(widget); // this will call _window_destroy -} // _window_deleteevent - -/*****************************************************************************/ -static void _qq_group_info_window_close(GtkWidget * widget, gpointer data) -{ - // this will call _info_window_destroy if it is info-window - gtk_widget_destroy(GTK_WIDGET(data)); -} // _window_close - -/*****************************************************************************/ -static void _qq_group_info_window_destroy(GtkWidget * widget, gpointer data) -{ - GaimConnection *gc; - GList *list; - qq_data *qd; - qun_info_window *info_window; - - gc = (GaimConnection *) data; - g_return_if_fail(gc != NULL && gc->proto_data != NULL); - gaim_debug(GAIM_DEBUG_INFO, "QQ", "Group info is destoryed\n"); - - qd = (qq_data *) gc->proto_data; - list = qd->qun_info_window; - - while (list) { - info_window = (qun_info_window *) (list->data); - if (info_window->window != widget) - list = list->next; - else { - qd->qun_info_window = g_list_remove(qd->qun_info_window, info_window); - g_free(info_window); - break; - } // if info_window - } // while -} // _window_destroy - -/*****************************************************************************/ -void qq_qun_info_window_free(qq_data * qd) -{ - gint i; - qun_info_window *info_window; - - i = 0; - while (qd->qun_info_window) { - info_window = (qun_info_window *) qd->qun_info_window->data; - qd->qun_info_window = g_list_remove(qd->qun_info_window, info_window); - if (info_window->window) - gtk_widget_destroy(info_window->window); - g_free(info_window); - i++; - } // while - - gaim_debug(GAIM_DEBUG_INFO, "QQ", "%d Qun info windows are freed\n", i); -} // qq_qun_info_window_free - -/*****************************************************************************/ -static void _qq_group_info_window_modify(GtkWidget * widget, gpointer data) -{ - GaimConnection *gc; - qun_info_window *info_window; - - g_return_if_fail(data != NULL); - info_window = (qun_info_window *) data; - - gc = info_window->gc; - g_return_if_fail(gc != NULL && gc->proto_data != NULL); - - //henry: This function contains some codes only supported by gtk-2.4 or later -//#if !GTK_CHECK_VERSION(2,4,0) -// gaim_notify_info(gc, _("QQ Qun Operation"), -// _("This version of GTK-2 does not support this function"), NULL); -// return; -//#else - gint page, group_category, i = 0; - qq_group *group; - qq_data *qd; - GtkTextIter start, end; - GtkTreeModel *model; - GtkTreeIter iter; - GValue value = { 0, }; - guint32 *new_members; - guint32 uid; - gboolean selected; - - qd = (qq_data *) gc->proto_data; - - // we assume the modification can succeed - // maybe it needs some tweak here - group = qq_group_find_by_internal_group_id(gc, info_window->internal_group_id); - g_return_if_fail(group != NULL); - - new_members = g_newa(guint32, QQ_QUN_MEMBER_MAX); - - page = gtk_notebook_get_current_page(GTK_NOTEBOOK(info_window->notebook)); - switch (page) { - case PAGE_INFO: - gaim_debug(GAIM_DEBUG_INFO, "QQ", "Gonna change Qun detailed information\n"); - // get the group_category -#if GTK_CHECK_VERSION(2,4,0) - group_category = gtk_combo_box_get_active(GTK_COMBO_BOX(info_window->cmb_group_category)); -#else - group_category = gtk_option_menu_get_history(GTK_OPTION_MENU(info_window->cmb_group_category)); -#endif - - if (group_category >= 0) - group->group_category = group_category; - else { - g_free(group); - gaim_debug(GAIM_DEBUG_ERROR, "QQ", "Invalid group_category: %d\n", group_category); - return; - } // if group_category - // get auth_type - if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(info_window->rad_auth[0]))) - group->auth_type = QQ_GROUP_AUTH_TYPE_NO_AUTH; - else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(info_window->rad_auth[1]))) - group->auth_type = QQ_GROUP_AUTH_TYPE_NEED_AUTH; - else - group->auth_type = QQ_GROUP_AUTH_TYPE_NO_ADD; - // MUST use g_strdup, otherwise core dump after info_window is closed - group->group_name_utf8 = g_strdup(gtk_entry_get_text(GTK_ENTRY(info_window->ent_group_name))); - gtk_text_buffer_get_bounds(gtk_text_view_get_buffer - (GTK_TEXT_VIEW(info_window->txt_group_desc)), &start, &end); - group->group_desc_utf8 = - g_strdup(gtk_text_buffer_get_text - (gtk_text_view_get_buffer - (GTK_TEXT_VIEW(info_window->txt_group_desc)), &start, &end, FALSE)); - gtk_text_buffer_get_bounds(gtk_text_view_get_buffer - (GTK_TEXT_VIEW(info_window->txt_group_notice)), &start, &end); - group->notice_utf8 = - g_strdup(gtk_text_buffer_get_text - (gtk_text_view_get_buffer - (GTK_TEXT_VIEW(info_window->txt_group_notice)), &start, &end, FALSE)); - // finally we can modify it with new information - qq_group_modify_info(gc, group); - break; - case PAGE_MEMBER: - if (info_window->tre_members == NULL) { - gaim_debug(GAIM_DEBUG_INFO, "QQ", "Member list is not ready, cannot modify!\n"); - } else { - gaim_debug(GAIM_DEBUG_INFO, "QQ", "Gonna change Qun member list\n"); - model = gtk_tree_view_get_model(GTK_TREE_VIEW(info_window->tre_members)); - if (gtk_tree_model_get_iter_first(model, &iter)) { - gtk_tree_model_get_value(model, &iter, COLUMN_UID, &value); - uid = g_value_get_uint(&value); - g_value_unset(&value); - gtk_tree_model_get_value(model, &iter, COLUMN_SELECTED, &value); - selected = g_value_get_boolean(&value); - g_value_unset(&value); - if (!selected) - new_members[i++] = uid; - while (gtk_tree_model_iter_next(model, &iter)) { - gtk_tree_model_get_value(model, &iter, COLUMN_UID, &value); - uid = g_value_get_uint(&value); - g_value_unset(&value); - gtk_tree_model_get_value(model, &iter, COLUMN_SELECTED, &value); - selected = g_value_get_boolean(&value); - g_value_unset(&value); - if (!selected) - new_members[i++] = uid; - } // while - new_members[i] = 0xffffffff; // this labels the end - } else - new_members[0] = 0xffffffff; - qq_group_modify_members(gc, group, new_members); - } // if info_window->tre_members - break; - default: - gaim_debug(GAIM_DEBUG_INFO, "QQ", "Invalid page number: %d\n", page); - } // switch - - _qq_group_info_window_close(NULL, info_window->window); - -//#endif /* GTK_CHECK_VERSION */ -} // _qq_group_info_window_modify - -/*****************************************************************************/ -static void _qq_group_member_list_deleted_toggled(GtkCellRendererToggle * cell, gchar * path_str, gpointer data) { - qun_info_window *info_window; - GaimConnection *gc; - qq_group *group; - - info_window = (qun_info_window *) data; - g_return_if_fail(info_window != NULL); - - gc = info_window->gc; - g_return_if_fail(gc != NULL); - - group = qq_group_find_by_internal_group_id(gc, info_window->internal_group_id); - g_return_if_fail(group != NULL); - - GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(info_window->tre_members)); - GtkTreeIter iter; - GtkTreePath *path = gtk_tree_path_new_from_string(path_str); - gboolean selected; - guint32 uid; - - gtk_tree_model_get_iter(model, &iter, path); - gtk_tree_model_get(model, &iter, COLUMN_SELECTED, &selected, -1); - gtk_tree_model_get(model, &iter, COLUMN_UID, &uid, -1); - - if (uid != group->creator_uid) { // do not allow delete admin - selected ^= 1; - gtk_list_store_set(GTK_LIST_STORE(model), &iter, COLUMN_SELECTED, selected, -1); - gtk_tree_path_free(path); - } else - gaim_notify_error(gc, NULL, _("Qun creator cannot be removed"), NULL); -} // _qq_group_member_list_deleted_toggled - -/*****************************************************************************/ -static void _qq_group_member_list_drag_data_rcv_cb - (GtkWidget * widget, GdkDragContext * dc, guint x, guint y, - GtkSelectionData * sd, guint info, guint t, gpointer data) { - - GaimConnection *gc; - GaimAccount *account; - GaimBlistNode *n = NULL; - GaimContact *c = NULL; - GaimBuddy *b = NULL; - GtkWidget *treeview; - GtkTreeModel *model; - GtkListStore *store; - GtkTreeIter iter; - GValue value = { 0, }; - guint32 uid, input_uid; - - treeview = widget; - gc = (GaimConnection *) data; - g_return_if_fail(gc != NULL); - account = gaim_connection_get_account(gc); - - if (sd->target != gdk_atom_intern("GAIM_BLIST_NODE", FALSE) || sd->data == NULL) { - gaim_debug(GAIM_DEBUG_ERROR, "QQ", "Invalid drag data received, discard...\n"); - return; - } // if (sd->target - - memcpy(&n, sd->data, sizeof(n)); - - // we expect GAIM_BLIST_CONTACT_NODE and GAIM_BLIST_BUDDY_NODE - if (GAIM_BLIST_NODE_IS_CONTACT(n)) { - c = (GaimContact *) n; - b = c->priority; // we get the first buddy only - } else if (GAIM_BLIST_NODE_IS_BUDDY(n)) - b = (GaimBuddy *) n; - - if (b == NULL) { - gaim_debug(GAIM_DEBUG_ERROR, "QQ", "No valid GaimBuddy is passed from DnD\n"); - return; - } // if b == NULL - - gaim_debug(GAIM_DEBUG_INFO, "QQ", "We get a GaimBuddy: %s\n", b->name); - input_uid = gaim_name_to_uid(b->name); - g_return_if_fail(input_uid > 0); - - model = gtk_tree_view_get_model(GTK_TREE_VIEW(treeview)); - // we need to check if the user id in the member list is unique - // possibly a tree transverse is necessary to achieve this - if (gtk_tree_model_get_iter_first(model, &iter)) { - gtk_tree_model_get_value(model, &iter, COLUMN_UID, &value); - uid = g_value_get_uint(&value); - g_value_unset(&value); - while (uid != input_uid && gtk_tree_model_iter_next(model, &iter)) { - gtk_tree_model_get_value(model, &iter, COLUMN_UID, &value); - uid = g_value_get_uint(&value); - g_value_unset(&value); - } // while - } else - uid = 0; // if gtk_tree_model_get_iter_first - - if (uid == input_uid) { - gaim_debug(GAIM_DEBUG_WARNING, "QQ", "Qun already has this buddy %s\n", b->name); - return; - } else { // we add it to list - store = GTK_LIST_STORE(model); - gtk_list_store_append(store, &iter); - gtk_list_store_set(store, &iter, - COLUMN_SELECTED, FALSE, COLUMN_UID, input_uid, COLUMN_NICKNAME, b->alias, -1); - // re-sort the list - gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(store), COLUMN_UID, GTK_SORT_ASCENDING); - } // if uid - -} // _qq_group_member_list_drag_data_rcv_cb - -/*****************************************************************************/ -static GtkWidget *_create_page_info(GaimConnection * gc, qq_group * group, gboolean do_manage, qun_info_window * info_window) { - GtkWidget *vbox, *hbox; - GtkWidget *frame_info, *frame_auth; - GtkWidget *tbl_info; - GtkWidget *label, *entry, *combo, *text, *scrolled_window; - gint i; - - g_return_val_if_fail(gc != NULL && group != NULL, NULL); - - vbox = gtk_vbox_new(FALSE, 5); - - frame_info = gtk_frame_new(NULL); - gtk_box_pack_start(GTK_BOX(vbox), frame_info, TRUE, TRUE, 0); - - tbl_info = gtk_table_new(6, 4, FALSE); - gtk_table_set_row_spacings(GTK_TABLE(tbl_info), 4); - gtk_table_set_col_spacing(GTK_TABLE(tbl_info), 1, 10); - gtk_container_add(GTK_CONTAINER(frame_info), tbl_info); - - label = gtk_label_new(_("Group ID: ")); - gtk_table_attach(GTK_TABLE(tbl_info), label, 0, 1, 0, 1, GTK_FILL, GTK_FILL, 0, 0); - label = gtk_label_new(g_strdup_printf("%d", group->external_group_id)); - gtk_table_attach(GTK_TABLE(tbl_info), label, 1, 2, 0, 1, GTK_FILL, GTK_FILL, 0, 0); - info_window->lbl_external_group_id = label; - - label = gtk_label_new(_("Group Name")); - gtk_table_attach(GTK_TABLE(tbl_info), label, 2, 3, 0, 1, GTK_FILL, GTK_FILL, 0, 0); - entry = gtk_entry_new(); - gtk_widget_set_size_request(entry, 100, -1); - if (group->group_name_utf8 != NULL) - gtk_entry_set_text(GTK_ENTRY(entry), group->group_name_utf8); - gtk_table_attach(GTK_TABLE(tbl_info), entry, 3, 4, 0, 1, GTK_FILL, GTK_FILL, 0, 0); - info_window->ent_group_name = entry; - - label = gtk_label_new(_("Admin: ")); - gtk_table_attach(GTK_TABLE(tbl_info), label, 0, 1, 1, 2, GTK_FILL, GTK_FILL, 0, 0); - label = gtk_label_new(g_strdup_printf("%d", group->creator_uid)); - gtk_table_attach(GTK_TABLE(tbl_info), label, 1, 2, 1, 2, GTK_FILL, GTK_FILL, 0, 0); - info_window->lbl_admin_uid = label; - - label = gtk_label_new(_("Category")); - gtk_table_attach(GTK_TABLE(tbl_info), label, 2, 3, 1, 2, GTK_FILL, GTK_FILL, 0, 0); - - //henry: these codes are supported only in GTK-2.4 or later -#if GTK_CHECK_VERSION(2, 4, 0) - combo = gtk_combo_box_new_text(); - for (i = 0; i < 4; i++) - gtk_combo_box_append_text(GTK_COMBO_BOX(combo), qq_group_category[i]); - gtk_combo_box_set_active(GTK_COMBO_BOX(combo), group->group_category); -#else - GtkWidget *menu; - GtkWidget *item; - - combo = gtk_option_menu_new(); - menu = gtk_menu_new(); - for (i = 0; i < 4; i++) { - item = gtk_menu_item_new_with_label(qq_group_category[i]); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - gtk_widget_show(item); - } - gtk_option_menu_set_menu(GTK_OPTION_MENU(combo), menu); - gtk_option_menu_set_history(GTK_OPTION_MENU(combo), group->group_category); -#endif /* GTK_CHECK_VERSION */ - - gtk_table_attach(GTK_TABLE(tbl_info), combo, 3, 4, 1, 2, GTK_FILL, GTK_FILL, 0, 0); - info_window->cmb_group_category = combo; - - label = gtk_label_new(_("Description")); - gtk_table_attach(GTK_TABLE(tbl_info), label, 0, 1, 2, 3, GTK_FILL, GTK_FILL, 0, 0); - text = gtk_text_view_new(); - gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD); - gtk_widget_set_size_request(text, -1, 50); - if (group->group_desc_utf8 != NULL) - gtk_text_buffer_set_text(gtk_text_view_get_buffer(GTK_TEXT_VIEW(text)), group->group_desc_utf8, -1); - info_window->txt_group_desc = text; - - scrolled_window = gtk_scrolled_window_new(NULL, NULL); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); - gtk_container_add(GTK_CONTAINER(scrolled_window), text); - gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled_window), GTK_SHADOW_IN); - gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text), 2); - gtk_text_view_set_right_margin(GTK_TEXT_VIEW(text), 2); - gtk_table_attach(GTK_TABLE(tbl_info), scrolled_window, 0, 4, 3, 4, GTK_FILL, GTK_FILL, 0, 0); - - label = gtk_label_new(_("Group Notice")); - gtk_table_attach(GTK_TABLE(tbl_info), label, 0, 1, 4, 5, GTK_FILL, GTK_FILL, 0, 0); - text = gtk_text_view_new(); - gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD); - gtk_widget_set_size_request(text, -1, 50); - if (group->notice_utf8 != NULL) - gtk_text_buffer_set_text(gtk_text_view_get_buffer(GTK_TEXT_VIEW(text)), group->notice_utf8, -1); - info_window->txt_group_notice = text; - - scrolled_window = gtk_scrolled_window_new(NULL, NULL); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); - gtk_container_add(GTK_CONTAINER(scrolled_window), text); - gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled_window), GTK_SHADOW_IN); - gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text), 2); - gtk_text_view_set_right_margin(GTK_TEXT_VIEW(text), 2); - gtk_table_attach(GTK_TABLE(tbl_info), scrolled_window, 0, 4, 5, 6, GTK_FILL, GTK_FILL, 0, 0); - - frame_auth = gtk_frame_new(_("Authentication")); - hbox = gtk_hbox_new(FALSE, 5); - gtk_container_add(GTK_CONTAINER(frame_auth), hbox); - info_window->rad_auth[0] = gtk_radio_button_new_with_label(NULL, qq_group_auth_type_desc[0]); - info_window->rad_auth[1] = - gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON - (info_window->rad_auth[0]), qq_group_auth_type_desc[1]); - info_window->rad_auth[2] = - gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON - (info_window->rad_auth[0]), qq_group_auth_type_desc[2]); - for (i = 0; i < 3; i++) - gtk_box_pack_start(GTK_BOX(hbox), info_window->rad_auth[i], FALSE, FALSE, 0); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(info_window->rad_auth[group->auth_type - 1]), TRUE); - gtk_box_pack_start(GTK_BOX(vbox), frame_auth, FALSE, FALSE, 0); - - if (!do_manage) { - gtk_widget_set_sensitive(frame_info, FALSE); - gtk_widget_set_sensitive(frame_auth, FALSE); - } // if ! do_manage - - return vbox; -} // _create_info_page - -/*****************************************************************************/ -static GtkWidget *_create_page_members - (GaimConnection * gc, qq_group * group, gboolean do_manage, qun_info_window * info_window) { - GtkWidget *vbox, *sw, *treeview; - GtkTreeModel *model; - GtkListStore *store; - GtkTreeIter iter; - GtkCellRenderer *renderer; - GtkTreeViewColumn *column; - GList *list; - qq_buddy *q_bud; - GtkTargetEntry gte = { "GAIM_BLIST_NODE", GTK_TARGET_SAME_APP, 0 }; - - g_return_val_if_fail(gc != NULL && group != NULL, NULL); - - vbox = gtk_vbox_new(FALSE, 0); - - if (group->members == NULL) { // if NULL, not ready - sw = gtk_label_new(_ - ("OpenQ is collecting member information.\nPlease close this window and open again")); - gtk_box_pack_start(GTK_BOX(vbox), sw, TRUE, TRUE, 0); - return vbox; - } // if group->members - - sw = gtk_scrolled_window_new(NULL, NULL); - gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_ETCHED_IN); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); - gtk_box_pack_start(GTK_BOX(vbox), sw, TRUE, TRUE, 0); - - store = gtk_list_store_new(NUM_COLUMNS, G_TYPE_BOOLEAN, G_TYPE_UINT, G_TYPE_STRING); - - list = group->members; - while (list != NULL) { - q_bud = (qq_buddy *) list->data; - gtk_list_store_append(store, &iter); - gtk_list_store_set(store, &iter, - COLUMN_SELECTED, FALSE, - COLUMN_UID, q_bud->uid, COLUMN_NICKNAME, q_bud->nickname, -1); - list = list->next; - } // for - - model = GTK_TREE_MODEL(store); - treeview = gtk_tree_view_new_with_model(model); - info_window->tre_members = treeview; - - gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(treeview), TRUE); - gtk_tree_view_set_search_column(GTK_TREE_VIEW(treeview), COLUMN_UID); - g_object_unref(model); - - // set up drag & drop ONLY for managable Qun - if (do_manage) { - gtk_tree_view_enable_model_drag_dest(GTK_TREE_VIEW(treeview), >e, 1, GDK_ACTION_COPY); - g_signal_connect(G_OBJECT(treeview), "drag-data-received", - G_CALLBACK(_qq_group_member_list_drag_data_rcv_cb), gc); - } // if do manage - - gtk_container_add(GTK_CONTAINER(sw), treeview); - - model = gtk_tree_view_get_model(GTK_TREE_VIEW(treeview)); - renderer = gtk_cell_renderer_toggle_new(); - - // it seems this signal has to be handled - // otherwise, the checkbox in the column does not reponse to user action - if (do_manage) - g_signal_connect(renderer, "toggled", G_CALLBACK(_qq_group_member_list_deleted_toggled), info_window); - - column = gtk_tree_view_column_new_with_attributes(_("Del"), renderer, "active", COLUMN_SELECTED, NULL); - - gtk_tree_view_column_set_sizing(GTK_TREE_VIEW_COLUMN(column), GTK_TREE_VIEW_COLUMN_FIXED); - gtk_tree_view_column_set_fixed_width(GTK_TREE_VIEW_COLUMN(column), 30); - gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column); - - renderer = gtk_cell_renderer_text_new(); - column = gtk_tree_view_column_new_with_attributes(_("UID"), renderer, "text", COLUMN_UID, NULL); - gtk_tree_view_column_set_sort_column_id(column, COLUMN_UID); - gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column); - // default sort by UID - gtk_tree_view_column_set_sort_order(column, GTK_SORT_ASCENDING); - gtk_tree_view_column_set_sort_indicator(column, TRUE); - - renderer = gtk_cell_renderer_text_new(); - column = gtk_tree_view_column_new_with_attributes(_("Nickname"), renderer, "text", COLUMN_NICKNAME, NULL); - gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column); - - return vbox; -} // _create_page_members - -/**************************************************************... [truncated message content] |