pidgin/gtkconv.c

branch
soc.2013.gobjectification.plugins
changeset 36971
602023a35d92
parent 36967
1dbfbb0eebdf
parent 35129
22dca763eeef
child 36972
dfe5c47d56a9
equal deleted inserted replaced
36970:ec09b0b91559 36971:602023a35d92
4348 if (is_me && color) 4348 if (is_me && color)
4349 gdk_color_free(color); 4349 gdk_color_free(color);
4350 g_free(alias_key); 4350 g_free(alias_key);
4351 } 4351 }
4352 4352
4353 #if 0
4354 /** 4353 /**
4355 * @param most_matched Used internally by this function. 4354 * @param most_matched Used internally by this function.
4356 * @param entered The partial string that the user types before hitting the 4355 * @param entered The partial string that the user types before hitting the
4357 * tab key. 4356 * tab key.
4358 * @param entered_bytes The length of entered. 4357 * @param entered_chars The length of entered.
4359 * @param partial This is a return variable. This will be set to a string 4358 * @param partial This is a return variable. This will be set to a string
4360 * containing the largest common string between all matches. This will 4359 * containing the largest common string between all matches. This will
4361 * be inserted into the input box at the start of the word that the 4360 * be inserted into the input box at the start of the word that the
4362 * user is tab completing. For example, if a chat room contains 4361 * user is tab completing. For example, if a chat room contains
4363 * "AlfFan" and "AlfHater" and the user types "a<TAB>" then this will 4362 * "AlfFan" and "AlfHater" and the user types "a<TAB>" then this will
4364 * contain "Alf" 4363 * contain "Alf"
4365 * @param nick_partial Used internally by this function. Shoudl be a
4366 * temporary buffer that is entered_bytes+1 bytes long.
4367 * @param matches This is a return variable. If the given name is a potential 4364 * @param matches This is a return variable. If the given name is a potential
4368 * match for the entered string, then add a copy of the name to this 4365 * match for the entered string, then add a copy of the name to this
4369 * list. The caller is responsible for g_free'ing the data in this 4366 * list. The caller is responsible for g_free'ing the data in this
4370 * list. 4367 * list.
4371 * @param name The buddy name or alias or slash command name that we're 4368 * @param name The buddy name or alias or slash command name that we're
4372 * checking for a match. 4369 * checking for a match.
4373 */ 4370 */
4374 static void 4371 static void
4375 tab_complete_process_item(int *most_matched, const char *entered, gsize entered_bytes, char **partial, char *nick_partial, 4372 tab_complete_process_item(int *most_matched, const char *entered, gsize entered_chars, char **partial,
4376 GList **matches, const char *name) 4373 GList **matches, const char *name)
4377 { 4374 {
4378 memcpy(nick_partial, name, entered_bytes); 4375 char *nick_partial;
4379 if (purple_utf8_strcasecmp(nick_partial, entered)) 4376 gsize name_len = g_utf8_strlen(name, -1);
4377
4378 if ((glong)entered_chars > name_len)
4380 return; 4379 return;
4380
4381 nick_partial = g_utf8_substring(name, 0, entered_chars);
4382 if (purple_utf8_strcasecmp(nick_partial, entered)) {
4383 g_free(nick_partial);
4384 return;
4385 }
4386 g_free(nick_partial);
4381 4387
4382 /* if we're here, it's a possible completion */ 4388 /* if we're here, it's a possible completion */
4383 4389
4384 if (*most_matched == -1) { 4390 if (*most_matched == -1) {
4385 /* 4391 /*
4386 * this will only get called once, since from now 4392 * this will only get called once, since from now
4387 * on *most_matched is >= 0 4393 * on *most_matched is >= 0
4388 */ 4394 */
4389 *most_matched = strlen(name); 4395 *most_matched = name_len;
4390 *partial = g_strdup(name); 4396 *partial = g_strdup(name);
4391 } 4397 }
4392 else if (*most_matched) { 4398 else if (*most_matched) {
4393 char *tmp = g_strdup(name); 4399 char *tmp = g_strdup(name);
4394 4400
4395 while (purple_utf8_strcasecmp(tmp, *partial)) { 4401 while (purple_utf8_strcasecmp(tmp, *partial)) {
4396 (*partial)[*most_matched] = '\0'; 4402 *(g_utf8_offset_to_pointer(*partial, *most_matched)) = '\0';
4397 if (*most_matched < (goffset)strlen(tmp)) 4403 if (*most_matched < (goffset)g_utf8_strlen(tmp, -1))
4398 tmp[*most_matched] = '\0'; 4404 *(g_utf8_offset_to_pointer(tmp, *most_matched)) = '\0';
4399 (*most_matched)--; 4405 (*most_matched)--;
4400 } 4406 }
4401 (*most_matched)++; 4407 (*most_matched)++;
4402 4408
4403 g_free(tmp); 4409 g_free(tmp);
4404 } 4410 }
4405 4411
4406 *matches = g_list_insert_sorted(*matches, g_strdup(name), 4412 *matches = g_list_insert_sorted(*matches, g_strdup(name),
4407 (GCompareFunc)purple_utf8_strcasecmp); 4413 (GCompareFunc)purple_utf8_strcasecmp);
4408 } 4414 }
4409 #endif 4415
4416 static gboolean
4417 is_first_container(WebKitDOMNode *container)
4418 {
4419 gchar *name;
4420 WebKitDOMNode *parent;
4421
4422 while (container) {
4423 parent = webkit_dom_node_get_parent_node(container);
4424 if (parent) {
4425 name = webkit_dom_node_get_node_name(parent);
4426
4427 if (!strcmp(name, "BODY")) {
4428 g_free(name);
4429
4430 if (webkit_dom_node_get_previous_sibling(container) == NULL)
4431 return TRUE;
4432 else
4433 return FALSE;
4434 }
4435 g_free(name);
4436 }
4437 else
4438 break;
4439
4440 container = parent;
4441 }
4442
4443 return FALSE;
4444 }
4410 4445
4411 static gboolean 4446 static gboolean
4412 tab_complete(PurpleConversation *conv) 4447 tab_complete(PurpleConversation *conv)
4413 { 4448 {
4414 #if 0
4415 /* TODO WebKit */
4416 PidginConversation *gtkconv; 4449 PidginConversation *gtkconv;
4417 GtkTextIter cursor, word_start, start_buffer; 4450 WebKitDOMNode *container;
4418 int start; 4451 glong caret, word_start, content_len;
4419 int most_matched = -1; 4452 int most_matched = -1, colon = 0;
4453 char *ch, *ch2 = NULL;
4420 char *entered, *partial = NULL; 4454 char *entered, *partial = NULL;
4421 char *text; 4455 char *content, *sub1, *sub2, *modified;
4422 char *nick_partial;
4423 const char *prefix; 4456 const char *prefix;
4424 GList *matches = NULL; 4457 GList *matches = NULL;
4425 gboolean command = FALSE; 4458 gboolean command = FALSE;
4426 gsize entered_bytes = 0; 4459 gsize entered_chars = 0;
4427 4460
4428 gtkconv = PIDGIN_CONVERSATION(conv); 4461 gtkconv = PIDGIN_CONVERSATION(conv);
4429 4462 gtk_webview_get_caret(GTK_WEBVIEW(gtkconv->entry), &container, &caret);
4430 gtk_text_buffer_get_start_iter(gtkconv->entry_buffer, &start_buffer);
4431 gtk_text_buffer_get_iter_at_mark(gtkconv->entry_buffer, &cursor,
4432 gtk_text_buffer_get_insert(gtkconv->entry_buffer));
4433
4434 word_start = cursor;
4435 4463
4436 /* if there's nothing there just return */ 4464 /* if there's nothing there just return */
4437 if (!gtk_text_iter_compare(&cursor, &start_buffer)) 4465 if (caret <= 0)
4438 return PURPLE_IS_CHAT_CONVERSATION(conv); 4466 return PURPLE_IS_CHAT_CONVERSATION(conv);
4439 4467
4440 text = gtk_text_buffer_get_text(gtkconv->entry_buffer, &start_buffer, 4468 content = webkit_dom_node_get_node_value(container);
4441 &cursor, FALSE); 4469 content_len = g_utf8_strlen(content, -1);
4442 4470
4443 /* if we're at the end of ": " we need to move back 2 spaces */ 4471 /* if we're at the end of ":" or ": " we need to move back 1 or 2 spaces */
4444 start = strlen(text) - 1; 4472 if (caret >= 2) {
4445 4473 ch = g_utf8_offset_to_pointer(content, caret - 2);
4446 if (start >= 1 && !strncmp(&text[start-1], ": ", 2)) { 4474 ch2 = g_utf8_find_next_char(ch, NULL);
4447 gtk_text_iter_backward_chars(&word_start, 2); 4475 }
4448 } 4476
4449 4477 if (caret >= 2 && *ch == ':' && (*ch2 == ' ' || g_utf8_get_char(ch2) == 0xA0))
4450 /* find the start of the word that we're tabbing. 4478 colon = 2;
4451 * Using gtk_text_iter_backward_word_start won't work, because a nick can contain 4479 else if (caret >= 1 && content[caret - 1] == ':')
4452 * characters (e.g. '.', '/' etc.) that Pango may think are word separators. */ 4480 colon = 1;
4453 while (gtk_text_iter_backward_char(&word_start)) { 4481
4454 if (gtk_text_iter_get_char(&word_start) == ' ') { 4482 caret -= colon;
4455 /* Reached the whitespace before the start of the word. Move forward once */ 4483 word_start = caret;
4456 gtk_text_iter_forward_char(&word_start); 4484
4485 /* find the start of the word that we're tabbing. */
4486 ch = g_utf8_offset_to_pointer(content, caret);
4487 while ((ch = g_utf8_find_prev_char(content, ch))) {
4488 if (*ch != ' ' && g_utf8_get_char(ch) != 0xA0)
4489 --word_start;
4490 else
4457 break; 4491 break;
4458 }
4459 } 4492 }
4460 4493
4461 prefix = pidgin_get_cmd_prefix(); 4494 prefix = pidgin_get_cmd_prefix();
4462 if (gtk_text_iter_get_offset(&word_start) == 0 && 4495 if (word_start == 0 &&
4463 (strlen(text) >= strlen(prefix)) && !strncmp(text, prefix, strlen(prefix))) { 4496 ((gsize)caret >= strlen(prefix)) && !strncmp(content, prefix, strlen(prefix))) {
4464 command = TRUE; 4497 command = TRUE;
4465 gtk_text_iter_forward_chars(&word_start, strlen(prefix)); 4498 word_start += strlen(prefix);
4466 } 4499 }
4467 4500
4468 g_free(text); 4501 entered = g_utf8_substring(content, word_start, caret);
4469 4502 entered_chars = g_utf8_strlen(entered, -1);
4470 entered = gtk_text_buffer_get_text(gtkconv->entry_buffer, &word_start, 4503
4471 &cursor, FALSE); 4504 if (!entered_chars) {
4472 entered_bytes = strlen(entered); 4505 g_free(content);
4473
4474 if (!g_utf8_strlen(entered, -1)) {
4475 g_free(entered); 4506 g_free(entered);
4476 return PURPLE_IS_CHAT_CONVERSATION(conv); 4507 return PURPLE_IS_CHAT_CONVERSATION(conv);
4477 } 4508 }
4478
4479 nick_partial = g_malloc0(entered_bytes + 1);
4480 4509
4481 if (command) { 4510 if (command) {
4482 GList *list = purple_cmd_list(conv); 4511 GList *list = purple_cmd_list(conv);
4483 GList *l; 4512 GList *l;
4484 4513
4485 /* Commands */ 4514 /* Commands */
4486 for (l = list; l != NULL; l = l->next) { 4515 for (l = list; l != NULL; l = l->next) {
4487 tab_complete_process_item(&most_matched, entered, entered_bytes, &partial, nick_partial, 4516 tab_complete_process_item(&most_matched, entered, entered_chars, &partial,
4488 &matches, l->data); 4517 &matches, l->data);
4489 } 4518 }
4490 g_list_free(list); 4519 g_list_free(list);
4491 } else if (PURPLE_IS_CHAT_CONVERSATION(conv)) { 4520 } else if (PURPLE_IS_CHAT_CONVERSATION(conv)) {
4492 PurpleChatConversation *chat = PURPLE_CONV_CHAT(conv); 4521 GList *l = purple_chat_conversation_get_users(PURPLE_CHAT_CONVERSATION(conv));
4493 GList *l = purple_chat_conversation_get_users(chat);
4494 GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(PIDGIN_CONVERSATION(conv)->u.chat->list)); 4522 GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(PIDGIN_CONVERSATION(conv)->u.chat->list));
4495 GtkTreeIter iter; 4523 GtkTreeIter iter;
4496 int f; 4524 int f;
4497 4525
4498 /* Users */ 4526 /* Users */
4499 for (; l != NULL; l = l->next) { 4527 for (; l != NULL; l = l->next) {
4500 tab_complete_process_item(&most_matched, entered, entered_bytes, &partial, nick_partial, 4528 tab_complete_process_item(&most_matched, entered, entered_chars, &partial,
4501 &matches, purple_chat_user_get_name((PurpleChatUser *)l->data)); 4529 &matches, purple_chat_user_get_name((PurpleChatUser *)l->data));
4502 } 4530 }
4503
4504 4531
4505 /* Aliases */ 4532 /* Aliases */
4506 if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter)) 4533 if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter))
4507 { 4534 {
4508 do { 4535 do {
4513 CHAT_USERS_NAME_COLUMN, &name, 4540 CHAT_USERS_NAME_COLUMN, &name,
4514 CHAT_USERS_ALIAS_COLUMN, &alias, 4541 CHAT_USERS_ALIAS_COLUMN, &alias,
4515 -1); 4542 -1);
4516 4543
4517 if (name && alias && strcmp(name, alias)) 4544 if (name && alias && strcmp(name, alias))
4518 tab_complete_process_item(&most_matched, entered, entered_bytes, &partial, nick_partial, 4545 tab_complete_process_item(&most_matched, entered, entered_chars, &partial,
4519 &matches, alias); 4546 &matches, alias);
4520 g_free(name); 4547 g_free(name);
4521 g_free(alias); 4548 g_free(alias);
4522 4549
4523 f = gtk_tree_model_iter_next(model, &iter); 4550 f = gtk_tree_model_iter_next(model, &iter);
4524 } while (f != 0); 4551 } while (f != 0);
4525 } 4552 }
4526 } else { 4553 } else {
4527 g_free(nick_partial); 4554 g_free(content);
4528 g_free(entered); 4555 g_free(entered);
4529 return FALSE; 4556 return FALSE;
4530 } 4557 }
4531
4532 g_free(nick_partial);
4533
4534 /* we're only here if we're doing new style */
4535 4558
4536 /* if there weren't any matches, return */ 4559 /* if there weren't any matches, return */
4537 if (!matches) { 4560 if (!matches) {
4538 /* if matches isn't set partials won't be either */ 4561 /* if matches isn't set partials won't be either */
4562 g_free(content);
4539 g_free(entered); 4563 g_free(entered);
4540 return PURPLE_IS_CHAT_CONVERSATION(conv); 4564 return PURPLE_IS_CHAT_CONVERSATION(conv);
4541 } 4565 }
4542 4566
4543 gtk_text_buffer_delete(gtkconv->entry_buffer, &word_start, &cursor); 4567 sub1 = g_utf8_substring(content, 0, word_start);
4568 sub2 = g_utf8_substring(content, caret, content_len);
4544 4569
4545 if (!matches->next) { 4570 if (!matches->next) {
4546 /* there was only one match. fill it in. */ 4571 /* there was only one match. fill it in. */
4547 gtk_text_buffer_get_start_iter(gtkconv->entry_buffer, &start_buffer); 4572
4548 gtk_text_buffer_get_iter_at_mark(gtkconv->entry_buffer, &cursor, 4573 if (!colon && !word_start && is_first_container(container)) {
4549 gtk_text_buffer_get_insert(gtkconv->entry_buffer)); 4574 char *tmp = NULL;
4550 4575 if (caret < content_len) {
4551 if (!gtk_text_iter_compare(&cursor, &start_buffer)) { 4576 tmp = g_strdup_printf("%s: ", (char *)matches->data);
4552 char *tmp = g_strdup_printf("%s: ", (char *)matches->data); 4577 } else {
4553 gtk_text_buffer_insert_at_cursor(gtkconv->entry_buffer, tmp, -1); 4578 char utf[6] = {0};
4579 g_unichar_to_utf8(0xA0, utf);
4580 tmp = g_strdup_printf("%s:%s", (char *)matches->data, utf);
4581 }
4582
4583 modified = g_strdup_printf("%s%s", tmp, sub2);
4584 webkit_dom_node_set_node_value(container, modified, NULL);
4585 gtk_webview_set_caret(GTK_WEBVIEW(gtkconv->entry), container,
4586 g_utf8_strlen(tmp, -1));
4554 g_free(tmp); 4587 g_free(tmp);
4555 } 4588 g_free(modified);
4556 else 4589 }
4557 gtk_text_buffer_insert_at_cursor(gtkconv->entry_buffer, 4590 else {
4558 matches->data, -1); 4591 modified = g_strdup_printf("%s%s%s", sub1, (char *)matches->data, sub2);
4592 webkit_dom_node_set_node_value(container, modified, NULL);
4593 gtk_webview_set_caret(GTK_WEBVIEW(gtkconv->entry), container,
4594 word_start + g_utf8_strlen(matches->data, -1) + colon);
4595 g_free(modified);
4596 }
4559 4597
4560 g_free(matches->data); 4598 g_free(matches->data);
4561 g_list_free(matches); 4599 g_list_free(matches);
4562 } 4600 }
4563 else { 4601 else {
4575 matches = g_list_remove(matches, matches->data); 4613 matches = g_list_remove(matches, matches->data);
4576 } 4614 }
4577 4615
4578 purple_conversation_write(conv, NULL, addthis, PURPLE_MESSAGE_NO_LOG, 4616 purple_conversation_write(conv, NULL, addthis, PURPLE_MESSAGE_NO_LOG,
4579 time(NULL)); 4617 time(NULL));
4580 gtk_text_buffer_insert_at_cursor(gtkconv->entry_buffer, partial, -1); 4618
4619 modified = g_strdup_printf("%s%s%s", sub1, partial, sub2);
4620 webkit_dom_node_set_node_value(container, modified, NULL);
4621 gtk_webview_set_caret(GTK_WEBVIEW(gtkconv->entry), container,
4622 word_start + g_utf8_strlen(partial, -1) + colon);
4581 g_free(addthis); 4623 g_free(addthis);
4582 } 4624 g_free(modified);
4583 4625 }
4626
4627 g_free(content);
4584 g_free(entered); 4628 g_free(entered);
4585 g_free(partial); 4629 g_free(partial);
4586 #endif 4630 g_free(sub1);
4631 g_free(sub2);
4632
4587 return TRUE; 4633 return TRUE;
4588 } 4634 }
4589 4635
4590 static void topic_callback(GtkWidget *w, PidginConversation *gtkconv) 4636 static void topic_callback(GtkWidget *w, PidginConversation *gtkconv)
4591 { 4637 {

mercurial