📄 spellchk.c
字号:
/* * Purple - Replace certain misspelled words with their correct form. * * Signification changes were made by Benjamin Kahn ("xkahn") and * Richard Laager ("rlaager") in April 2005--you may want to contact * them if you have questions. * * Pidgin is the legal property of its developers, whose names are too numerous * to list here. Please refer to the COPYRIGHT file distributed with this * source distribution. * * 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 * *//* * A lot of this code (especially the config code) was taken directly * or nearly directly from xchat, version 1.4.2 by Peter Zelezny and others. */#include "internal.h"#include "pidgin.h"#include "debug.h"#include "notify.h"#include "signals.h"#include "util.h"#include "version.h"#include "gtkplugin.h"#include "gtkprefs.h"#include "gtkutils.h"#include <stdio.h>#include <string.h>#ifndef _WIN32#include <strings.h>#endif#include <sys/types.h>#include <sys/stat.h>#define SPELLCHECK_PLUGIN_ID "gtk-spellcheck"#define SPELLCHK_OBJECT_KEY "spellchk"enum { BAD_COLUMN, GOOD_COLUMN, WORD_ONLY_COLUMN, CASE_SENSITIVE_COLUMN, N_COLUMNS};struct _spellchk { GtkTextView *view; GtkTextMark *mark_insert_start; GtkTextMark *mark_insert_end; const gchar *word; gboolean inserting; gboolean ignore_correction; gboolean ignore_correction_on_send; gint pos;};typedef struct _spellchk spellchk;static GtkListStore *model;static gbooleanis_word_uppercase(const gchar *word){ for (; word[0] != '\0'; word = g_utf8_find_next_char (word, NULL)) { gunichar c = g_utf8_get_char(word); if (!(g_unichar_isupper(c) || g_unichar_ispunct(c) || g_unichar_isspace(c))) return FALSE; } return TRUE;}static gbooleanis_word_lowercase(const gchar *word){ for (; word[0] != '\0'; word = g_utf8_find_next_char(word, NULL)) { gunichar c = g_utf8_get_char(word); if (!(g_unichar_islower(c) || g_unichar_ispunct(c) || g_unichar_isspace(c))) return FALSE; } return TRUE;}static gbooleanis_word_proper(const gchar *word){ if (word[0] == '\0') return FALSE; if (!g_unichar_isupper(g_utf8_get_char_validated(word, -1))) return FALSE; return is_word_lowercase(g_utf8_offset_to_pointer(word, 1));}static gchar *make_word_proper(const gchar *word){ char buf[7]; gchar *lower = g_utf8_strdown(word, -1); gint bytes; gchar *ret; bytes = g_unichar_to_utf8(g_unichar_toupper(g_utf8_get_char(word)), buf); buf[MIN(bytes, sizeof(buf) - 1)] = '\0'; ret = g_strconcat(buf, g_utf8_offset_to_pointer(lower, 1), NULL); g_free(lower); return ret;}static gbooleansubstitute_simple_buffer(GtkTextBuffer *buffer){ GtkTextIter start; GtkTextIter end; GtkTreeIter treeiter; gchar *text = NULL; gtk_text_buffer_get_iter_at_offset(buffer, &start, 0); gtk_text_buffer_get_iter_at_offset(buffer, &end, 0); gtk_text_iter_forward_to_end(&end); text = gtk_text_buffer_get_text(buffer, &start, &end, FALSE); if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &treeiter) && text) { do { GValue val1; const gchar *bad; gchar *cursor; glong char_pos; val1.g_type = 0; gtk_tree_model_get_value(GTK_TREE_MODEL(model), &treeiter, WORD_ONLY_COLUMN, &val1); if (g_value_get_boolean(&val1)) { g_value_unset(&val1); continue; } g_value_unset(&val1); gtk_tree_model_get_value(GTK_TREE_MODEL(model), &treeiter, BAD_COLUMN, &val1); bad = g_value_get_string(&val1); /* using g_utf8_* to get /character/ offsets instead of byte offsets for buffer */ if ((cursor = g_strrstr(text, bad))) { GValue val2; const gchar *good; val2.g_type = 0; gtk_tree_model_get_value(GTK_TREE_MODEL(model), &treeiter, GOOD_COLUMN, &val2); good = g_value_get_string(&val2); char_pos = g_utf8_pointer_to_offset(text, cursor); gtk_text_buffer_get_iter_at_offset(buffer, &start, char_pos); gtk_text_buffer_get_iter_at_offset(buffer, &end, char_pos + g_utf8_strlen(bad, -1)); gtk_text_buffer_delete(buffer, &start, &end); gtk_text_buffer_get_iter_at_offset(buffer, &start, char_pos); gtk_text_buffer_insert(buffer, &start, good, -1); g_value_unset(&val2); g_free(text); g_value_unset(&val1); return TRUE; } g_value_unset(&val1); } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &treeiter)); } g_free(text); return FALSE;}static gchar *substitute_word(gchar *word){ GtkTreeIter iter; gchar *outword; gchar *lowerword; gchar *foldedword; if (word == NULL) return NULL; lowerword = g_utf8_strdown(word, -1); foldedword = g_utf8_casefold(word, -1); if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter)) { do { GValue val1; gboolean case_sensitive; const char *bad; gchar *tmpbad = NULL; val1.g_type = 0; gtk_tree_model_get_value(GTK_TREE_MODEL(model), &iter, WORD_ONLY_COLUMN, &val1); if (!g_value_get_boolean(&val1)) { g_value_unset(&val1); continue; } g_value_unset(&val1); gtk_tree_model_get_value(GTK_TREE_MODEL(model), &iter, CASE_SENSITIVE_COLUMN, &val1); case_sensitive = g_value_get_boolean(&val1); g_value_unset(&val1); gtk_tree_model_get_value(GTK_TREE_MODEL(model), &iter, BAD_COLUMN, &val1); bad = g_value_get_string(&val1); if ((case_sensitive && !strcmp(bad, word)) || (!case_sensitive && (!strcmp(bad, lowerword) || (!is_word_lowercase(bad) && !strcmp((tmpbad = g_utf8_casefold(bad, -1)), foldedword))))) { GValue val2; const char *good; g_free(tmpbad); val2.g_type = 0; gtk_tree_model_get_value(GTK_TREE_MODEL(model), &iter, GOOD_COLUMN, &val2); good = g_value_get_string(&val2); if (!case_sensitive && is_word_lowercase(bad) && is_word_lowercase(good)) { if (is_word_uppercase(word)) outword = g_utf8_strup(good, -1); else if (is_word_proper(word)) outword = make_word_proper(good); else outword = g_strdup(good); } else outword = g_strdup(good); g_value_unset(&val1); g_value_unset(&val2); g_free(foldedword); return outword; } g_value_unset(&val1); g_free(tmpbad); } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &iter)); } g_free(foldedword); return NULL;}static voidspellchk_free(spellchk *spell){ GtkTextBuffer *buffer; g_return_if_fail(spell != NULL); buffer = gtk_text_view_get_buffer(spell->view); g_signal_handlers_disconnect_matched(buffer, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, spell); g_free(spell);}/* Pango doesn't know about the "'" character. Let's fix that. */static gbooleanspellchk_inside_word(GtkTextIter *iter){ gunichar ucs4_char = gtk_text_iter_get_char(iter); gchar *utf8_str; gchar c = 0; utf8_str = g_ucs4_to_utf8(&ucs4_char, 1, NULL, NULL, NULL); if (utf8_str != NULL) { c = utf8_str[0]; g_free(utf8_str); } /* Hack because otherwise typing things like U.S. gets difficult * if you have 'u' -> 'you' set as a correction... * * Part 1 of 2: This marks . as being an inside-word character. */ if (c == '.') return TRUE; /* Avoid problems with \r, for example (SF #1289031). */ if (c == '\\') return TRUE; if (gtk_text_iter_inside_word (iter) == TRUE) return TRUE; if (c == '\'') { gboolean result = gtk_text_iter_backward_char(iter); gboolean output = gtk_text_iter_inside_word(iter); if (result) { /* * Hack so that "u'll" will correct correctly. */ ucs4_char = gtk_text_iter_get_char(iter); utf8_str = g_ucs4_to_utf8(&ucs4_char, 1, NULL, NULL, NULL); if (utf8_str != NULL) { c = utf8_str[0]; g_free(utf8_str); if (c == 'u' || c == 'U') { gtk_text_iter_forward_char(iter); return FALSE; } } gtk_text_iter_forward_char(iter); } return output; } else if (c == '&') return TRUE; return FALSE;}static gbooleanspellchk_backward_word_start(GtkTextIter *iter){ int output; int result; output = gtk_text_iter_backward_word_start(iter); /* It didn't work... */ if (!output) return FALSE; while (spellchk_inside_word(iter)) { result = gtk_text_iter_backward_char(iter); /* We can't go backwards anymore? We're at the beginning of the word. */ if (!result) return TRUE; if (!spellchk_inside_word(iter)) { gtk_text_iter_forward_char(iter); return TRUE; } output = gtk_text_iter_backward_word_start(iter); if (!output) return FALSE; } return TRUE;}static gbooleancheck_range(spellchk *spell, GtkTextBuffer *buffer, GtkTextIter start, GtkTextIter end, gboolean sending){ gboolean replaced; gboolean result; gchar *tmp; int period_count = 0; gchar *word; GtkTextMark *mark; GtkTextIter pos; if ((replaced = substitute_simple_buffer(buffer))) { mark = gtk_text_buffer_get_insert(buffer); gtk_text_buffer_get_iter_at_mark(buffer, &pos, mark); spell->pos = gtk_text_iter_get_offset(&pos); gtk_text_buffer_get_iter_at_mark(buffer, &start, mark); gtk_text_buffer_get_iter_at_mark(buffer, &end, mark); } if (!sending) { /* We need to go backwords to find out if we are inside a word or not. */ gtk_text_iter_backward_char(&end); if (spellchk_inside_word(&end)) { gtk_text_iter_forward_char(&end); return replaced; /* We only pay attention to whole words. */ } } /* We could be in the middle of a whitespace block. Check for that. */ result = gtk_text_iter_backward_char(&end); if (!spellchk_inside_word(&end)) { if (result) gtk_text_iter_forward_char(&end); return replaced;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -