diff options
Diffstat (limited to 'misc/stardict/patches/fixup_build_on_gcc44.patch')
-rw-r--r-- | misc/stardict/patches/fixup_build_on_gcc44.patch | 8728 |
1 files changed, 8728 insertions, 0 deletions
diff --git a/misc/stardict/patches/fixup_build_on_gcc44.patch b/misc/stardict/patches/fixup_build_on_gcc44.patch new file mode 100644 index 0000000000..9653171976 --- /dev/null +++ b/misc/stardict/patches/fixup_build_on_gcc44.patch @@ -0,0 +1,8728 @@ +diff -Nur stardict-3.0.1.orig//src/gconf_file.cpp stardict-3.0.1/src/gconf_file.cpp +--- stardict-3.0.1.orig//src/gconf_file.cpp 2007-07-10 02:16:04.000000000 -0500 ++++ stardict-3.0.1/src/gconf_file.cpp 2010-05-24 00:53:36.371667435 -0500 +@@ -22,6 +22,8 @@ + # include "config.h" + #endif + ++#include <memory> ++ + #include <glib/gi18n.h> + + #include "gconf_file.hpp" +diff -Nur stardict-3.0.1.orig//src/gconf_file.cpp~ stardict-3.0.1/src/gconf_file.cpp~ +--- stardict-3.0.1.orig//src/gconf_file.cpp~ 1969-12-31 18:00:00.000000000 -0600 ++++ stardict-3.0.1/src/gconf_file.cpp~ 2007-07-10 02:16:04.000000000 -0500 +@@ -0,0 +1,224 @@ ++/* ++ * This file part of StarDict - A international dictionary for GNOME. ++ * http://stardict.sourceforge.net ++ * Copyright (C) 2005-2006 Evgeniy <dushistov@mail.ru> ++ * ++ * 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 3 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 Library 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. ++ */ ++ ++#ifdef HAVE_CONFIG_H ++# include "config.h" ++#endif ++ ++#include <glib/gi18n.h> ++ ++#include "gconf_file.hpp" ++ ++gconf_file::gconf_file(const std::string& path) ++{ ++ cfgname=path; ++ if ((gconf_client = gconf_client_get_default())==NULL) ++ g_warning("Cannot connect to gconf.\n"); ++ else ++ gconf_client_add_dir(gconf_client, cfgname.c_str(), GCONF_CLIENT_PRELOAD_RECURSIVE, NULL); ++} ++ ++gconf_file::~gconf_file() ++{ ++ for (std::vector<guint>::iterator it=notification_ids.begin(); ++ it!=notification_ids.end(); ++it) ++ gconf_client_notify_remove(gconf_client, *it); ++ ++ if (!gconf_client) ++ return; ++ gconf_client_remove_dir(gconf_client, cfgname.c_str(), NULL); ++ g_object_unref(gconf_client); ++} ++ ++bool gconf_file::read_bool(const gchar *sect, const gchar *key, bool& val) ++{ ++ if (!gconf_client) ++ return false; ++ ++ std::string real_key(std::string(sect)+"/"+key); ++ ++ GConfValue *gval=gconf_client_get(gconf_client, real_key.c_str(), NULL); ++ if (!gval) ++ return false; ++ val=gconf_value_get_bool(gval); ++ gconf_value_free(gval); ++ ++ return true; ++} ++ ++bool gconf_file::read_int(const gchar *sect, const gchar *key, int& val) ++{ ++ if (!gconf_client) ++ return false; ++ std::string real_key(std::string(sect)+"/"+key); ++ GConfValue *gval=gconf_client_get(gconf_client, real_key.c_str(), NULL); ++ if (!gval) ++ return false; ++ val=gconf_value_get_int(gval); ++ gconf_value_free(gval); ++ ++ return true; ++} ++ ++bool gconf_file::read_string(const gchar * sect, const gchar *key, std::string& val) ++{ ++ if (!gconf_client) ++ return false; ++ ++ std::string real_key(std::string(sect)+"/"+key); ++ gchar *gconf_val = gconf_client_get_string(gconf_client, real_key.c_str(), NULL); ++ if (gconf_val!=NULL) ++ val=gconf_val; ++ ++ g_free(gconf_val); ++ ++ return true; ++} ++ ++bool gconf_file::read_strlist(const gchar * sect, const gchar * key, std::list<std::string>& slist) ++{ ++ if (!gconf_client) ++ return false; ++ ++ std::string real_key(std::string(sect)+"/"+key); ++ GSList *gslist = gconf_client_get_list(gconf_client, real_key.c_str(), GCONF_VALUE_STRING, NULL); ++ if (!gslist) ++ return false; ++ ++ slist.clear(); ++ GSList *p = gslist; ++ while (p) { ++ slist.push_back(static_cast<char *>(p->data)); ++ g_free(p->data); ++ p=g_slist_next(p); ++ } ++ g_slist_free(gslist); ++ ++ return true; ++} ++ ++void gconf_file::write_bool(const gchar *sect, const gchar *key, bool val) ++{ ++ if (!gconf_client) ++ return; ++ gchar *real_key=g_strdup_printf("%s/%s", sect, key); ++ gconf_client_set_bool(gconf_client, real_key, val, NULL); ++ g_free(real_key); ++} ++ ++void gconf_file::write_int(const gchar *sect, const gchar *key, int val) ++{ ++ if (!gconf_client) ++ return; ++ gchar *real_key=g_strdup_printf("%s/%s", sect, key); ++ gconf_client_set_int(gconf_client, real_key, val, NULL); ++ g_free(real_key); ++} ++ ++void gconf_file::write_string(const gchar *sect, const gchar *key, const std::string& val) ++{ ++ if(!gconf_client) ++ return; ++ gchar *real_key=g_strdup_printf("%s/%s", sect, key); ++ gconf_client_set_string(gconf_client, real_key, val.c_str(), NULL); ++ g_free(real_key); ++} ++ ++void gconf_file::write_strlist(const gchar *sect, const gchar *key, const std::list<std::string>& slist) ++{ ++ if (!gconf_client) ++ return; ++ ++ GSList *gslist = NULL; ++ for (std::list<std::string>::const_iterator p = slist.begin(); ++ p!=slist.end(); ++p) ++ gslist = g_slist_append(gslist, const_cast<char *>(p->c_str())); ++ ++ gchar *real_key=g_strdup_printf("%s/%s", sect, key); ++ gconf_client_set_list(gconf_client, real_key, GCONF_VALUE_STRING, gslist, NULL); ++ g_free(real_key); ++ g_slist_free(gslist); ++} ++ ++static void gconf_client_notify_func(GConfClient *client, guint cnxn_id, ++ GConfEntry *entry, gpointer user_data) ++{ ++ sigc::signal<void, const baseconfval*> *ch = ++ static_cast< sigc::signal<void, const baseconfval*> *>(user_data); ++ std::auto_ptr<baseconfval> cv; ++ switch (entry->value->type) { ++ case GCONF_VALUE_BOOL: ++ cv.reset(new confval<bool>); ++ static_cast<confval<bool> *>(cv.get())->val_ = ++ gconf_value_get_bool(entry->value); ++ break; ++ case GCONF_VALUE_INT: ++ cv.reset(new confval<int>); ++ static_cast<confval<int> *>(cv.get())->val_ = ++ gconf_value_get_int(entry->value); ++ break; ++ case GCONF_VALUE_STRING: { ++ cv.reset(new confval<std::string>); ++ const gchar *gconf_val = gconf_value_get_string(entry->value); ++ if (gconf_val) ++ static_cast<confval<std::string> *>(cv.get())->val_ = ++ gconf_val; ++ } ++ case GCONF_VALUE_LIST: { ++ confval<std::list<std::string> > *newval = ++ new confval<std::list<std::string> >; ++ cv.reset(newval); ++ GSList *gslist = gconf_value_get_list(entry->value); ++ ++ ++ GSList *p = gslist; ++ while (p) { ++ newval->val_.push_back(static_cast<char *>(p->data)); ++ p = g_slist_next(p); ++ } ++ ++ } ++ break; ++ default: ++ g_assert(false); ++ return; ++ } ++ ch->emit(cv.get()); ++} ++ ++static void gfree_func(gpointer data) ++{ ++ sigc::signal<void, const baseconfval*> *bcv = ++ static_cast< sigc::signal<void, const baseconfval*> *>(data); ++ delete bcv; ++} ++ ++void gconf_file::notify_add(const gchar *sect, const gchar *key, ++ const sigc::slot<void, const baseconfval*>& slot) ++{ ++ std::string name = std::string(sect) + "/" + key; ++ sigc::signal<void, const baseconfval*> *ch = ++ new sigc::signal<void, const baseconfval*>; ++ ch->connect(slot); ++ guint id = gconf_client_notify_add(gconf_client, name.c_str(), ++ gconf_client_notify_func, ch, ++ gfree_func, NULL); ++ notification_ids.push_back(id); ++} +diff -Nur stardict-3.0.1.orig//src/inifile.cpp stardict-3.0.1/src/inifile.cpp +--- stardict-3.0.1.orig//src/inifile.cpp 2007-08-01 04:43:00.000000000 -0500 ++++ stardict-3.0.1/src/inifile.cpp 2010-05-24 00:53:36.372667591 -0500 +@@ -25,6 +25,7 @@ + #include <vector> + #include <cerrno> + #include <cstring> ++#include <cstdlib> + #include <cstdio> + #include <glib/gi18n.h> + #include <glib/gstdio.h> +diff -Nur stardict-3.0.1.orig//src/inifile.cpp~ stardict-3.0.1/src/inifile.cpp~ +--- stardict-3.0.1.orig//src/inifile.cpp~ 1969-12-31 18:00:00.000000000 -0600 ++++ stardict-3.0.1/src/inifile.cpp~ 2007-08-01 04:43:00.000000000 -0500 +@@ -0,0 +1,272 @@ ++/* ++ * This file part of StarDict - A international dictionary for GNOME. ++ * http://stardict.sourceforge.net ++ * Copyright (C) 2005-2006 Evgeniy <dushistov@mail.ru> ++ * ++ * 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 3 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 Library 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. ++ */ ++ ++#ifdef HAVE_CONFIG_H ++# include "config.h" ++#endif ++ ++#include <vector> ++#include <cerrno> ++#include <cstring> ++#include <cstdio> ++#include <glib/gi18n.h> ++#include <glib/gstdio.h> ++ ++#include "utils.h" ++ ++#include "inifile.hpp" ++ ++static const guchar NEW_STRING_SEP = 1; ++static const guchar OLD_STRING_SEP = 0xFF; ++static const gchar *myversion = "1.0"; ++ ++typedef ResourceWrapper<GError, GError, g_error_free> MyGError; ++ ++void inifile::create_empty() ++{ ++ g_key_file_set_string(gkeyfile_, "stardict-private", "version", ++ myversion); ++ save(); ++ g_key_file_free(gkeyfile_); ++} ++ ++void inifile::convert_from_locale_enc() ++{ ++ MyGError err; ++ glib::CharStr data; ++ ++ if (!g_file_get_contents(fname_.c_str(), get_addr(data), NULL, ++ get_addr(err))) { ++ g_error(("Can not read %s, reason %s\n"), fname_.c_str(), ++ err->message); ++ exit(EXIT_SUCCESS); ++ } ++ ++ glib::CharStr utfdata(g_locale_to_utf8(get_impl(data), -1, NULL, NULL, ++ NULL)); ++ if (!utfdata) { ++ g_error(("Can not convert ini file content to current locale\n")); ++ exit(EXIT_SUCCESS); ++ } ++ ++ if (!g_file_set_contents(fname_.c_str(), get_impl(utfdata), -1, get_addr(err))) { ++ g_error("can not save content of ini file %s, reason %s\n", ++ fname_.c_str(), err->message); ++ exit(EXIT_SUCCESS); ++ } ++} ++ ++inifile::inifile(const std::string& path) ++{ ++ fname_ = path; ++ bool done = false; ++ while (!done) { ++ gkeyfile_ = g_key_file_new(); ++ g_key_file_set_list_separator(gkeyfile_, NEW_STRING_SEP); ++/* create file if not exist, because of g_key_file can not do that */ ++ if (!g_file_test(path.c_str(), ++ GFileTest(G_FILE_TEST_EXISTS | ++ G_FILE_TEST_IS_REGULAR))) { ++ create_empty(); ++ continue; ++ } ++ ++ MyGError err; ++ if (!g_key_file_load_from_file(gkeyfile_, path.c_str(), ++ GKeyFileFlags(G_KEY_FILE_KEEP_COMMENTS | ++ G_KEY_FILE_KEEP_TRANSLATIONS), ++ get_addr(err))) { ++ if (err->code == G_KEY_FILE_ERROR_UNKNOWN_ENCODING) { ++ g_key_file_free(gkeyfile_); ++ convert_from_locale_enc(); ++ continue; ++ } ++ g_error(("Can not open config file: %s, reason: %s\n"), ++ path.c_str(), err->message); ++ exit(EXIT_FAILURE);//just in case ++ } ++ ++ glib::CharStr version(g_key_file_get_string(gkeyfile_, "stardict-private", ++ "version", get_addr(err))); ++ if (err) { ++ if (err->code != G_KEY_FILE_ERROR_KEY_NOT_FOUND && ++ err->code != G_KEY_FILE_ERROR_GROUP_NOT_FOUND) { ++ g_error(("internal error, reason: %s\n"), ++ err->message); ++ exit(EXIT_FAILURE);//just in case ++ } ++ create_empty(); ++ continue; ++ } ++ if (strcmp(get_impl(version), myversion)) { ++ g_error(("unsupported ini file format\n")); ++ exit(EXIT_FAILURE); ++ } ++ done = true; ++ } ++ ++} ++ ++void inifile::save() ++{ ++ gsize len; ++ MyGError err; ++ glib::CharStr data( ++ g_key_file_to_data(gkeyfile_, &len, get_addr(err))); ++ ++ if (err) { ++ g_warning(("internal error, reason: %s\n"), ++ err->message); ++ return; ++ } ++ FILE *f = g_fopen(fname_.c_str(), "w"); ++ if (!f) { ++ g_warning(("can not open file: %s\n"), ++ fname_.c_str()); ++ return; ++ } ++ size_t writeb = fwrite(get_impl(data), 1, len, f); ++ fclose(f); ++ if (writeb < len) ++ g_warning(("write to %s failed, instead of %lu," ++ " we wrote %lu\n"), fname_.c_str(), gulong(len), gulong(writeb)); ++} ++ ++inifile::~inifile() ++{ ++ save(); ++ g_key_file_free(gkeyfile_); ++} ++ ++static bool report_error(GError *err, const gchar *sect, const gchar *key) ++{ ++ bool res = false; ++ if (err->code == G_KEY_FILE_ERROR_KEY_NOT_FOUND || ++ err->code == G_KEY_FILE_ERROR_GROUP_NOT_FOUND) ++ res = true; ++ else ++ g_warning("Can not read %s/%s value," ++ " reason %s\n", sect, key, err->message); ++ g_error_free(err); ++ return res; ++} ++ ++bool inifile::read_bool(const gchar *sect, const gchar *key, bool& val) ++{ ++ GError *err = NULL; ++ ++ gboolean newval = g_key_file_get_boolean(gkeyfile_, sect, key, &err); ++ if (err) ++ return report_error(err, sect, key); ++ ++ val = newval; ++ return true; ++} ++ ++bool inifile::read_int(const gchar *sect, const gchar *key, int& val) ++{ ++ GError *err = NULL; ++ gint newval = g_key_file_get_integer(gkeyfile_, sect, key, &err); ++ if (err) ++ return report_error(err, sect, key); ++ ++ val = newval; ++ return true; ++} ++ ++bool inifile::read_string(const gchar * sect, const gchar *key, std::string& val) ++{ ++ GError *err = NULL; ++ gchar *newval = g_key_file_get_string(gkeyfile_, sect, key, &err); ++ if (err) { ++ g_free(newval); ++ return report_error(err, sect, key); ++ } ++ val = newval; ++ g_free(newval); ++ return true; ++} ++ ++bool inifile::read_strlist(const gchar *sect, const gchar * key, ++ std::list<std::string>& slist) ++{ ++ GError *err = NULL; ++ gchar **newval = g_key_file_get_string_list(gkeyfile_, sect, key, ++ NULL, &err); ++ if (err) { ++ g_strfreev(newval); ++ return report_error(err, sect, key); ++ } ++ slist.clear(); ++ gchar **p = newval; ++ while (*p) { ++ slist.push_back(*p); ++ ++p; ++ } ++ g_strfreev(newval); ++ return true; ++} ++ ++void inifile::write_bool(const gchar *sect, const gchar *key, bool val) ++{ ++ g_key_file_set_boolean(gkeyfile_, sect, key, val); ++ expose_event(sect, key, val); ++} ++ ++void inifile::write_int(const gchar *sect, const gchar *key, int val) ++{ ++ g_key_file_set_integer(gkeyfile_, sect, key, val); ++ expose_event(sect, key, val); ++} ++ ++void inifile::write_string(const gchar *sect, const gchar *key, ++ const std::string& val) ++{ ++ g_key_file_set_string(gkeyfile_, sect, key, val.c_str()); ++ expose_event(sect, key, val); ++} ++ ++void inifile::write_strlist(const gchar *sect, const gchar *key, ++ const std::list<std::string>& slist) ++{ ++ size_t len = slist.size(); ++ std::vector<const gchar *> glib_list(len + 1); ++ ++ std::list<std::string>::const_iterator it; ++ size_t i; ++ ++ for (it = slist.begin(), i = 0; it != slist.end(); ++it, ++i) ++ glib_list[i] = it->c_str(); ++ glib_list[i] = NULL; ++ g_key_file_set_string_list(gkeyfile_, sect, key, &glib_list[0], len); ++ expose_event(sect, key, slist); ++} ++ ++void inifile::notify_add(const gchar *sect, const gchar *key, ++ const sigc::slot<void, const baseconfval*>& slot) ++{ ++ std::string name = std::string(sect) + "/" + key; ++ ++ ChangeEventsMap::iterator it = ++ change_events_map_.insert( ++ std::make_pair(name, ++ sigc::signal<void, const baseconfval*>())).first; ++ it->second.connect(slot); ++} +diff -Nur stardict-3.0.1.orig//src/lib/common.cpp stardict-3.0.1/src/lib/common.cpp +--- stardict-3.0.1.orig//src/lib/common.cpp 2007-09-24 21:27:24.000000000 -0500 ++++ stardict-3.0.1/src/lib/common.cpp 2010-05-24 00:53:36.372667591 -0500 +@@ -25,6 +25,8 @@ + #endif + + #include "common.hpp" ++#include <cstring> ++#include <cstdlib> + + static void parse_description(const char *p, long len, std::string &description) + { +diff -Nur stardict-3.0.1.orig//src/lib/common.cpp~ stardict-3.0.1/src/lib/common.cpp~ +--- stardict-3.0.1.orig//src/lib/common.cpp~ 1969-12-31 18:00:00.000000000 -0600 ++++ stardict-3.0.1/src/lib/common.cpp~ 2007-09-24 21:27:24.000000000 -0500 +@@ -0,0 +1,221 @@ ++/* ++ * This file part of StarDict - A international dictionary for GNOME. ++ * http://stardict.sourceforge.net ++ * ++ * 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 3 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 Library 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. ++ */ ++ ++/* ++ * implementation of methods of common for dictionaries structures ++ */ ++#ifdef HAVE_CONFIG_H ++# include "config.h" ++#endif ++ ++#include "common.hpp" ++ ++static void parse_description(const char *p, long len, std::string &description) ++{ ++ description.clear(); ++ const char *p1 = p; ++ while (p1 - p < len) { ++ if (*p1 == '<') { ++ p1++; ++ if ((*p1 == 'b' || *p1 == 'B') && (*(p1+1)=='r' || *(p1+1)=='R') && *(p1+2)=='>') { ++ description += '\n'; ++ p1+=3; ++ } else { ++ description += '<'; ++ } ++ } else { ++ description += *p1; ++ p1++; ++ } ++ } ++} ++ ++//looks not optimal, TODO: refactor ++bool DictInfo::load_from_ifo_file(const std::string& ifofilename, ++ bool istreedict) ++{ ++ ifo_file_name=ifofilename; ++ gchar *buffer; ++ if (!g_file_get_contents(ifofilename.c_str(), &buffer, NULL, NULL)) ++ return false; ++ ++#define TREEDICT_MAGIC_DATA "StarDict's treedict ifo file\nversion=" ++#define DICT_MAGIC_DATA "StarDict's dict ifo file\nversion=" ++ const gchar *magic_data=istreedict ? TREEDICT_MAGIC_DATA : DICT_MAGIC_DATA; ++ if (!g_str_has_prefix(buffer, magic_data)) { ++ g_free(buffer); ++ return false; ++ } ++ ++ bool is_dict_300 = false; ++ gchar *p1; ++ if (istreedict) { ++ p1 = buffer + sizeof(TREEDICT_MAGIC_DATA) -1; ++#define TREEDICT_VERSION_242 "2.4.2\n" ++ if (g_str_has_prefix(p1, TREEDICT_VERSION_242)) { ++ p1 += sizeof(TREEDICT_VERSION_242) -2; ++ } else { ++ g_print("Load %s failed: Unknown version.\n", ifofilename.c_str()); ++ g_free(buffer); ++ return false; ++ } ++ } else { ++ p1 = buffer + sizeof(DICT_MAGIC_DATA) -1; ++#define DICT_VERSION_242 "2.4.2\n" ++#define DICT_VERSION_300 "3.0.0\n" ++ if (g_str_has_prefix(p1, DICT_VERSION_242)) { ++ p1 += sizeof(DICT_VERSION_242) -2; ++ } else if (g_str_has_prefix(p1, DICT_VERSION_300)) { ++ p1 += sizeof(DICT_VERSION_300) -2; ++ is_dict_300 = true; ++ } else { ++ g_print("Load %s failed: Unknown version.\n", ifofilename.c_str()); ++ g_free(buffer); ++ return false; ++ } ++ } ++ ++ gchar *p2,*p3; ++ ++ if (is_dict_300) { ++ p2 = strstr(p1,"\nidxoffsetbits="); ++ if (p2) { ++ p2 = p2 + sizeof("\nidxoffsetbits=") -1; ++ if (g_str_has_prefix(p2, "64\n")) { ++ // TODO ++ g_print("Load %s failed: not supported presently.\n", ifofilename.c_str()); ++ g_free(buffer); ++ return false; ++ } ++ } ++ } ++ ++ p2 = strstr(p1,"\nwordcount="); ++ if (!p2) { ++ g_free(buffer); ++ return false; ++ } ++ ++ p3 = strchr(p2+ sizeof("\nwordcount=")-1,'\n'); ++ gchar *tmpstr = (gchar *)g_memdup(p2+sizeof("\nwordcount=")-1, p3-(p2+sizeof("\nwordcount=")-1)+1); ++ tmpstr[p3-(p2+sizeof("\nwordcount=")-1)] = '\0'; ++ wordcount = atol(tmpstr); ++ g_free(tmpstr); ++ ++ p2 = strstr(p1,"\nsynwordcount="); ++ if (p2) { ++ p3 = strchr(p2+ sizeof("\nsynwordcount=")-1,'\n'); ++ gchar *tmpstr = (gchar *)g_memdup(p2+sizeof("\nsynwordcount=")-1, p3-(p2+sizeof("\nsynwordcount=")-1)+1); ++ tmpstr[p3-(p2+sizeof("\nsynwordcount=")-1)] = '\0'; ++ synwordcount = atol(tmpstr); ++ g_free(tmpstr); ++ } else { ++ synwordcount = 0; ++ } ++ ++ if (istreedict) { ++ p2 = strstr(p1,"\ntdxfilesize="); ++ if (!p2) { ++ g_free(buffer); ++ return false; ++ } ++ p3 = strchr(p2+ sizeof("\ntdxfilesize=")-1,'\n'); ++ tmpstr = (gchar *)g_memdup(p2+sizeof("\ntdxfilesize=")-1, p3-(p2+sizeof("\ntdxfilesize=")-1)+1); ++ tmpstr[p3-(p2+sizeof("\ntdxfilesize=")-1)] = '\0'; ++ index_file_size = atol(tmpstr); ++ g_free(tmpstr); ++ } else { ++ p2 = strstr(p1,"\nidxfilesize="); ++ if (!p2) { ++ g_free(buffer); ++ return false; ++ } ++ ++ p3 = strchr(p2+ sizeof("\nidxfilesize=")-1,'\n'); ++ tmpstr = (gchar *)g_memdup(p2+sizeof("\nidxfilesize=")-1, p3-(p2+sizeof("\nidxfilesize=")-1)+1); ++ tmpstr[p3-(p2+sizeof("\nidxfilesize=")-1)] = '\0'; ++ index_file_size = atol(tmpstr); ++ g_free(tmpstr); ++ ++ p2 = strstr(p1,"\ndicttype="); ++ if (p2) { ++ p2+=sizeof("\ndicttype=")-1; ++ p3 = strchr(p2, '\n'); ++ dicttype.assign(p2, p3-p2); ++ } ++ } ++ ++ p2 = strstr(p1,"\nbookname="); ++ ++ if (!p2) { ++ g_free(buffer); ++ return false; ++ } ++ ++ p2 = p2 + sizeof("\nbookname=") -1; ++ p3 = strchr(p2, '\n'); ++ bookname.assign(p2, p3-p2); ++ ++ p2 = strstr(p1,"\nauthor="); ++ if (p2) { ++ p2 = p2 + sizeof("\nauthor=") -1; ++ p3 = strchr(p2, '\n'); ++ author.assign(p2, p3-p2); ++ } ++ ++ p2 = strstr(p1,"\nemail="); ++ if (p2) { ++ p2 = p2 + sizeof("\nemail=") -1; ++ p3 = strchr(p2, '\n'); ++ email.assign(p2, p3-p2); ++ } ++ ++ p2 = strstr(p1,"\nwebsite="); ++ if (p2) { ++ p2 = p2 + sizeof("\nwebsite=") -1; ++ p3 = strchr(p2, '\n'); ++ website.assign(p2, p3-p2); ++ } ++ ++ p2 = strstr(p1,"\ndate="); ++ if (p2) { ++ p2 = p2 + sizeof("\ndate=") -1; ++ p3 = strchr(p2, '\n'); ++ date.assign(p2, p3-p2); ++ } ++ ++ p2 = strstr(p1,"\ndescription="); ++ if (p2) { ++ p2 = p2 + sizeof("\ndescription=")-1; ++ p3 = strchr(p2, '\n'); ++ parse_description(p2, p3-p2, description); ++ } ++ ++ p2 = strstr(p1,"\nsametypesequence="); ++ if (p2) { ++ p2+=sizeof("\nsametypesequence=")-1; ++ p3 = strchr(p2, '\n'); ++ sametypesequence.assign(p2, p3-p2); ++ } ++ ++ g_free(buffer); ++ ++ return true; ++} ++ +diff -Nur stardict-3.0.1.orig//src/lib/data.cpp stardict-3.0.1/src/lib/data.cpp +--- stardict-3.0.1.orig//src/lib/data.cpp 2007-09-20 20:09:52.000000000 -0500 ++++ stardict-3.0.1/src/lib/data.cpp 2010-05-24 00:54:04.762542528 -0500 +@@ -26,7 +26,8 @@ + + #include "data.hpp" + #include "getuint32.h" +- ++#include <stdio.h> ++#include <cstring> + + DictBase::DictBase() + { +diff -Nur stardict-3.0.1.orig//src/lib/data.cpp~ stardict-3.0.1/src/lib/data.cpp~ +--- stardict-3.0.1.orig//src/lib/data.cpp~ 1969-12-31 18:00:00.000000000 -0600 ++++ stardict-3.0.1/src/lib/data.cpp~ 2007-09-20 20:09:52.000000000 -0500 +@@ -0,0 +1,298 @@ ++/* ++ * This file part of StarDict - A international dictionary for GNOME. ++ * http://stardict.sourceforge.net ++ * ++ * 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 3 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 Library 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. ++ */ ++ ++/* implementation of class to work with dictionary data */ ++#ifdef HAVE_CONFIG_H ++# include "config.h" ++#endif ++ ++#include "kmp.h" ++ ++#include "data.hpp" ++#include "getuint32.h" ++ ++ ++DictBase::DictBase() ++{ ++ dictfile = NULL; ++ cache_cur =0; ++} ++ ++DictBase::~DictBase() ++{ ++ if (dictfile) ++ fclose(dictfile); ++} ++ ++gchar* DictBase::GetWordData(guint32 idxitem_offset, guint32 idxitem_size) ++{ ++ for (int i=0; i<WORDDATA_CACHE_NUM; i++) ++ if (cache[i].data && cache[i].offset == idxitem_offset) ++ return cache[i].data; ++ ++ if (dictfile) ++ fseek(dictfile, idxitem_offset, SEEK_SET); ++ ++ gchar *data; ++ if (!sametypesequence.empty()) { ++ gchar *origin_data = (gchar *)g_malloc(idxitem_size); ++ ++ if (dictfile) ++ fread(origin_data, idxitem_size, 1, dictfile); ++ else ++ dictdzfile->read(origin_data, idxitem_offset, idxitem_size); ++ ++ guint32 data_size; ++ gint sametypesequence_len = sametypesequence.length(); ++ //there have sametypesequence_len char being omitted. ++ data_size = idxitem_size + sametypesequence_len; //Here is a bug fix of 2.4.8, which don't add sizeof(guint32) anymore. ++ ++ //if the last item's size is determined by the end up '\0',then +=sizeof(gchar); ++ //if the last item's size is determined by the head guint32 type data,then +=sizeof(guint32); ++ switch (sametypesequence[sametypesequence_len-1]) { ++ case 'm': ++ case 't': ++ case 'y': ++ case 'l': ++ case 'g': ++ case 'x': ++ case 'k': ++ case 'w': ++ data_size += sizeof(gchar); ++ break; ++ case 'W': ++ case 'P': ++ data_size += sizeof(guint32); ++ break; ++ default: ++ if (g_ascii_isupper(sametypesequence[sametypesequence_len-1])) ++ data_size += sizeof(guint32); ++ else ++ data_size += sizeof(gchar); ++ break; ++ } ++ data = (gchar *)g_malloc(data_size + sizeof(guint32)); ++ gchar *p1,*p2; ++ p1 = data + sizeof(guint32); ++ p2 = origin_data; ++ guint32 sec_size; ++ //copy the head items. ++ for (int i=0; i<sametypesequence_len-1; i++) { ++ *p1=sametypesequence[i]; ++ p1+=sizeof(gchar); ++ switch (sametypesequence[i]) { ++ case 'm': ++ case 't': ++ case 'y': ++ case 'l': ++ case 'g': ++ case 'x': ++ case 'k': ++ case 'w': ++ sec_size = strlen(p2)+1; ++ memcpy(p1, p2, sec_size); ++ p1+=sec_size; ++ p2+=sec_size; ++ break; ++ case 'W': ++ case 'P': ++ sec_size = get_uint32(p2); ++ sec_size += sizeof(guint32); ++ memcpy(p1, p2, sec_size); ++ p1+=sec_size; ++ p2+=sec_size; ++ break; ++ default: ++ if (g_ascii_isupper(sametypesequence[i])) { ++ sec_size = get_uint32(p2); ++ sec_size += sizeof(guint32); ++ } else { ++ sec_size = strlen(p2)+1; ++ } ++ memcpy(p1, p2, sec_size); ++ p1+=sec_size; ++ p2+=sec_size; ++ break; ++ } ++ } ++ //calculate the last item 's size. ++ sec_size = idxitem_size - (p2-origin_data); ++ *p1=sametypesequence[sametypesequence_len-1]; ++ p1+=sizeof(gchar); ++ switch (sametypesequence[sametypesequence_len-1]) { ++ case 'm': ++ case 't': ++ case 'y': ++ case 'l': ++ case 'g': ++ case 'x': ++ case 'k': ++ case 'w': ++ memcpy(p1, p2, sec_size); ++ p1 += sec_size; ++ *p1='\0';//add the end up '\0'; ++ break; ++ case 'W': ++ case 'P': ++ memcpy(p1, &sec_size, sizeof(guint32)); ++ p1 += sizeof(guint32); ++ memcpy(p1, p2, sec_size); ++ break; ++ default: ++ if (g_ascii_isupper(sametypesequence[sametypesequence_len-1])) { ++ memcpy(p1, &sec_size, sizeof(guint32)); ++ p1 += sizeof(guint32); ++ memcpy(p1, p2, sec_size); ++ } else { ++ memcpy(p1, p2, sec_size); ++ p1 += sec_size; ++ *p1='\0'; ++ } ++ break; ++ } ++ g_free(origin_data); ++ memcpy(data, &data_size, sizeof(guint32)); ++ } else { ++ data = (gchar *)g_malloc(idxitem_size + sizeof(guint32)); ++ if (dictfile) ++ fread(data+sizeof(guint32), idxitem_size, 1, dictfile); ++ else ++ dictdzfile->read(data+sizeof(guint32), idxitem_offset, idxitem_size); ++ memcpy(data, &idxitem_size, sizeof(guint32)); ++ } ++ g_free(cache[cache_cur].data); ++ ++ cache[cache_cur].data = data; ++ cache[cache_cur].offset = idxitem_offset; ++ cache_cur++; ++ if (cache_cur==WORDDATA_CACHE_NUM) ++ cache_cur = 0; ++ return data; ++} ++ ++bool DictBase::SearchData(std::vector<std::string> &SearchWords, guint32 idxitem_offset, guint32 idxitem_size, gchar *origin_data) ++{ ++ int nWord = SearchWords.size(); ++ std::vector<bool> WordFind(nWord, false); ++ int nfound=0; ++ ++ if (dictfile) ++ fseek(dictfile, idxitem_offset, SEEK_SET); ++ if (dictfile) ++ fread(origin_data, idxitem_size, 1, dictfile); ++ else ++ dictdzfile->read(origin_data, idxitem_offset, idxitem_size); ++ gchar *p = origin_data; ++ guint32 sec_size; ++ int j; ++ if (!sametypesequence.empty()) { ++ gint sametypesequence_len = sametypesequence.length(); ++ for (int i=0; i<sametypesequence_len-1; i++) { ++ switch (sametypesequence[i]) { ++ case 'm': ++ case 't': ++ case 'y': ++ case 'l': ++ case 'g': ++ case 'x': ++ case 'k': ++ case 'w': ++ case 'h': ++ for (j=0; j<nWord; j++) ++ // KMP() is faster than strstr() in theory. Really? Always be true? ++ //if (!WordFind[j] && strstr(p, SearchWords[j].c_str())) { ++ if (!WordFind[j] && KMP(p, strlen(p), SearchWords[j].c_str())!=-1) { ++ WordFind[j] = true; ++ ++nfound; ++ } ++ ++ if (nfound==nWord) ++ return true; ++ sec_size = strlen(p)+1; ++ p+=sec_size; ++ break; ++ default: ++ if (g_ascii_isupper(sametypesequence[i])) { ++ sec_size = get_uint32(p); ++ sec_size += sizeof(guint32); ++ } else { ++ sec_size = strlen(p)+1; ++ } ++ p+=sec_size; ++ } ++ } ++ switch (sametypesequence[sametypesequence_len-1]) { ++ case 'm': ++ case 't': ++ case 'y': ++ case 'l': ++ case 'g': ++ case 'x': ++ case 'k': ++ case 'w': ++ case 'h': ++ sec_size = idxitem_size - (p-origin_data); ++ for (j=0; j<nWord; j++) ++ //if (!WordFind[j] && g_strstr_len(p, sec_size, SearchWords[j].c_str())) { ++ if (!WordFind[j] && KMP(p, sec_size, SearchWords[j].c_str())!=-1) { ++ WordFind[j] = true; ++ ++nfound; ++ } ++ ++ if (nfound==nWord) ++ return true; ++ break; ++ } ++ } else { ++ while (guint32(p - origin_data)<idxitem_size) { ++ switch (*p) { ++ case 'm': ++ case 't': ++ case 'y': ++ case 'l': ++ case 'g': ++ case 'x': ++ case 'k': ++ case 'w': ++ case 'h': ++ for (j=0; j<nWord; j++) ++ if (!WordFind[j] && strstr(p, SearchWords[j].c_str())) { ++ WordFind[j] = true; ++ ++nfound; ++ } ++ ++ if (nfound==nWord) ++ return true; ++ sec_size = strlen(p)+1; ++ p+=sec_size; ++ break; ++ default: ++ if (g_ascii_isupper(*p)) { ++ sec_size = get_uint32(p); ++ sec_size += sizeof(guint32); ++ } else { ++ sec_size = strlen(p)+1; ++ } ++ p+=sec_size; ++ } ++ } ++ } ++ return false; ++} ++ +diff -Nur stardict-3.0.1.orig//src/lib/http_client.h stardict-3.0.1/src/lib/http_client.h +--- stardict-3.0.1.orig//src/lib/http_client.h 2007-08-30 04:23:32.000000000 -0500 ++++ stardict-3.0.1/src/lib/http_client.h 2010-05-24 00:53:36.373667747 -0500 +@@ -5,6 +5,7 @@ + #include "sigc++/sigc++.h" + #include <string> + #include <vector> ++#include <cstring> + + #ifndef _WIN32 + # include <netdb.h> +diff -Nur stardict-3.0.1.orig//src/lib/pluginmanager.cpp stardict-3.0.1/src/lib/pluginmanager.cpp +--- stardict-3.0.1.orig//src/lib/pluginmanager.cpp 2007-10-09 22:26:45.000000000 -0500 ++++ stardict-3.0.1/src/lib/pluginmanager.cpp 2010-05-24 00:53:36.374667481 -0500 +@@ -1,6 +1,7 @@ + #include "pluginmanager.h" + #include "file.hpp" + #include <string> ++#include <cstring> + + StarDictPluginBaseObject::StarDictPluginBaseObject(const char *filename, GModule *module_, plugin_configure_func_t configure_func_): + plugin_filename(filename), module(module_), configure_func(configure_func_) +diff -Nur stardict-3.0.1.orig//src/lib/stardict_client.cpp stardict-3.0.1/src/lib/stardict_client.cpp +--- stardict-3.0.1.orig//src/lib/stardict_client.cpp 2007-10-31 03:32:11.000000000 -0500 ++++ stardict-3.0.1/src/lib/stardict_client.cpp 2010-05-24 00:55:03.618667273 -0500 +@@ -30,6 +30,9 @@ + #include "getuint32.h" + + #include "stardict_client.hpp" ++#include <stdio.h> ++#include <cstdlib> ++#include <cstring> + + #define PROTOCOL_VERSION "0.3" + +diff -Nur stardict-3.0.1.orig//src/lib/stardict_client.cpp~ stardict-3.0.1/src/lib/stardict_client.cpp~ +--- stardict-3.0.1.orig//src/lib/stardict_client.cpp~ 1969-12-31 18:00:00.000000000 -0600 ++++ stardict-3.0.1/src/lib/stardict_client.cpp~ 2007-10-31 03:32:11.000000000 -0500 +@@ -0,0 +1,1263 @@ ++/* ++ * This file part of StarDict - A international dictionary for GNOME. ++ * http://stardict.sourceforge.net ++ * Copyright (C) 2006 Hu Zheng <huzheng_001@163.com> ++ * ++ * 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 3 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 Library 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. ++ */ ++ ++#ifdef HAVE_CONFIG_H ++# include "config.h" ++#endif ++ ++#include <glib.h> ++#include <glib/gi18n.h> ++ ++#include "sockets.hpp" ++#include "md5.h" ++#include "getuint32.h" ++ ++#include "stardict_client.hpp" ++ ++#define PROTOCOL_VERSION "0.3" ++ ++#define CODE_HELLO 220 /* text msg-id */ ++#define CODE_GOODBYE 221 /* Closing Connection */ ++#define CODE_OK 250 /* ok */ ++#define CODE_TEMPORARILY_UNAVAILABLE 420 /* server unavailable */ ++#define CODE_SYNTAX_ERROR 500 /* syntax, command not recognized */ ++#define CODE_DENIED 521 ++#define CODE_DICTMASK_NOTSET 522 ++ ++unsigned int STARDICT::Cmd::next_seq = 1; ++ ++sigc::signal<void, const char *> StarDictClient::on_error_; ++sigc::signal<void, const struct STARDICT::LookupResponse *, unsigned int> StarDictClient::on_lookup_end_; ++sigc::signal<void, const struct STARDICT::LookupResponse *, unsigned int> StarDictClient::on_floatwin_lookup_end_; ++sigc::signal<void, const char *> StarDictClient::on_register_end_; ++sigc::signal<void, const char *> StarDictClient::on_getdictmask_end_; ++sigc::signal<void, const char *> StarDictClient::on_dirinfo_end_; ++sigc::signal<void, const char *> StarDictClient::on_dictinfo_end_; ++sigc::signal<void, int> StarDictClient::on_maxdictcount_end_; ++sigc::signal<void, std::list<char *> *> StarDictClient::on_previous_end_; ++sigc::signal<void, std::list<char *> *> StarDictClient::on_next_end_; ++ ++static void arg_escape(std::string &earg, const char *arg) ++{ ++ earg.clear(); ++ while (*arg) { ++ if (*arg=='\\') { ++ earg+="\\\\"; ++ } else if (*arg==' ') { ++ earg+="\\ "; ++ } else if (*arg=='\n') { ++ earg+="\\n"; ++ } else { ++ earg+=*arg; ++ } ++ arg++; ++ } ++} ++ ++STARDICT::Cmd::Cmd(int cmd, ...) ++{ ++ this->seq = this->next_seq; ++ this->next_seq++; ++ this->reading_status = 0; ++ this->command = cmd; ++ va_list ap; ++ va_start( ap, cmd ); ++ switch (cmd) { ++ case CMD_CLIENT: ++ { ++ const char *client_name = va_arg( ap, const char * ); ++ std::string earg1, earg2; ++ arg_escape(earg1, PROTOCOL_VERSION); ++ arg_escape(earg2, client_name); ++ this->data = g_strdup_printf("client %s %s\n", earg1.c_str(), earg2.c_str()); ++ break; ++ } ++ case CMD_REGISTER: ++ { ++ const char *user = va_arg( ap, const char * ); ++ const char *passwd = va_arg( ap, const char * ); ++ const char *email = va_arg( ap, const char * ); ++ std::string earg1, earg2, earg3; ++ arg_escape(earg1, user); ++ arg_escape(earg2, passwd); ++ arg_escape(earg3, email); ++ this->data = g_strdup_printf("register %s %s %s\n", earg1.c_str(), earg2.c_str(), earg3.c_str()); ++ break; ++ } ++ /*case CMD_CHANGE_PASSWD: ++ { ++ const char *user = va_arg( ap, const char * ); ++ const char *old_passwd = va_arg( ap, const char * ); ++ const char *new_passwd = va_arg( ap, const char * ); ++ std::string earg1, earg2, earg3; ++ arg_escape(earg1, user); ++ arg_escape(earg2, old_passwd); ++ arg_escape(earg3, new_passwd); ++ this->data = g_strdup_printf("change_password %s %s %s\n", earg1.c_str(), earg2.c_str(), earg3.c_str()); ++ break; ++ }*/ ++ case CMD_AUTH: ++ this->auth = new AuthInfo(); ++ this->auth->user = va_arg( ap, const char * ); ++ this->auth->passwd = va_arg( ap, const char * ); ++ break; ++ case CMD_LOOKUP: ++ { ++ std::string earg; ++ arg_escape(earg, va_arg( ap, const char * )); ++ this->data = g_strdup_printf("lookup %s 30\n", earg.c_str()); ++ this->lookup_response = NULL; ++ break; ++ } ++ case CMD_PREVIOUS: ++ { ++ std::string earg; ++ arg_escape(earg, va_arg( ap, const char * )); ++ this->data = g_strdup_printf("previous %s 15\n", earg.c_str()); ++ this->wordlist_response = NULL; ++ break; ++ } ++ case CMD_NEXT: ++ { ++ std::string earg; ++ arg_escape(earg, va_arg( ap, const char * )); ++ this->data = g_strdup_printf("next %s 30\n", earg.c_str()); ++ this->wordlist_response = NULL; ++ break; ++ } ++ /*case CMD_QUERY: ++ { ++ std::string earg; ++ arg_escape(earg, va_arg( ap, const char * )); ++ this->data = g_strdup_printf("query %s\n", earg.c_str()); ++ this->lookup_response = NULL; ++ break; ++ }*/ ++ case CMD_SELECT_QUERY: ++ { ++ std::string earg; ++ arg_escape(earg, va_arg( ap, const char * )); ++ this->data = g_strdup_printf("selectquery %s\n", earg.c_str()); ++ this->lookup_response = NULL; ++ break; ++ } ++ case CMD_SMART_QUERY: ++ { ++ std::string earg; ++ arg_escape(earg, va_arg( ap, const char * )); ++ int BeginPos = va_arg( ap, int ); ++ this->data = g_strdup_printf("smartquery %s %d\n", earg.c_str(), BeginPos); ++ this->lookup_response = NULL; ++ break; ++ } ++ case CMD_DEFINE: ++ { ++ std::string earg; ++ arg_escape(earg, va_arg( ap, const char * )); ++ this->data = g_strdup_printf("define %s\n", earg.c_str()); ++ this->lookup_response = NULL; ++ break; ++ } ++ case CMD_SET_DICT_MASK: ++ { ++ std::string earg; ++ arg_escape(earg, va_arg( ap, const char * )); ++ this->data = g_strdup_printf("setdictmask %s\n", earg.c_str()); ++ break; ++ } ++ case CMD_GET_DICT_MASK: ++ this->data = g_strdup("getdictmask\n"); ++ break; ++ /*case CMD_SET_COLLATE_FUNC: ++ { ++ std::string earg; ++ arg_escape(earg, va_arg( ap, const char * )); ++ this->data = g_strdup_printf("setcollatefunc %s\n", earg.c_str()); ++ break; ++ } ++ case CMD_GET_COLLATE_FUNC: ++ this->data = g_strdup("getcollatefunc\n"); ++ break; ++ case CMD_SET_LANGUAGE: ++ { ++ std::string earg; ++ arg_escape(earg, va_arg( ap, const char * )); ++ this->data = g_strdup_printf("setlanguage %s\n", earg.c_str()); ++ break; ++ } ++ case CMD_GET_LANGUAGE: ++ this->data = g_strdup("getlanguage\n"); ++ break; ++ case CMD_SET_EMAIL: ++ { ++ std::string earg; ++ arg_escape(earg, va_arg( ap, const char * )); ++ this->data = g_strdup_printf("setemail %s\n", earg.c_str()); ++ break; ++ } ++ case CMD_GET_EMAIL: ++ this->data = g_strdup("getemail\n"); ++ break; ++ case CMD_GET_USER_LEVEL: ++ this->data = g_strdup("getuserlevel\n"); ++ break;*/ ++ case CMD_MAX_DICT_COUNT: ++ this->data = g_strdup("maxdictcount\n"); ++ break; ++ case CMD_DIR_INFO: ++ { ++ std::string earg; ++ arg_escape(earg, va_arg( ap, const char * )); ++ this->data = g_strdup_printf("dirinfo %s\n", earg.c_str()); ++ break; ++ } ++ case CMD_DICT_INFO: ++ { ++ std::string earg; ++ arg_escape(earg, va_arg( ap, const char * )); ++ this->data = g_strdup_printf("dictinfo %s\n", earg.c_str()); ++ break; ++ } ++ /*case CMD_USER_LEVEL: ++ { ++ std::string earg1, earg2, earg3; ++ arg_escape(earg1, va_arg( ap, const char * )); ++ arg_escape(earg2, va_arg( ap, const char * )); ++ arg_escape(earg3, va_arg( ap, const char * )); ++ this->data = g_strdup_printf("userlevel %s %s %s\n", earg1.c_str(), earg2.c_str(), earg3.c_str()); ++ break; ++ }*/ ++ case CMD_QUIT: ++ this->data = g_strdup("quit\n"); ++ break; ++ } ++ va_end( ap ); ++} ++ ++STARDICT::LookupResponse::DictResponse::DictResponse() ++{ ++ oword = NULL; ++} ++ ++STARDICT::LookupResponse::DictResponse::~DictResponse() ++{ ++ g_free(oword); ++ for (std::list<DictResult *>::iterator i = dict_result_list.begin(); i != dict_result_list.end(); ++i) { ++ delete *i; ++ } ++} ++ ++STARDICT::LookupResponse::DictResponse::DictResult::DictResult() ++{ ++ bookname = NULL; ++} ++ ++STARDICT::LookupResponse::DictResponse::DictResult::~DictResult() ++{ ++ g_free(bookname); ++ for (std::list<WordResult *>::iterator i = word_result_list.begin(); i != word_result_list.end(); ++i) { ++ delete *i; ++ } ++} ++ ++STARDICT::LookupResponse::DictResponse::DictResult::WordResult::WordResult() ++{ ++ word = NULL; ++} ++ ++STARDICT::LookupResponse::DictResponse::DictResult::WordResult::~WordResult() ++{ ++ g_free(word); ++ for (std::list<char *>::iterator i = datalist.begin(); i != datalist.end(); ++i) { ++ g_free(*i); ++ } ++} ++ ++STARDICT::LookupResponse::~LookupResponse() ++{ ++ if (listtype == ListType_List) { ++ for (std::list<char *>::iterator i = wordlist->begin(); i != wordlist->end(); ++i) { ++ g_free(*i); ++ } ++ delete wordlist; ++ } else if (listtype == ListType_Tree) { ++ for (std::list<WordTreeElement *>::iterator i = wordtree->begin(); i != wordtree->end(); ++i) { ++ g_free((*i)->bookname); ++ for (std::list<char *>::iterator j = (*i)->wordlist.begin(); j != (*i)->wordlist.end(); ++j) { ++ g_free(*j); ++ } ++ delete *i; ++ } ++ delete wordtree; ++ } ++} ++ ++STARDICT::Cmd::~Cmd() ++{ ++ if (this->command == CMD_AUTH) { ++ delete this->auth; ++ } else { ++ g_free(this->data); ++ } ++ if (this->command == CMD_LOOKUP || this->command == CMD_DEFINE || this->command == CMD_SELECT_QUERY || this->command == CMD_SMART_QUERY) { ++ delete this->lookup_response; ++ } else if (this->command == CMD_PREVIOUS || this->command == CMD_NEXT) { ++ if (this->wordlist_response) { ++ for (std::list<char *>::iterator i = this->wordlist_response->begin(); i != this->wordlist_response->end(); ++i) { ++ g_free(*i); ++ } ++ delete this->wordlist_response; ++ } ++ } ++} ++ ++StarDictCache::StarDictCache() ++{ ++ str_pool.resize(str_pool_size); ++ for (size_t i = 0; i< str_pool_size; i++) { ++ str_pool[i] = NULL; ++ } ++ cur_str_pool_pos = 0; ++ ++ lookup_response_pool.resize(lookup_response_pool_size); ++ for (size_t i = 0; i< lookup_response_pool_size; i++) { ++ lookup_response_pool[i] = NULL; ++ } ++ cur_lookup_response_pool_pos = 0; ++} ++ ++StarDictCache::~StarDictCache() ++{ ++ clean_all_cache(); ++} ++ ++void StarDictCache::clean_all_cache() ++{ ++ for (std::vector<StrElement *>::iterator i = str_pool.begin(); i != str_pool.end(); ++i) { ++ if (*i) { ++ g_free((*i)->data); ++ delete *i; ++ *i = NULL; ++ } ++ } ++ clean_cache_lookup_response(); ++} ++ ++void StarDictCache::clean_cache_lookup_response() ++{ ++ for (std::vector<LookupResponseElement *>::iterator i = lookup_response_pool.begin(); i != lookup_response_pool.end(); ++i) { ++ if (*i) { ++ delete ((*i)->lookup_response); ++ delete *i; ++ *i = NULL; ++ } ++ } ++} ++ ++char *StarDictCache::get_cache_str(const char *key_str) ++{ ++ for (std::vector<StrElement *>::iterator i = str_pool.begin(); i != str_pool.end(); ++i) { ++ if (*i) { ++ if ((*i)->key == key_str) ++ return (*i)->data; ++ } ++ } ++ return NULL; ++} ++ ++STARDICT::LookupResponse *StarDictCache::get_cache_lookup_response(const char *key_str) ++{ ++ for (std::vector<LookupResponseElement *>::iterator i = lookup_response_pool.begin(); i != lookup_response_pool.end(); ++i) { ++ if (*i) { ++ if ((*i)->key == key_str) ++ return (*i)->lookup_response; ++ } ++ } ++ return NULL; ++} ++ ++void StarDictCache::clean_cache_str(const char *key_str) ++{ ++ for (std::vector<StrElement *>::iterator i = str_pool.begin(); i != str_pool.end(); ++i) { ++ if (*i) { ++ if ((*i)->key == key_str) { ++ g_free((*i)->data); ++ delete *i; ++ *i = NULL; ++ //return; ++ } ++ } ++ } ++} ++ ++void StarDictCache::save_cache_str(const char *key_str, char *data) ++{ ++ if (str_pool[cur_str_pool_pos]) { ++ g_free(str_pool[cur_str_pool_pos]->data); ++ delete str_pool[cur_str_pool_pos]; ++ } ++ str_pool[cur_str_pool_pos] = new StrElement(); ++ str_pool[cur_str_pool_pos]->key = key_str; ++ str_pool[cur_str_pool_pos]->data = data; ++ cur_str_pool_pos++; ++ if (cur_str_pool_pos == str_pool_size) ++ cur_str_pool_pos = 0; ++} ++ ++void StarDictCache::save_cache_lookup_response(const char *key_str, STARDICT::LookupResponse *lookup_response) ++{ ++ if (lookup_response_pool[cur_lookup_response_pool_pos]) { ++ delete lookup_response_pool[cur_lookup_response_pool_pos]->lookup_response; ++ delete lookup_response_pool[cur_lookup_response_pool_pos]; ++ } ++ lookup_response_pool[cur_lookup_response_pool_pos] = new LookupResponseElement(); ++ lookup_response_pool[cur_lookup_response_pool_pos]->key = key_str; ++ lookup_response_pool[cur_lookup_response_pool_pos]->lookup_response = lookup_response; ++ cur_lookup_response_pool_pos++; ++ if (cur_lookup_response_pool_pos == lookup_response_pool_size) ++ cur_lookup_response_pool_pos = 0; ++} ++ ++StarDictClient::StarDictClient() ++{ ++ sd_ = -1; ++ channel_ = NULL; ++ in_source_id_ = 0; ++ out_source_id_ = 0; ++ is_connected_ = false; ++} ++ ++StarDictClient::~StarDictClient() ++{ ++ disconnect(); ++} ++ ++void StarDictClient::set_server(const char *host, int port) ++{ ++ if (host_ != host || port_ != port) { ++ host_ = host; ++ port_ = port; ++ host_resolved = false; ++ clean_all_cache(); ++ } ++} ++ ++void StarDictClient::set_auth(const char *user, const char *md5passwd) ++{ ++ if (user_ != user || md5passwd_ != md5passwd) { ++ user_ = user; ++ md5passwd_ = md5passwd; ++ clean_all_cache(); ++ } ++} ++ ++bool StarDictClient::try_cache(STARDICT::Cmd *c) ++{ ++ if (c->command == STARDICT::CMD_LOOKUP || c->command == STARDICT::CMD_DEFINE || c->command == STARDICT::CMD_SELECT_QUERY || c->command == STARDICT::CMD_SMART_QUERY) { ++ STARDICT::LookupResponse *res = get_cache_lookup_response(c->data); ++ if (res) { ++ if (c->command == STARDICT::CMD_LOOKUP || c->command == STARDICT::CMD_DEFINE) ++ on_lookup_end_.emit(res, 0); ++ else if (c->command == STARDICT::CMD_SELECT_QUERY || c->command == STARDICT::CMD_SMART_QUERY) ++ on_floatwin_lookup_end_.emit(res, 0); ++ delete c; ++ return true; ++ } else { ++ return false; ++ } ++ } ++ if (c->command == STARDICT::CMD_PREVIOUS || c->command == STARDICT::CMD_NEXT) { ++ // Not implemented yet. ++ return false; ++ } ++ char *data = get_cache_str(c->data); ++ if (data) { ++ switch (c->command) { ++ case STARDICT::CMD_DIR_INFO: ++ on_dirinfo_end_.emit(data); ++ break; ++ case STARDICT::CMD_DICT_INFO: ++ on_dictinfo_end_.emit(data); ++ break; ++ case STARDICT::CMD_GET_DICT_MASK: ++ on_getdictmask_end_.emit(data); ++ break; ++ case STARDICT::CMD_MAX_DICT_COUNT: ++ on_maxdictcount_end_.emit(atoi(data)); ++ break; ++ } ++ delete c; ++ return true; ++ } else { ++ return false; ++ } ++} ++ ++void StarDictClient::send_commands(int num, ...) ++{ ++ STARDICT::Cmd *c; ++ if (!is_connected_) { ++#ifdef _WIN32 ++ c = new STARDICT::Cmd(STARDICT::CMD_CLIENT, "StarDict Windows"); ++#else ++ c = new STARDICT::Cmd(STARDICT::CMD_CLIENT, "StarDict Linux"); ++#endif ++ cmdlist.push_back(c); ++ if (!user_.empty() && !md5passwd_.empty()) { ++ c = new STARDICT::Cmd(STARDICT::CMD_AUTH, user_.c_str(), md5passwd_.c_str()); ++ cmdlist.push_back(c); ++ } ++ } ++ va_list ap; ++ va_start( ap, num); ++ for (int i = 0; i< num; i++) { ++ c = va_arg( ap, STARDICT::Cmd *); ++ cmdlist.push_back(c); ++ } ++ va_end( ap ); ++ if (!is_connected_) { ++ waiting_banner_ = true; ++ connect(); ++ } ++} ++ ++void StarDictClient::try_cache_or_send_commands(int num, ...) ++{ ++ STARDICT::Cmd *c; ++ std::list<STARDICT::Cmd *> send_cmdlist; ++ va_list ap; ++ va_start( ap, num); ++ for (int i = 0; i< num; i++) { ++ c = va_arg( ap, STARDICT::Cmd *); ++ if (!try_cache(c)) { ++ send_cmdlist.push_back(c); ++ } ++ } ++ va_end( ap ); ++ if (send_cmdlist.empty()) ++ return; ++ ++ if (!is_connected_) { ++#ifdef _WIN32 ++ c = new STARDICT::Cmd(STARDICT::CMD_CLIENT, "StarDict Windows"); ++#else ++ c = new STARDICT::Cmd(STARDICT::CMD_CLIENT, "StarDict Linux"); ++#endif ++ cmdlist.push_back(c); ++ if (!user_.empty() && !md5passwd_.empty()) { ++ c = new STARDICT::Cmd(STARDICT::CMD_AUTH, user_.c_str(), md5passwd_.c_str()); ++ cmdlist.push_back(c); ++ } ++ } ++ for (std::list<STARDICT::Cmd *>::iterator i = send_cmdlist.begin(); i!= send_cmdlist.end(); ++i) { ++ cmdlist.push_back(*i); ++ } ++ if (!is_connected_) { ++ waiting_banner_ = true; ++ connect(); ++ } ++} ++ ++void StarDictClient::write_str(const char *str, GError **err) ++{ ++ int len = strlen(str); ++ int left_byte = len; ++ GIOStatus res; ++ gsize bytes_written; ++ while (left_byte) { ++ res = g_io_channel_write_chars(channel_, str+(len - left_byte), left_byte, &bytes_written, err); ++ if (res == G_IO_STATUS_ERROR) { ++ disconnect(); ++ return; ++ } ++ left_byte -= bytes_written; ++ } ++ res = g_io_channel_flush(channel_, err); ++ if (res == G_IO_STATUS_ERROR) { ++ disconnect(); ++ } ++ if (out_source_id_ == 0) ++ out_source_id_ = g_io_add_watch(channel_, GIOCondition(G_IO_OUT), on_io_out_event, this); ++} ++ ++void StarDictClient::request_command() ++{ ++ reading_type_ = READ_LINE; ++ if (cmdlist.empty()) { ++ cmdlist.push_back(new STARDICT::Cmd(STARDICT::CMD_QUIT)); ++ } ++ STARDICT::Cmd *c = cmdlist.front(); ++ switch (c->command) { ++ case STARDICT::CMD_AUTH: ++ { ++ struct MD5Context ctx; ++ unsigned char digest[16]; ++ char hex[33]; ++ int i; ++ MD5Init(&ctx); ++ MD5Update(&ctx, (const unsigned char*)cmd_reply.daemonStamp.c_str(), cmd_reply.daemonStamp.length()); ++ MD5Update(&ctx, (const unsigned char*)(c->auth->passwd.c_str()), c->auth->passwd.length()); ++ MD5Final(digest, &ctx ); ++ for (i = 0; i < 16; i++) ++ sprintf( hex+2*i, "%02x", digest[i] ); ++ hex[32] = '\0'; ++ std::string earg1, earg2; ++ arg_escape(earg1, c->auth->user.c_str()); ++ arg_escape(earg2, hex); ++ char *data = g_strdup_printf("auth %s %s\n", earg1.c_str(), earg2.c_str()); ++ GError *err = NULL; ++ write_str(data, &err); ++ g_free(data); ++ if (err) { ++ on_error_.emit(err->message); ++ g_error_free(err); ++ return; ++ } ++ break; ++ } ++ default: ++ { ++ GError *err = NULL; ++ write_str(c->data, &err); ++ if (err) { ++ on_error_.emit(err->message); ++ g_error_free(err); ++ return; ++ } ++ break; ++ } ++ } ++ return; ++} ++ ++void StarDictClient::clean_command() ++{ ++ for (std::list<STARDICT::Cmd *>::iterator i=cmdlist.begin(); i!=cmdlist.end(); ++i) { ++ delete *i; ++ } ++ cmdlist.clear(); ++} ++ ++void StarDictClient::connect() ++{ ++ if (host_resolved) { ++ on_resolved(this, true, sa); ++ } else { ++ Socket::resolve(host_, this, on_resolved); ++ } ++} ++ ++void StarDictClient::on_resolved(gpointer data, bool resolved, in_addr_t sa_) ++{ ++ StarDictClient *oStarDictClient = (StarDictClient *)data; ++ if (!resolved) { ++ static bool showed_once = false; ++ if (!showed_once) { ++ showed_once = true; ++ gchar *mes = g_strdup_printf("Can not reslove %s: %s\n", ++ oStarDictClient->host_.c_str(), Socket::get_error_msg().c_str()); ++ on_error_.emit(mes); ++ g_free(mes); ++ } ++ return; ++ } ++ ++ if (oStarDictClient->host_resolved == false) { ++ oStarDictClient->sa = sa_; ++ oStarDictClient->host_resolved = true; ++ } ++ ++ oStarDictClient->sd_ = Socket::socket(); ++ ++ if (oStarDictClient->sd_ == -1) { ++ std::string str = "Can not create socket: " + Socket::get_error_msg(); ++ on_error_.emit(str.c_str()); ++ return; ++ } ++ Socket::connect(oStarDictClient->sd_, sa_, oStarDictClient->port_, oStarDictClient, on_connected); ++} ++ ++void StarDictClient::on_connected(gpointer data, bool succeeded) ++{ ++ StarDictClient *oStarDictClient = (StarDictClient *)data; ++ if (!succeeded) { ++ static bool showed_once = false; ++ if (!showed_once) { ++ showed_once = true; ++ gchar *mes = g_strdup_printf("Can not connect to %s: %s\n", ++ oStarDictClient->host_.c_str(), Socket::get_error_msg().c_str()); ++ on_error_.emit(mes); ++ g_free(mes); ++ } ++ return; ++ } ++#ifdef _WIN32 ++ oStarDictClient->channel_ = g_io_channel_win32_new_socket(oStarDictClient->sd_); ++#else ++ oStarDictClient->channel_ = g_io_channel_unix_new(oStarDictClient->sd_); ++#endif ++ ++ g_io_channel_set_encoding(oStarDictClient->channel_, NULL, NULL); ++ ++ /* make sure that the channel is non-blocking */ ++ int flags = g_io_channel_get_flags(oStarDictClient->channel_); ++ flags |= G_IO_FLAG_NONBLOCK; ++ GError *err = NULL; ++ g_io_channel_set_flags(oStarDictClient->channel_, GIOFlags(flags), &err); ++ if (err) { ++ g_io_channel_unref(oStarDictClient->channel_); ++ oStarDictClient->channel_ = NULL; ++ gchar *str = g_strdup_printf("Unable to set the channel as non-blocking: %s", err->message); ++ on_error_.emit(str); ++ g_free(str); ++ g_error_free(err); ++ return; ++ } ++ ++ oStarDictClient->is_connected_ = true; ++ oStarDictClient->waiting_banner_ = true; ++ oStarDictClient->reading_type_ = READ_LINE; ++ oStarDictClient->in_source_id_ = g_io_add_watch(oStarDictClient->channel_, GIOCondition(G_IO_IN | G_IO_ERR), on_io_in_event, oStarDictClient); ++} ++ ++void StarDictClient::disconnect() ++{ ++ clean_command(); ++ if (in_source_id_) { ++ g_source_remove(in_source_id_); ++ in_source_id_ = 0; ++ } ++ if (out_source_id_) { ++ g_source_remove(out_source_id_); ++ out_source_id_ = 0; ++ } ++ ++ if (channel_) { ++ g_io_channel_shutdown(channel_, TRUE, NULL); ++ g_io_channel_unref(channel_); ++ channel_ = NULL; ++ } ++ if (sd_ != -1) { ++ Socket::close(sd_); ++ sd_ = -1; ++ } ++ is_connected_ = false; ++} ++ ++gboolean StarDictClient::on_io_out_event(GIOChannel *ch, GIOCondition cond, ++ gpointer user_data) ++{ ++ StarDictClient *stardict_client = static_cast<StarDictClient *>(user_data); ++ GError *err = NULL; ++ GIOStatus res = g_io_channel_flush(stardict_client->channel_, &err); ++ if (res == G_IO_STATUS_AGAIN) { ++ return TRUE; ++ } else if (err) { ++ on_error_.emit(err->message); ++ g_error_free(err); ++ } ++ stardict_client->out_source_id_ = 0; ++ return FALSE; ++} ++ ++gboolean StarDictClient::on_io_in_event(GIOChannel *ch, GIOCondition cond, ++ gpointer user_data) ++{ ++ StarDictClient *stardict_client = static_cast<StarDictClient *>(user_data); ++ ++ if (!stardict_client->channel_) { ++ //g_warning("No channel available\n"); ++ return FALSE; ++ } ++ if (cond & G_IO_ERR) { ++ /*gchar *mes = ++ g_strdup_printf("Connection failed to the dictionary server at %s:%d", ++ stardict_client->host_.c_str(), stardict_client->port_); ++ on_error_.emit(mes); ++ g_free(mes);*/ ++ stardict_client->disconnect(); ++ return FALSE; ++ } ++ GError *err = NULL; ++ gsize term, len; ++ gchar *line; ++ GIOStatus res; ++ ++ for (;;) { ++ if (!stardict_client->channel_) ++ break; ++ bool result; ++ if (stardict_client->reading_type_ == READ_SIZE) { ++ gsize bytes_read; ++ res = g_io_channel_read_chars(stardict_client->channel_, stardict_client->size_data+(stardict_client->size_count-stardict_client->size_left), stardict_client->size_left, &bytes_read, &err); ++ if (res == G_IO_STATUS_ERROR || res == G_IO_STATUS_EOF) { ++ if (err) { ++ gchar *str = g_strdup_printf("Error while reading reply from server: %s", err->message); ++ on_error_.emit(str); ++ g_free(str); ++ g_error_free(err); ++ } ++ stardict_client->disconnect(); ++ ++ return FALSE; ++ } ++ stardict_client->size_left -= bytes_read; ++ if (stardict_client->size_left == 0) ++ result = stardict_client->parse(stardict_client->size_data); ++ else ++ break; ++ } else { ++ if (stardict_client->reading_type_ == READ_LINE) ++ g_io_channel_set_line_term(stardict_client->channel_, "\n", 1); ++ else if (stardict_client->reading_type_ == READ_STRING) ++ g_io_channel_set_line_term(stardict_client->channel_, "", 1); ++ ++ res = g_io_channel_read_line(stardict_client->channel_, &line, ++ &len, &term, &err); ++ if (res == G_IO_STATUS_ERROR || res == G_IO_STATUS_EOF) { ++ if (err) { ++ gchar *str = g_strdup_printf("Error while reading reply from server: %s", err->message); ++ on_error_.emit(str); ++ g_free(str); ++ g_error_free(err); ++ } ++ stardict_client->disconnect(); ++ ++ return FALSE; ++ } ++ ++ if (!len) ++ break; ++ ++ //truncate the line terminator before parsing ++ line[term] = '\0'; ++ result = stardict_client->parse(line); ++ } ++ if (!result) { ++ stardict_client->disconnect(); ++ return FALSE; ++ } ++ } ++ ++ return TRUE; ++} ++ ++int StarDictClient::parse_banner(gchar *line) ++{ ++ int status; ++ status = atoi(line); ++ if (status != CODE_HELLO) { ++ if (status == CODE_TEMPORARILY_UNAVAILABLE) { ++ printf("Server temporarily unavailable!\n"); ++ } else { ++ printf("Unexpected status code %d\n", status); ++ } ++ return 0; ++ } ++ char *p; ++ p = strrchr(line, ' '); ++ if (p) { ++ p++; ++ cmd_reply.daemonStamp = p; ++ } ++ return 1; ++} ++ ++int StarDictClient::parse_command_client(gchar *line) ++{ ++ int status; ++ status = atoi(line); ++ if (status != CODE_OK) { ++ gchar *str = g_strdup_printf("Client denied: %s", line); ++ on_error_.emit(str); ++ g_free(str); ++ return 0; ++ } ++ return 1; ++} ++ ++int StarDictClient::parse_command_auth(gchar *line) ++{ ++ int status; ++ status = atoi(line); ++ if (status != CODE_OK) { ++ gchar *str = g_strdup_printf(_("Authentication denied: %s"), line); ++ on_error_.emit(str); ++ g_free(str); ++ return 0; ++ } ++ return 1; ++} ++ ++int StarDictClient::parse_command_register(gchar *line) ++{ ++ int status; ++ status = atoi(line); ++ if (status != CODE_OK) { ++ gchar *str = g_strdup_printf(_("Register failed: %s"), line); ++ on_error_.emit(str); ++ g_free(str); ++ return 0; ++ } ++ on_register_end_.emit(_("Register success!")); ++ return 1; ++} ++ ++int StarDictClient::parse_command_quit(gchar *line) ++{ ++ int status; ++ status = atoi(line); ++ if (status != CODE_GOODBYE) { ++ } ++ return 0; ++} ++ ++int StarDictClient::parse_command_setdictmask(gchar *line) ++{ ++ int status; ++ status = atoi(line); ++ if (status != CODE_OK) { ++ gchar *str = g_strdup_printf("Set Dict Mask failed: %s", line); ++ on_error_.emit(str); ++ g_free(str); ++ return 0; ++ } ++ clean_cache_str("getdictmask\n"); ++ clean_cache_lookup_response(); ++ return 1; ++} ++ ++int StarDictClient::parse_command_getdictmask(STARDICT::Cmd* cmd, gchar *buf) ++{ ++ if (cmd->reading_status == 0) { ++ int status; ++ status = atoi(buf); ++ if (status != CODE_OK) { ++ g_free(buf); ++ on_error_.emit(_("You haven't setup the account. Please open the \"Net Dict\" page in the Preferences dialog and register an account first.")); ++ return 0; ++ } ++ g_free(buf); ++ cmd->reading_status = 1; ++ reading_type_ = READ_STRING; ++ } else if (cmd->reading_status == 1) { ++ on_getdictmask_end_.emit(buf); ++ save_cache_str(cmd->data, buf); ++ return 1; ++ } ++ return 2; ++} ++ ++int StarDictClient::parse_command_dirinfo(STARDICT::Cmd* cmd, gchar *buf) ++{ ++ if (cmd->reading_status == 0) { ++ int status; ++ status = atoi(buf); ++ if (status != CODE_OK) { ++ gchar *str = g_strdup_printf("Get dir info failed: %s", buf); ++ g_free(buf); ++ on_error_.emit(str); ++ g_free(str); ++ return 0; ++ } ++ g_free(buf); ++ cmd->reading_status = 1; ++ reading_type_ = READ_STRING; ++ } else if (cmd->reading_status == 1) { ++ on_dirinfo_end_.emit(buf); ++ save_cache_str(cmd->data, buf); ++ return 1; ++ } ++ return 2; ++} ++ ++int StarDictClient::parse_command_dictinfo(STARDICT::Cmd* cmd, gchar *buf) ++{ ++ if (cmd->reading_status == 0) { ++ int status; ++ status = atoi(buf); ++ if (status != CODE_OK) { ++ gchar *str = g_strdup_printf("Get dict info failed: %s", buf); ++ g_free(buf); ++ on_error_.emit(str); ++ g_free(str); ++ return 0; ++ } ++ g_free(buf); ++ cmd->reading_status = 1; ++ reading_type_ = READ_STRING; ++ } else if (cmd->reading_status == 1) { ++ on_dictinfo_end_.emit(buf); ++ save_cache_str(cmd->data, buf); ++ return 1; ++ } ++ return 2; ++} ++ ++int StarDictClient::parse_command_maxdictcount(STARDICT::Cmd* cmd, gchar *buf) ++{ ++ if (cmd->reading_status == 0) { ++ int status; ++ status = atoi(buf); ++ if (status != CODE_OK) { ++ gchar *str = g_strdup_printf("Get max dict count failed: %s", buf); ++ g_free(buf); ++ on_error_.emit(str); ++ g_free(str); ++ return 0; ++ } ++ g_free(buf); ++ cmd->reading_status = 1; ++ reading_type_ = READ_STRING; ++ } else if (cmd->reading_status == 1) { ++ on_maxdictcount_end_.emit(atoi(buf)); ++ save_cache_str(cmd->data, buf); ++ return 1; ++ } ++ return 2; ++} ++ ++int StarDictClient::parse_wordlist(STARDICT::Cmd* cmd, gchar *buf) ++{ ++ if (cmd->reading_status == 0) { // Read code. ++ int status; ++ status = atoi(buf); ++ g_free(buf); ++ if (status != CODE_OK) { ++ return 0; ++ } ++ cmd->wordlist_response = new std::list<char *>; ++ cmd->reading_status = 1; ++ reading_type_ = READ_STRING; ++ } else if (cmd->reading_status == 1) { ++ if (*buf == '\0') { ++ g_free(buf); ++ if (cmd->command == STARDICT::CMD_PREVIOUS) { ++ on_previous_end_.emit(cmd->wordlist_response); ++ return 1; ++ } else { ++ on_next_end_.emit(cmd->wordlist_response); ++ return 1; ++ } ++ } else { ++ cmd->wordlist_response->push_back(buf); ++ } ++ } ++ return 2; ++} ++ ++int StarDictClient::parse_dict_result(STARDICT::Cmd* cmd, gchar *buf) ++{ ++ if (cmd->reading_status == 0) { // Read code. ++ int status; ++ status = atoi(buf); ++ g_free(buf); ++ if (status != CODE_OK) { ++ if (status == CODE_DICTMASK_NOTSET) { ++ on_error_.emit(_("You haven't chosen any dictionaries, please choose some by clicking \"Manage Dict\"->\"Network dictionaries\"->\"Add\".")); ++ return 1; ++ } else { ++ return 0; ++ } ++ } ++ cmd->lookup_response = new STARDICT::LookupResponse(); ++ cmd->lookup_response->listtype = STARDICT::LookupResponse::ListType_None; ++ cmd->reading_status = 1; ++ reading_type_ = READ_STRING; ++ } else if (cmd->reading_status == 1) { // Read original word. ++ cmd->lookup_response->dict_response.oword = buf; ++ cmd->reading_status = 2; ++ } else if (cmd->reading_status == 2) { // Read book name. ++ if (*buf == '\0') { ++ g_free(buf); ++ if (cmd->command == STARDICT::CMD_DEFINE) { ++ on_lookup_end_.emit(cmd->lookup_response, cmd->seq); ++ save_cache_lookup_response(cmd->data, cmd->lookup_response); ++ cmd->lookup_response = NULL; ++ return 1; ++ } else if ( cmd->command == STARDICT::CMD_SELECT_QUERY || cmd->command == STARDICT::CMD_SMART_QUERY) { ++ on_floatwin_lookup_end_.emit(cmd->lookup_response, cmd->seq); ++ save_cache_lookup_response(cmd->data, cmd->lookup_response); ++ cmd->lookup_response = NULL; ++ return 1; ++ } ++ cmd->reading_status = 6; ++ reading_type_ = READ_STRING; ++ } else { ++ struct STARDICT::LookupResponse::DictResponse::DictResult *dict_result = new STARDICT::LookupResponse::DictResponse::DictResult(); ++ dict_result->bookname = buf; ++ cmd->lookup_response->dict_response.dict_result_list.push_back(dict_result); ++ cmd->reading_status = 3; ++ } ++ } else if (cmd->reading_status == 3) { // Read word. ++ if (*buf == '\0') { ++ g_free(buf); ++ cmd->reading_status = 2; ++ } else { ++ struct STARDICT::LookupResponse::DictResponse::DictResult::WordResult *word_result = new STARDICT::LookupResponse::DictResponse::DictResult::WordResult(); ++ word_result->word = buf; ++ cmd->lookup_response->dict_response.dict_result_list.back()->word_result_list.push_back(word_result);; ++ cmd->reading_status = 4; ++ reading_type_ = READ_SIZE; ++ size_data = (char *)g_malloc(sizeof(guint32)); ++ size_count = size_left = sizeof(guint32); ++ } ++ } else if (cmd->reading_status == 4) { ++ guint32 datasize = g_ntohl(get_uint32(buf)); ++ memcpy(buf, &datasize, sizeof(guint32)); ++ if (datasize == 0) { ++ g_free(buf); ++ cmd->reading_status = 3; ++ reading_type_ = READ_STRING; ++ } else { ++ cmd->reading_status = 5; ++ size_data = (char *)g_realloc(buf, datasize + sizeof(guint32)); ++ size_count = datasize + sizeof(guint32); ++ size_left = datasize; ++ } ++ } else if (cmd->reading_status == 5) { ++ cmd->lookup_response->dict_response.dict_result_list.back()->word_result_list.back()->datalist.push_back(buf); ++ cmd->reading_status = 4; ++ size_data = (char *)g_malloc(sizeof(guint32)); ++ size_count = size_left = sizeof(guint32); ++ } else if (cmd->reading_status == 6) { ++ if (strcmp(buf, "d") == 0) { ++ cmd->reading_status = 8; ++ cmd->lookup_response->listtype = STARDICT::LookupResponse::ListType_Tree; ++ cmd->lookup_response->wordtree = new std::list<STARDICT::LookupResponse::WordTreeElement *>; ++ } else { ++ cmd->reading_status = 7; ++ if (strcmp(buf, "r") == 0) ++ cmd->lookup_response->listtype = STARDICT::LookupResponse::ListType_Rule_List; ++ else if (strcmp(buf, "g") == 0) ++ cmd->lookup_response->listtype = STARDICT::LookupResponse::ListType_Regex_List; ++ else if (strcmp(buf, "f") == 0) ++ cmd->lookup_response->listtype = STARDICT::LookupResponse::ListType_Fuzzy_List; ++ else ++ cmd->lookup_response->listtype = STARDICT::LookupResponse::ListType_List; ++ cmd->lookup_response->wordlist = new std::list<char *>; ++ } ++ g_free(buf); ++ } else if (cmd->reading_status == 7) { ++ if (*buf == '\0') { ++ g_free(buf); ++ on_lookup_end_.emit(cmd->lookup_response, cmd->seq); ++ save_cache_lookup_response(cmd->data, cmd->lookup_response); ++ cmd->lookup_response = NULL; ++ return 1; ++ } else { ++ cmd->lookup_response->wordlist->push_back(buf); ++ } ++ } else if (cmd->reading_status == 8) { ++ if (*buf == '\0') { ++ g_free(buf); ++ on_lookup_end_.emit(cmd->lookup_response, cmd->seq); ++ save_cache_lookup_response(cmd->data, cmd->lookup_response); ++ cmd->lookup_response = NULL; ++ return 1; ++ } else { ++ STARDICT::LookupResponse::WordTreeElement *element = new STARDICT::LookupResponse::WordTreeElement(); ++ element->bookname = buf; ++ cmd->lookup_response->wordtree->push_back(element); ++ cmd->reading_status = 9; ++ } ++ } else if (cmd->reading_status == 9) { ++ if (*buf == '\0') { ++ g_free(buf); ++ cmd->reading_status = 8; ++ } else { ++ cmd->lookup_response->wordtree->back()->wordlist.push_back(buf); ++ } ++ } ++ return 2; ++} ++ ++bool StarDictClient::parse(gchar *line) ++{ ++ int result; ++ if (waiting_banner_) { ++ waiting_banner_ = false; ++ result = parse_banner(line); ++ g_free(line); ++ if (!result) ++ return false; ++ request_command(); ++ return true; ++ } ++ STARDICT::Cmd* cmd = cmdlist.front(); ++ switch (cmd->command) { ++ case STARDICT::CMD_CLIENT: ++ result = parse_command_client(line); ++ g_free(line); ++ break; ++ case STARDICT::CMD_AUTH: ++ result = parse_command_auth(line); ++ g_free(line); ++ break; ++ case STARDICT::CMD_REGISTER: ++ result = parse_command_register(line); ++ g_free(line); ++ break; ++ case STARDICT::CMD_GET_DICT_MASK: ++ result = parse_command_getdictmask(cmd, line); ++ break; ++ case STARDICT::CMD_SET_DICT_MASK: ++ result = parse_command_setdictmask(line); ++ break; ++ case STARDICT::CMD_DIR_INFO: ++ result = parse_command_dirinfo(cmd, line); ++ break; ++ case STARDICT::CMD_DICT_INFO: ++ result = parse_command_dictinfo(cmd, line); ++ break; ++ case STARDICT::CMD_MAX_DICT_COUNT: ++ result = parse_command_maxdictcount(cmd, line); ++ break; ++ case STARDICT::CMD_DEFINE: ++ case STARDICT::CMD_LOOKUP: ++ case STARDICT::CMD_SELECT_QUERY: ++ case STARDICT::CMD_SMART_QUERY: ++ result = parse_dict_result(cmd, line); ++ break; ++ case STARDICT::CMD_PREVIOUS: ++ case STARDICT::CMD_NEXT: ++ result = parse_wordlist(cmd, line); ++ break; ++ case STARDICT::CMD_QUIT: ++ result = parse_command_quit(line); ++ g_free(line); ++ break; ++ default: ++ result = 0; ++ g_free(line); ++ break; ++ } ++ if (result == 0) ++ return false; ++ if (result == 1) { ++ delete cmd; ++ cmdlist.pop_front(); ++ if (cmdlist.empty()) { ++ cmdlist.push_back(new STARDICT::Cmd(STARDICT::CMD_QUIT)); ++ } ++ request_command(); ++ } ++ return true; ++} +diff -Nur stardict-3.0.1.orig//src/lib/stddict.cpp stardict-3.0.1/src/lib/stddict.cpp +--- stardict-3.0.1.orig//src/lib/stddict.cpp 2007-10-30 04:06:07.000000000 -0500 ++++ stardict-3.0.1/src/lib/stddict.cpp 2010-05-24 00:53:36.377667178 -0500 +@@ -39,6 +39,7 @@ + #include "stddict.hpp" + #include <algorithm> + #include "getuint32.h" ++#include <cstring> + + static inline gint stardict_strcmp(const gchar *s1, const gchar *s2) + { +diff -Nur stardict-3.0.1.orig//src/lib/stddict.cpp~ stardict-3.0.1/src/lib/stddict.cpp~ +--- stardict-3.0.1.orig//src/lib/stddict.cpp~ 1969-12-31 18:00:00.000000000 -0600 ++++ stardict-3.0.1/src/lib/stddict.cpp~ 2007-10-30 04:06:07.000000000 -0500 +@@ -0,0 +1,3263 @@ ++/* ++ * This file part of StarDict - A international dictionary for GNOME. ++ * http://stardict.sourceforge.net ++ * ++ * 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 3 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 Library 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. ++ */ ++ ++/* ++ * Implementation of class to work with standard StarDict's dictionaries ++ * lookup word, get articles and so on. ++ * ++ * Notice: read doc/DICTFILE_FORMAT for the dictionary ++ * file's format information! ++ */ ++#ifdef HAVE_CONFIG_H ++# include "config.h" ++#endif ++ ++#include <glib/gi18n.h> ++#include <glib/gstdio.h> ++ ++#include "common.hpp" ++#include "distance.h" ++#include "kmp.h" ++#include "mapfile.hpp" ++ ++#include "stddict.hpp" ++#include <algorithm> ++#include "getuint32.h" ++ ++static inline gint stardict_strcmp(const gchar *s1, const gchar *s2) ++{ ++ gint a=g_ascii_strcasecmp(s1, s2); ++ if (a == 0) ++ return strcmp(s1, s2); ++ else ++ return a; ++} ++ ++static gint stardict_collate(const gchar *str1, const gchar *str2, CollateFunctions func) ++{ ++ gint x = utf8_collate(str1, str2, func); ++ if (x == 0) ++ return strcmp(str1, str2); ++ else ++ return x; ++} ++ ++gint stardict_server_collate(const gchar *str1, const gchar *str2, int EnableCollationLevel, CollateFunctions func, int servercollatefunc) ++{ ++ if (EnableCollationLevel == 0) ++ return stardict_strcmp(str1, str2); ++ if (EnableCollationLevel == 1) ++ return stardict_collate(str1, str2, func); ++ if (servercollatefunc == 0) ++ return stardict_strcmp(str1, str2); ++ return stardict_collate(str1, str2, (CollateFunctions)(servercollatefunc-1)); ++} ++ ++gint stardict_casecmp(const gchar *s1, const gchar *s2, int EnableCollationLevel, CollateFunctions func, int servercollatefunc) ++{ ++ if (EnableCollationLevel == 0) ++ return g_ascii_strcasecmp(s1, s2); ++ if (EnableCollationLevel == 1) ++ return utf8_collate(s1, s2, func); ++ if (servercollatefunc == 0) ++ return g_ascii_strcasecmp(s1, s2); ++ return utf8_collate(s1, s2, (CollateFunctions)(servercollatefunc-1)); ++} ++ ++static inline gint prefix_match (const gchar *s1, const gchar *s2) ++{ ++ gint ret=-1; ++ gunichar u1, u2; ++ do { ++ u1 = g_utf8_get_char(s1); ++ u2 = g_utf8_get_char(s2); ++ s1 = g_utf8_next_char(s1); ++ s2 = g_utf8_next_char(s2); ++ ret++; ++ } while (u1 && g_unichar_tolower(u1) == g_unichar_tolower(u2)); ++ return ret; ++} ++ ++static inline bool bIsVowel(gchar inputchar) ++{ ++ gchar ch = g_ascii_toupper(inputchar); ++ return( ch=='A' || ch=='E' || ch=='I' || ch=='O' || ch=='U' ); ++} ++ ++ ++bool bIsPureEnglish(const gchar *str) ++{ ++ // i think this should work even when it is UTF8 string :). ++ for (int i=0; str[i]!=0; i++) ++ //if(str[i]<0) ++ //if(str[i]<32 || str[i]>126) // tab equal 9,so this is not OK. ++ // Better use isascii() but not str[i]<0 while char is default unsigned in arm ++ if (!isascii(str[i])) ++ return false; ++ return true; ++} ++ ++class offset_index : public index_file { ++public: ++ offset_index(); ++ ~offset_index(); ++ bool load(const std::string& url, gulong wc, gulong fsize, ++ bool CreateCacheFile, int EnableCollationLevel, ++ CollateFunctions _CollateFunction, show_progress_t *sp); ++ void get_data(glong idx); ++ const gchar *get_key_and_data(glong idx); ++private: ++ const gchar *get_key(glong idx); ++ bool lookup(const char *str, glong &idx, glong &idx_suggest); ++ ++ static const gint ENTR_PER_PAGE=32; ++ ++ cache_file oft_file; ++ FILE *idxfile; ++ gulong npages; ++ ++ gchar wordentry_buf[256+sizeof(guint32)*2]; // The length of "word_str" should be less than 256. See doc/DICTFILE_FORMAT. ++ struct index_entry { ++ glong idx; ++ std::string keystr; ++ void assign(glong i, const std::string& str) { ++ idx=i; ++ keystr.assign(str); ++ } ++ }; ++ index_entry first, last, middle, real_last; ++ ++ struct page_entry { ++ gchar *keystr; ++ guint32 off, size; ++ }; ++ std::vector<gchar> page_data; ++ struct page_t { ++ glong idx; ++ page_entry entries[ENTR_PER_PAGE]; ++ ++ page_t(): idx(-1) {} ++ void fill(gchar *data, gint nent, glong idx_); ++ } page; ++ gulong load_page(glong page_idx); ++ const gchar *read_first_on_page_key(glong page_idx); ++ const gchar *get_first_on_page_key(glong page_idx); ++}; ++ ++class wordlist_index : public index_file { ++public: ++ wordlist_index(); ++ ~wordlist_index(); ++ bool load(const std::string& url, gulong wc, gulong fsize, ++ bool CreateCacheFile, int EnableCollationLevel, ++ CollateFunctions _CollateFunction, show_progress_t *sp); ++ void get_data(glong idx); ++ const gchar *get_key_and_data(glong idx); ++private: ++ const gchar *get_key(glong idx); ++ bool lookup(const char *str, glong &idx, glong &idx_suggest); ++ ++ gchar *idxdatabuf; ++ std::vector<gchar *> wordlist; ++}; ++ ++offset_index::offset_index() : oft_file(CacheFileType_oft) ++{ ++ clt_file = NULL; ++ idxfile = NULL; ++} ++ ++offset_index::~offset_index() ++{ ++ delete clt_file; ++ if (idxfile) ++ fclose(idxfile); ++} ++ ++void offset_index::page_t::fill(gchar *data, gint nent, glong idx_) ++{ ++ idx=idx_; ++ gchar *p=data; ++ glong len; ++ for (gint i=0; i<nent; ++i) { ++ entries[i].keystr=p; ++ len=strlen(p); ++ p+=len+1; ++ entries[i].off=g_ntohl(get_uint32(p)); ++ p+=sizeof(guint32); ++ entries[i].size=g_ntohl(get_uint32(p)); ++ p+=sizeof(guint32); ++ } ++} ++ ++inline const gchar *offset_index::read_first_on_page_key(glong page_idx) ++{ ++ fseek(idxfile, oft_file.wordoffset[page_idx], SEEK_SET); ++ guint32 page_size=oft_file.wordoffset[page_idx+1]-oft_file.wordoffset[page_idx]; ++ gulong minsize = sizeof(wordentry_buf); ++ if (page_size < minsize) ++ minsize = page_size; ++ fread(wordentry_buf, minsize, 1, idxfile); //TODO: check returned values, deal with word entry that strlen>255. ++ return wordentry_buf; ++} ++ ++inline const gchar *offset_index::get_first_on_page_key(glong page_idx) ++{ ++ if (page_idx<middle.idx) { ++ if (page_idx==first.idx) ++ return first.keystr.c_str(); ++ return read_first_on_page_key(page_idx); ++ } else if (page_idx>middle.idx) { ++ if (page_idx==last.idx) ++ return last.keystr.c_str(); ++ return read_first_on_page_key(page_idx); ++ } else ++ return middle.keystr.c_str(); ++} ++ ++cache_file::cache_file(CacheFileType _cachefiletype) ++{ ++ wordoffset = NULL; ++ mf = NULL; ++ cachefiletype = _cachefiletype; ++} ++ ++ ++cache_file::~cache_file() ++{ ++ if (mf) ++ delete mf; ++ else ++ g_free(wordoffset); ++} ++ ++#define OFFSETFILE_MAGIC_DATA "StarDict's oft file\nversion=2.4.8\n" ++#define COLLATIONFILE_MAGIC_DATA "StarDict's clt file\nversion=2.4.8\n" ++ ++MapFile* cache_file::get_cache_loadfile(const gchar *filename, const std::string &url, const std::string &saveurl, CollateFunctions cltfunc, glong filedatasize, int next) ++{ ++ struct stat cachestat; ++ if (g_stat(filename, &cachestat)!=0) ++ return NULL; ++ MapFile *mf = new MapFile; ++ if (!mf->open(filename, cachestat.st_size)) { ++ delete mf; ++ return NULL; ++ } ++ ++ gchar *p = mf->begin(); ++ gboolean has_prefix; ++ if (cachefiletype == CacheFileType_oft) ++ has_prefix = g_str_has_prefix(p, OFFSETFILE_MAGIC_DATA); ++ else ++ has_prefix = g_str_has_prefix(p, COLLATIONFILE_MAGIC_DATA); ++ if (!has_prefix) { ++ delete mf; ++ return NULL; ++ } ++ if (cachefiletype == CacheFileType_oft) ++ p+= sizeof(OFFSETFILE_MAGIC_DATA)-1-1; ++ else ++ p+= sizeof(COLLATIONFILE_MAGIC_DATA)-1-1; ++ gchar *p2; ++ p2 = strstr(p, "\nurl="); ++ if (!p2) { ++ delete mf; ++ return NULL; ++ } ++ p2+=sizeof("\nurl=")-1; ++ gchar *p3; ++ p3 = strchr(p2, '\n'); ++ if (!p3) { ++ delete mf; ++ return NULL; ++ } ++ gchar *tmpstr; ++ tmpstr = (gchar *)g_memdup(p2, p3-p2+1); ++ tmpstr[p3-p2] = '\0'; ++ if (saveurl == tmpstr) { ++ g_free(tmpstr); ++ if (cachefiletype == CacheFileType_clt) { ++ p2 = strstr(p, "\nfunc="); ++ if (!p2) { ++ delete mf; ++ return NULL; ++ } ++ p2+=sizeof("\nfunc=")-1; ++ p3 = strchr(p2, '\n'); ++ if (!p3) { ++ delete mf; ++ return NULL; ++ } ++ tmpstr = (gchar *)g_memdup(p2, p3-p2+1); ++ tmpstr[p3-p2] = '\0'; ++ if (atoi(tmpstr)!=cltfunc) { ++ g_free(tmpstr); ++ delete mf; ++ return NULL; ++ } ++ g_free(tmpstr); ++ } ++ if (cachestat.st_size!=glong(filedatasize + strlen(mf->begin()) +1)) { ++ delete mf; ++ return NULL; ++ } ++ struct stat idxstat; ++ if (g_stat(url.c_str(), &idxstat)!=0) { ++ delete mf; ++ return NULL; ++ } ++ if (cachestat.st_mtime<idxstat.st_mtime) { ++ delete mf; ++ return NULL; ++ } ++ //g_print("Using map file: %s\n", filename); ++ return mf; ++ } ++ g_free(tmpstr); ++ delete mf; ++ gchar *basename = g_path_get_basename(saveurl.c_str()); ++ p = strrchr(basename, '.'); ++ if (!p) { ++ g_free(basename); ++ return NULL; ++ } ++ *p='\0'; ++ gchar *extendname = p+1; ++ gchar *dirname = g_path_get_dirname(filename); ++ gchar *nextfilename; ++ if (cachefiletype == CacheFileType_oft) ++ nextfilename = g_strdup_printf("%s" G_DIR_SEPARATOR_S "%s(%d).%s.oft", dirname, basename, next, extendname); ++ else if (cachefiletype == CacheFileType_clt) ++ nextfilename = g_strdup_printf("%s" G_DIR_SEPARATOR_S "%s(%d).%s.clt", dirname, basename, next, extendname); ++ else ++ nextfilename = g_strdup_printf("%s" G_DIR_SEPARATOR_S "%s(%d).%s.%d.clt", dirname, basename, next, extendname, cltfunc); ++ MapFile *out = get_cache_loadfile(nextfilename, url, saveurl, cltfunc, filedatasize, next+1); ++ g_free(basename); ++ g_free(dirname); ++ g_free(nextfilename); ++ return out; ++} ++ ++bool cache_file::load_cache(const std::string& url, const std::string& saveurl, CollateFunctions cltfunc, glong filedatasize) ++{ ++ std::string oftfilename; ++ if (cachefiletype == CacheFileType_oft) ++ oftfilename=saveurl+".oft"; ++ else if (cachefiletype == CacheFileType_clt) ++ oftfilename=saveurl+".clt"; ++ else { ++ gchar *func = g_strdup_printf("%d", cltfunc); ++ oftfilename=saveurl+'.'+func+".clt"; ++ g_free(func); ++ } ++ for (int i=0;i<2;i++) { ++ if (i==1) { ++ if (!get_cache_filename(saveurl, oftfilename, false, cltfunc)) ++ break; ++ } ++ mf = get_cache_loadfile(oftfilename.c_str(), url, saveurl, cltfunc, filedatasize, 2); ++ if (!mf) ++ continue; ++ wordoffset = (guint32 *)(mf->begin()+strlen(mf->begin())+1); ++ return true; ++ } ++ return false; ++} ++ ++bool cache_file::get_cache_filename(const std::string& url, std::string &cachefilename, bool create, CollateFunctions cltfunc) ++{ ++ if (create) { ++ if (!g_file_test(g_get_user_cache_dir(), G_FILE_TEST_EXISTS) && ++ g_mkdir(g_get_user_cache_dir(), 0700)==-1) ++ return false; ++ } ++ ++ std::string cache_dir=g_get_user_cache_dir(); ++ cache_dir += G_DIR_SEPARATOR_S "stardict"; ++ ++ if (create) { ++ if (!g_file_test(cache_dir.c_str(), G_FILE_TEST_EXISTS)) { ++ if (g_mkdir(cache_dir.c_str(), 0700)==-1) ++ return false; ++ } else if (!g_file_test(cache_dir.c_str(), G_FILE_TEST_IS_DIR)) ++ return false; ++ } ++ ++ gchar *base=g_path_get_basename(url.c_str()); ++ if (cachefiletype == CacheFileType_oft) { ++ cachefilename = cache_dir+G_DIR_SEPARATOR_S+base+".oft"; ++ } else if (cachefiletype == CacheFileType_clt) { ++ cachefilename = cache_dir+G_DIR_SEPARATOR_S+base+".clt"; ++ } else { ++ gchar *func = g_strdup_printf("%d", cltfunc); ++ cachefilename = cache_dir+G_DIR_SEPARATOR_S+base+'.'+func+".clt"; ++ g_free(func); ++ } ++ g_free(base); ++ return true; ++} ++ ++FILE* cache_file::get_cache_savefile(const gchar *filename, const std::string &url, int next, std::string &cfilename, CollateFunctions cltfunc) ++{ ++ cfilename = filename; ++ struct stat oftstat; ++ if (g_stat(filename, &oftstat)!=0) { ++ return fopen(filename, "wb"); ++ } ++ MapFile mf; ++ if (!mf.open(filename, oftstat.st_size)) { ++ return fopen(filename, "wb"); ++ } ++ gchar *p = mf.begin(); ++ bool has_prefix; ++ if (cachefiletype == CacheFileType_oft) ++ has_prefix = g_str_has_prefix(p, OFFSETFILE_MAGIC_DATA); ++ else ++ has_prefix = g_str_has_prefix(p, COLLATIONFILE_MAGIC_DATA); ++ if (!has_prefix) { ++ mf.close(); ++ return fopen(filename, "wb"); ++ } ++ if (cachefiletype == CacheFileType_oft) ++ p+= sizeof(OFFSETFILE_MAGIC_DATA)-1-1; ++ else ++ p+= sizeof(COLLATIONFILE_MAGIC_DATA)-1-1; ++ gchar *p2; ++ p2 = strstr(p, "\nurl="); ++ if (!p2) { ++ mf.close(); ++ return fopen(filename, "wb"); ++ } ++ p2+=sizeof("\nurl=")-1; ++ gchar *p3; ++ p3 = strchr(p2, '\n'); ++ if (!p3) { ++ mf.close(); ++ return fopen(filename, "wb"); ++ } ++ gchar *tmpstr; ++ tmpstr = (gchar *)g_memdup(p2, p3-p2+1); ++ tmpstr[p3-p2] = '\0'; ++ if (url == tmpstr) { ++ g_free(tmpstr); ++ mf.close(); ++ return fopen(filename, "wb"); ++ } ++ g_free(tmpstr); ++ mf.close(); ++ gchar *basename = g_path_get_basename(url.c_str()); ++ p = strrchr(basename, '.'); ++ if (!p) { ++ g_free(basename); ++ return NULL; ++ } ++ *p='\0'; ++ gchar *extendname = p+1; ++ gchar *dirname = g_path_get_dirname(filename); ++ gchar *nextfilename; ++ if (cachefiletype == CacheFileType_oft) ++ nextfilename = g_strdup_printf("%s" G_DIR_SEPARATOR_S "%s(%d).%s.oft", dirname, basename, next, extendname); ++ else if (cachefiletype == CacheFileType_clt) ++ nextfilename = g_strdup_printf("%s" G_DIR_SEPARATOR_S "%s(%d).%s.clt", dirname, basename, next, extendname); ++ else ++ nextfilename = g_strdup_printf("%s" G_DIR_SEPARATOR_S "%s(%d).%s.%d.clt", dirname, basename, next, extendname, cltfunc); ++ FILE *out = get_cache_savefile(nextfilename, url, next+1, cfilename, cltfunc); ++ g_free(basename); ++ g_free(dirname); ++ g_free(nextfilename); ++ return out; ++} ++ ++bool cache_file::save_cache(const std::string& url, CollateFunctions cltfunc, gulong npages) ++{ ++ std::string oftfilename; ++ if (cachefiletype == CacheFileType_oft) { ++ oftfilename=url+".oft"; ++ } else if (cachefiletype == CacheFileType_clt) { ++ oftfilename=url+".clt"; ++ } else { ++ gchar *func = g_strdup_printf("%d", cltfunc); ++ oftfilename=url+'.'+func+".clt"; ++ g_free(func); ++ } ++ for (int i=0;i<2;i++) { ++ if (i==1) { ++ if (!get_cache_filename(url, oftfilename, true, cltfunc)) ++ break; ++ } ++ std::string cfilename; ++ FILE *out= get_cache_savefile(oftfilename.c_str(), url, 2, cfilename, cltfunc); ++ if (!out) ++ continue; ++ if (cachefiletype == CacheFileType_oft) ++ fwrite(OFFSETFILE_MAGIC_DATA, 1, sizeof(OFFSETFILE_MAGIC_DATA)-1, out); ++ else ++ fwrite(COLLATIONFILE_MAGIC_DATA, 1, sizeof(COLLATIONFILE_MAGIC_DATA)-1, out); ++ fwrite("url=", 1, sizeof("url=")-1, out); ++ fwrite(url.c_str(), 1, url.length(), out); ++ if (cachefiletype == CacheFileType_clt) { ++#ifdef _MSC_VER ++ fprintf_s(out, "\nfunc=%d", cltfunc); ++#else ++ fprintf(out, "\nfunc=%d", cltfunc); ++#endif ++ } ++ fwrite("\n", 1, 2, out); ++ fwrite(wordoffset, sizeof(guint32), npages, out); ++ fclose(out); ++ g_print("Save cache file: %s\n", cfilename.c_str()); ++ return true; ++ } ++ return false; ++} ++ ++collation_file::collation_file(idxsyn_file *_idx_file, CacheFileType _cachefiletype) : cache_file(_cachefiletype) ++{ ++ idx_file = _idx_file; ++} ++ ++const gchar *collation_file::GetWord(glong idx) ++{ ++ return idx_file->get_key(wordoffset[idx]); ++} ++ ++glong collation_file::GetOrigIndex(glong cltidx) ++{ ++ return wordoffset[cltidx]; ++} ++ ++bool collation_file::lookup(const char *sWord, glong &idx, glong &idx_suggest) ++{ ++ bool bFound=false; ++ glong iTo=idx_file->wordcount-1; ++ if (stardict_collate(sWord, GetWord(0), CollateFunction)<0) { ++ idx = 0; ++ idx_suggest = 0; ++ } else if (stardict_collate(sWord, GetWord(iTo), CollateFunction) >0) { ++ idx = INVALID_INDEX; ++ idx_suggest = iTo; ++ } else { ++ glong iThisIndex=0; ++ glong iFrom=0; ++ gint cmpint; ++ while (iFrom<=iTo) { ++ iThisIndex=(iFrom+iTo)/2; ++ cmpint = stardict_collate(sWord, GetWord(iThisIndex), CollateFunction); ++ if (cmpint>0) ++ iFrom=iThisIndex+1; ++ else if (cmpint<0) ++ iTo=iThisIndex-1; ++ else { ++ bFound=true; ++ break; ++ } ++ } ++ if (!bFound) { ++ idx = iFrom; //next ++ idx_suggest = iFrom; ++ gint best, back; ++ best = prefix_match (sWord, GetWord(idx_suggest)); ++ for (;;) { ++ if ((iTo=idx_suggest-1) < 0) ++ break; ++ back = prefix_match (sWord, GetWord(iTo)); ++ if (!back || back < best) ++ break; ++ best = back; ++ idx_suggest = iTo; ++ } ++ } else { ++ idx = iThisIndex; ++ idx_suggest = iThisIndex; ++ } ++ } ++ return bFound; ++} ++ ++struct sort_collation_index_user_data { ++ idxsyn_file *idx_file; ++ CollateFunctions cltfunc; ++}; ++ ++static gint sort_collation_index(gconstpointer a, gconstpointer b, gpointer user_data) ++{ ++ sort_collation_index_user_data *data = (sort_collation_index_user_data*)user_data; ++ gchar *str1 = g_strdup(data->idx_file->get_key(*((guint32 *)a))); ++ const gchar *str2 = data->idx_file->get_key(*((guint32 *)b)); ++ gint x = stardict_collate(str1, str2, data->cltfunc); ++ g_free(str1); ++ if (x==0) ++ return *((guint32 *)a) - *((guint32 *)b); ++ else ++ return x; ++} ++ ++idxsyn_file::idxsyn_file() ++{ ++ memset(clt_files, 0, sizeof(clt_files)); ++} ++ ++const gchar *idxsyn_file::getWord(glong idx, int EnableCollationLevel, int servercollatefunc) ++{ ++ if (EnableCollationLevel == 0) ++ return get_key(idx); ++ if (EnableCollationLevel == 1) ++ return clt_file->GetWord(idx); ++ if (servercollatefunc == 0) ++ return get_key(idx); ++ collate_load((CollateFunctions)(servercollatefunc-1)); ++ return clt_files[servercollatefunc-1]->GetWord(idx); ++} ++ ++bool idxsyn_file::Lookup(const char *str, glong &idx, glong &idx_suggest, int EnableCollationLevel, int servercollatefunc) ++{ ++ if (EnableCollationLevel == 0) ++ return lookup(str, idx, idx_suggest); ++ if (EnableCollationLevel == 1) ++ return clt_file->lookup(str, idx, idx_suggest); ++ if (servercollatefunc == 0) ++ return lookup(str, idx, idx_suggest); ++ collate_load((CollateFunctions)(servercollatefunc-1)); ++ return clt_files[servercollatefunc-1]->lookup(str, idx, idx_suggest); ++} ++ ++void idxsyn_file::collate_sort(const std::string& url, ++ const std::string& saveurl, ++ CollateFunctions collf, ++ show_progress_t *sp) ++{ ++ clt_file = new collation_file(this, CacheFileType_clt); ++ clt_file->CollateFunction = collf; ++ if (!clt_file->load_cache(url, saveurl, collf, wordcount*sizeof(guint32))) { ++ sp->notify_about_start(_("Sorting, please wait...")); ++ clt_file->wordoffset = (guint32 *)g_malloc(wordcount*sizeof(guint32)); ++ for (glong i=0; i<wordcount; i++) ++ clt_file->wordoffset[i] = i; ++ sort_collation_index_user_data data; ++ data.idx_file = this; ++ data.cltfunc = collf; ++ g_qsort_with_data(clt_file->wordoffset, wordcount, sizeof(guint32), sort_collation_index, &data); ++ if (!clt_file->save_cache(saveurl, collf, wordcount)) ++ g_printerr("Cache update failed.\n"); ++ } ++} ++ ++void idxsyn_file::collate_save_info(const std::string& _url, const std::string& _saveurl) ++{ ++ url = _url; ++ saveurl = _saveurl; ++} ++ ++void idxsyn_file::collate_load(CollateFunctions collf) ++{ ++ if (clt_files[collf]) ++ return; ++ clt_files[collf] = new collation_file(this, CacheFileType_server_clt); ++ clt_files[collf]->CollateFunction = collf; ++ if (!clt_files[collf]->load_cache(url, saveurl, collf, wordcount*sizeof(guint32))) { ++ clt_files[collf]->wordoffset = (guint32 *)g_malloc(wordcount*sizeof(guint32)); ++ for (glong i=0; i<wordcount; i++) ++ clt_files[collf]->wordoffset[i] = i; ++ sort_collation_index_user_data data; ++ data.idx_file = this; ++ data.cltfunc = collf; ++ g_qsort_with_data(clt_files[collf]->wordoffset, wordcount, sizeof(guint32), sort_collation_index, &data); ++ if (!clt_files[collf]->save_cache(saveurl, collf, wordcount)) ++ g_printerr("Cache update failed.\n"); ++ } ++} ++ ++bool offset_index::load(const std::string& url, gulong wc, gulong fsize, ++ bool CreateCacheFile, int EnableCollationLevel, ++ CollateFunctions _CollateFunction, show_progress_t *sp) ++{ ++ wordcount=wc; ++ npages=(wc-1)/ENTR_PER_PAGE+2; ++ if (!oft_file.load_cache(url, url, _CollateFunction, npages*sizeof(guint32))) { ++ MapFile map_file; ++ if (!map_file.open(url.c_str(), fsize)) ++ return false; ++ const gchar *idxdatabuffer=map_file.begin(); ++ oft_file.wordoffset = (guint32 *)g_malloc(npages*sizeof(guint32)); ++ const gchar *p1 = idxdatabuffer; ++ gulong index_size; ++ guint32 j=0; ++ for (guint32 i=0; i<wc; i++) { ++ index_size=strlen(p1) +1 + 2*sizeof(guint32); ++ if (i % ENTR_PER_PAGE==0) { ++ oft_file.wordoffset[j]=p1-idxdatabuffer; ++ ++j; ++ } ++ p1 += index_size; ++ } ++ oft_file.wordoffset[j]=p1-idxdatabuffer; ++ map_file.close(); ++ if (CreateCacheFile) { ++ if (!oft_file.save_cache(url, _CollateFunction, npages)) ++ g_printerr("Cache update failed.\n"); ++ } ++ } ++ ++ if (!(idxfile = fopen(url.c_str(), "rb"))) { ++ return false; ++ } ++ ++ first.assign(0, read_first_on_page_key(0)); ++ last.assign(npages-2, read_first_on_page_key(npages-2)); ++ middle.assign((npages-2)/2, read_first_on_page_key((npages-2)/2)); ++ real_last.assign(wc-1, get_key(wc-1)); ++ ++ if (EnableCollationLevel == 0) { ++ } else if (EnableCollationLevel == 1) { ++ collate_sort(url, url, _CollateFunction, sp); ++ } else if (EnableCollationLevel == 2) { ++ collate_save_info(url, url); ++ } ++ ++ return true; ++} ++ ++inline gulong offset_index::load_page(glong page_idx) ++{ ++ gulong nentr=ENTR_PER_PAGE; ++ if (page_idx==glong(npages-2)) ++ if ((nentr=wordcount%ENTR_PER_PAGE)==0) ++ nentr=ENTR_PER_PAGE; ++ ++ ++ if (page_idx!=page.idx) { ++ page_data.resize(oft_file.wordoffset[page_idx+1]-oft_file.wordoffset[page_idx]); ++ fseek(idxfile, oft_file.wordoffset[page_idx], SEEK_SET); ++ fread(&page_data[0], 1, page_data.size(), idxfile); ++ page.fill(&page_data[0], nentr, page_idx); ++ } ++ ++ return nentr; ++} ++ ++const gchar *offset_index::get_key(glong idx) ++{ ++ load_page(idx/ENTR_PER_PAGE); ++ glong idx_in_page=idx%ENTR_PER_PAGE; ++ wordentry_offset=page.entries[idx_in_page].off; ++ wordentry_size=page.entries[idx_in_page].size; ++ ++ return page.entries[idx_in_page].keystr; ++} ++ ++void offset_index::get_data(glong idx) ++{ ++ get_key(idx); ++} ++ ++const gchar *offset_index::get_key_and_data(glong idx) ++{ ++ return get_key(idx); ++} ++ ++bool offset_index::lookup(const char *str, glong &idx, glong &idx_suggest) ++{ ++ bool bFound=false; ++ glong iFrom; ++ glong iTo=npages-2; ++ gint cmpint; ++ glong iThisIndex; ++ if (stardict_strcmp(str, first.keystr.c_str())<0) { ++ idx = 0; ++ idx_suggest = 0; ++ return false; ++ } else if (stardict_strcmp(str, real_last.keystr.c_str()) >0) { ++ idx = INVALID_INDEX; ++ idx_suggest = iTo; ++ return false; ++ } else { ++ iFrom=0; ++ iThisIndex=0; ++ while (iFrom<=iTo) { ++ iThisIndex=(iFrom+iTo)/2; ++ cmpint = stardict_strcmp(str, get_first_on_page_key(iThisIndex)); ++ if (cmpint>0) ++ iFrom=iThisIndex+1; ++ else if (cmpint<0) ++ iTo=iThisIndex-1; ++ else { ++ bFound=true; ++ break; ++ } ++ } ++ if (!bFound) { ++ idx = iTo; //prev ++ } else { ++ idx = iThisIndex; ++ } ++ } ++ if (!bFound) { ++ gulong netr=load_page(idx); ++ iFrom=1; // Needn't search the first word anymore. ++ iTo=netr-1; ++ iThisIndex=0; ++ while (iFrom<=iTo) { ++ iThisIndex=(iFrom+iTo)/2; ++ cmpint = stardict_strcmp(str, page.entries[iThisIndex].keystr); ++ if (cmpint>0) ++ iFrom=iThisIndex+1; ++ else if (cmpint<0) ++ iTo=iThisIndex-1; ++ else { ++ bFound=true; ++ break; ++ } ++ } ++ idx*=ENTR_PER_PAGE; ++ if (!bFound) { ++ idx += iFrom; //next ++ idx_suggest = idx; ++ gint best, back; ++ best = prefix_match (str, page.entries[idx_suggest % ENTR_PER_PAGE].keystr); ++ for (;;) { ++ if ((iTo=idx_suggest-1) < 0) ++ break; ++ if (idx_suggest % ENTR_PER_PAGE == 0) ++ load_page(iTo / ENTR_PER_PAGE); ++ back = prefix_match (str, page.entries[iTo % ENTR_PER_PAGE].keystr); ++ if (!back || back < best) ++ break; ++ best = back; ++ idx_suggest = iTo; ++ } ++ } else { ++ idx += iThisIndex; ++ idx_suggest = idx; ++ } ++ } else { ++ idx*=ENTR_PER_PAGE; ++ idx_suggest = idx; ++ } ++ return bFound; ++} ++ ++wordlist_index::wordlist_index() ++{ ++ clt_file = NULL; ++ idxdatabuf = NULL; ++} ++ ++wordlist_index::~wordlist_index() ++{ ++ delete clt_file; ++ g_free(idxdatabuf); ++} ++ ++bool wordlist_index::load(const std::string& url, gulong wc, gulong fsize, ++ bool CreateCacheFile, int EnableCollationLevel, ++ CollateFunctions _CollateFunction, show_progress_t *sp) ++{ ++ wordcount=wc; ++ gzFile in = gzopen(url.c_str(), "rb"); ++ if (in == NULL) ++ return false; ++ ++ idxdatabuf = (gchar *)g_malloc(fsize); ++ ++ gulong len = gzread(in, idxdatabuf, fsize); ++ gzclose(in); ++ if (len < 0) ++ return false; ++ ++ if (len != fsize) ++ return false; ++ ++ wordlist.resize(wc+1); ++ gchar *p1 = idxdatabuf; ++ guint32 i; ++ for (i=0; i<wc; i++) { ++ wordlist[i] = p1; ++ p1 += strlen(p1) +1 + 2*sizeof(guint32); ++ } ++ wordlist[wc] = p1; ++ ++ if (EnableCollationLevel == 0) { ++ } else { ++ std::string saveurl = url; ++ saveurl.erase(saveurl.length()-sizeof(".gz")+1, sizeof(".gz")-1); ++ if (EnableCollationLevel == 1) { ++ collate_sort(url, saveurl, _CollateFunction, sp); ++ } else if (EnableCollationLevel == 2) { ++ collate_save_info(url, saveurl); ++ } ++ } ++ return true; ++} ++ ++const gchar *wordlist_index::get_key(glong idx) ++{ ++ return wordlist[idx]; ++} ++ ++void wordlist_index::get_data(glong idx) ++{ ++ gchar *p1 = wordlist[idx]+strlen(wordlist[idx])+sizeof(gchar); ++ wordentry_offset = g_ntohl(get_uint32(p1)); ++ p1 += sizeof(guint32); ++ wordentry_size = g_ntohl(get_uint32(p1)); ++} ++ ++const gchar *wordlist_index::get_key_and_data(glong idx) ++{ ++ get_data(idx); ++ return get_key(idx); ++} ++ ++bool wordlist_index::lookup(const char *str, glong &idx, glong &idx_suggest) ++{ ++ bool bFound=false; ++ glong iTo=wordlist.size()-2; ++ ++ if (stardict_strcmp(str, get_key(0))<0) { ++ idx = 0; ++ idx_suggest = 0; ++ } else if (stardict_strcmp(str, get_key(iTo)) >0) { ++ idx = INVALID_INDEX; ++ idx_suggest = iTo; ++ } else { ++ glong iThisIndex=0; ++ glong iFrom=0; ++ gint cmpint; ++ while (iFrom<=iTo) { ++ iThisIndex=(iFrom+iTo)/2; ++ cmpint = stardict_strcmp(str, get_key(iThisIndex)); ++ if (cmpint>0) ++ iFrom=iThisIndex+1; ++ else if (cmpint<0) ++ iTo=iThisIndex-1; ++ else { ++ bFound=true; ++ break; ++ } ++ } ++ if (!bFound) { ++ idx = iFrom; //next ++ idx_suggest = iFrom; ++ gint best, back; ++ best = prefix_match (str, get_key(idx_suggest)); ++ for (;;) { ++ if ((iTo=idx_suggest-1) < 0) ++ break; ++ back = prefix_match (str, get_key(iTo)); ++ if (!back || back < best) ++ break; ++ best = back; ++ idx_suggest = iTo; ++ } ++ } else { ++ idx = iThisIndex; ++ idx_suggest = iThisIndex; ++ } ++ } ++ return bFound; ++} ++ ++//=================================================================== ++void synonym_file::page_t::fill(gchar *data, gint nent, glong idx_) ++{ ++ idx=idx_; ++ gchar *p=data; ++ glong len; ++ for (gint i=0; i<nent; ++i) { ++ entries[i].keystr=p; ++ len=strlen(p); ++ p+=len+1; ++ entries[i].index=g_ntohl(get_uint32(p)); ++ p+=sizeof(guint32); ++ } ++} ++ ++synonym_file::synonym_file() : oft_file(CacheFileType_oft) ++{ ++ clt_file = NULL; ++} ++ ++synonym_file::~synonym_file() ++{ ++ delete clt_file; ++ if (synfile) ++ fclose(synfile); ++} ++ ++inline const gchar *synonym_file::read_first_on_page_key(glong page_idx) ++{ ++ fseek(synfile, oft_file.wordoffset[page_idx], SEEK_SET); ++ guint32 page_size=oft_file.wordoffset[page_idx+1]-oft_file.wordoffset[page_idx]; ++ gulong minsize = sizeof(wordentry_buf); ++ if (page_size < minsize) ++ minsize = page_size; ++ fread(wordentry_buf, minsize, 1, synfile); //TODO: check returned values, deal with word entry that strlen>255. ++ return wordentry_buf; ++} ++ ++inline const gchar *synonym_file::get_first_on_page_key(glong page_idx) ++{ ++ if (page_idx<middle.idx) { ++ if (page_idx==first.idx) ++ return first.keystr.c_str(); ++ return read_first_on_page_key(page_idx); ++ } else if (page_idx>middle.idx) { ++ if (page_idx==last.idx) ++ return last.keystr.c_str(); ++ return read_first_on_page_key(page_idx); ++ } else ++ return middle.keystr.c_str(); ++} ++ ++bool synonym_file::load(const std::string& url, gulong wc, bool CreateCacheFile, ++ int EnableCollationLevel, CollateFunctions _CollateFunction, ++ show_progress_t *sp) ++{ ++ wordcount=wc; ++ npages=(wc-1)/ENTR_PER_PAGE+2; ++ if (!oft_file.load_cache(url, url, _CollateFunction, npages*sizeof(guint32))) { ++ struct stat stats; ++ if (stat (url.c_str(), &stats) == -1) ++ return false; ++ MapFile map_file; ++ if (!map_file.open(url.c_str(), stats.st_size)) ++ return false; ++ const gchar *syndatabuffer=map_file.begin(); ++ oft_file.wordoffset = (guint32 *)g_malloc(npages*sizeof(guint32)); ++ const gchar *p1 = syndatabuffer; ++ gulong index_size; ++ guint32 j=0; ++ for (guint32 i=0; i<wc; i++) { ++ index_size=strlen(p1) +1 + sizeof(guint32); ++ if (i % ENTR_PER_PAGE==0) { ++ oft_file.wordoffset[j]=p1-syndatabuffer; ++ ++j; ++ } ++ p1 += index_size; ++ } ++ oft_file.wordoffset[j]=p1-syndatabuffer; ++ map_file.close(); ++ if (CreateCacheFile) { ++ if (!oft_file.save_cache(url, _CollateFunction, npages)) ++ g_printerr("Cache update failed.\n"); ++ } ++ } ++ ++ if (!(synfile = fopen(url.c_str(), "rb"))) { ++ return false; ++ } ++ ++ first.assign(0, read_first_on_page_key(0)); ++ last.assign(npages-2, read_first_on_page_key(npages-2)); ++ middle.assign((npages-2)/2, read_first_on_page_key((npages-2)/2)); ++ real_last.assign(wc-1, get_key(wc-1)); ++ ++ if (EnableCollationLevel == 0) { ++ } else if (EnableCollationLevel == 1) ++ collate_sort(url, url, _CollateFunction, sp); ++ else if (EnableCollationLevel == 2) { ++ collate_save_info(url, url); ++ } ++ ++ return true; ++} ++ ++inline gulong synonym_file::load_page(glong page_idx) ++{ ++ gulong nentr=ENTR_PER_PAGE; ++ if (page_idx==glong(npages-2)) ++ if ((nentr=wordcount%ENTR_PER_PAGE)==0) ++ nentr=ENTR_PER_PAGE; ++ ++ ++ if (page_idx!=page.idx) { ++ page_data.resize(oft_file.wordoffset[page_idx+1]-oft_file.wordoffset[page_idx]); ++ fseek(synfile, oft_file.wordoffset[page_idx], SEEK_SET); ++ fread(&page_data[0], 1, page_data.size(), synfile); ++ page.fill(&page_data[0], nentr, page_idx); ++ } ++ ++ return nentr; ++} ++ ++const gchar *synonym_file::get_key(glong idx) ++{ ++ load_page(idx/ENTR_PER_PAGE); ++ glong idx_in_page=idx%ENTR_PER_PAGE; ++ wordentry_index=page.entries[idx_in_page].index; ++ ++ return page.entries[idx_in_page].keystr; ++} ++ ++bool synonym_file::lookup(const char *str, glong &idx, glong &idx_suggest) ++{ ++ bool bFound=false; ++ glong iFrom; ++ glong iTo=npages-2; ++ gint cmpint; ++ glong iThisIndex; ++ if (stardict_strcmp(str, first.keystr.c_str())<0) { ++ idx = 0; ++ idx_suggest = 0; ++ return false; ++ } else if (stardict_strcmp(str, real_last.keystr.c_str()) >0) { ++ idx = INVALID_INDEX; ++ idx_suggest = iTo; ++ return false; ++ } else { ++ iFrom=0; ++ iThisIndex=0; ++ while (iFrom<=iTo) { ++ iThisIndex=(iFrom+iTo)/2; ++ cmpint = stardict_strcmp(str, get_first_on_page_key(iThisIndex)); ++ if (cmpint>0) ++ iFrom=iThisIndex+1; ++ else if (cmpint<0) ++ iTo=iThisIndex-1; ++ else { ++ bFound=true; ++ break; ++ } ++ } ++ if (!bFound) ++ idx = iTo; //prev ++ else ++ idx = iThisIndex; ++ } ++ if (!bFound) { ++ gulong netr=load_page(idx); ++ iFrom=1; // Needn't search the first word anymore. ++ iTo=netr-1; ++ iThisIndex=0; ++ while (iFrom<=iTo) { ++ iThisIndex=(iFrom+iTo)/2; ++ cmpint = stardict_strcmp(str, page.entries[iThisIndex].keystr); ++ if (cmpint>0) ++ iFrom=iThisIndex+1; ++ else if (cmpint<0) ++ iTo=iThisIndex-1; ++ else { ++ bFound=true; ++ break; ++ } ++ } ++ idx*=ENTR_PER_PAGE; ++ if (!bFound) { ++ idx += iFrom; //next ++ idx_suggest = idx; ++ gint best, back; ++ best = prefix_match (str, page.entries[idx_suggest % ENTR_PER_PAGE].keystr); ++ for (;;) { ++ if ((iTo=idx_suggest-1) < 0) ++ break; ++ if (idx_suggest % ENTR_PER_PAGE == 0) ++ load_page(iTo / ENTR_PER_PAGE); ++ back = prefix_match (str, page.entries[iTo % ENTR_PER_PAGE].keystr); ++ if (!back || back < best) ++ break; ++ best = back; ++ idx_suggest = iTo; ++ } ++ } else { ++ idx += iThisIndex; ++ idx_suggest = idx; ++ } ++ } else { ++ idx*=ENTR_PER_PAGE; ++ idx_suggest = idx; ++ } ++ return bFound; ++} ++ ++//=================================================================== ++Dict::Dict() ++{ ++ storage = NULL; ++} ++ ++Dict::~Dict() ++{ ++ delete storage; ++} ++ ++bool Dict::load(const std::string& ifofilename, bool CreateCacheFile, ++ int EnableCollationLevel, CollateFunctions CollateFunction, ++ show_progress_t *sp) ++{ ++ gulong idxfilesize; ++ glong wordcount, synwordcount; ++ if (!load_ifofile(ifofilename, idxfilesize, wordcount, synwordcount)) ++ return false; ++ sp->notify_about_start(_("Loading...")); ++ std::string fullfilename(ifofilename); ++ fullfilename.replace(fullfilename.length()-sizeof("ifo")+1, sizeof("ifo")-1, "dict.dz"); ++ ++ if (g_file_test(fullfilename.c_str(), G_FILE_TEST_EXISTS)) { ++ dictdzfile.reset(new dictData); ++ if (!dictdzfile->open(fullfilename, 0)) { ++ //g_print("open file %s failed!\n",fullfilename); ++ return false; ++ } ++ } else { ++ fullfilename.erase(fullfilename.length()-sizeof(".dz")+1, sizeof(".dz")-1); ++ dictfile = fopen(fullfilename.c_str(),"rb"); ++ if (!dictfile) { ++ //g_print("open file %s failed!\n",fullfilename); ++ return false; ++ } ++ } ++ ++ fullfilename=ifofilename; ++ fullfilename.replace(fullfilename.length()-sizeof("ifo")+1, sizeof("ifo")-1, "idx.gz"); ++ ++ if (g_file_test(fullfilename.c_str(), G_FILE_TEST_EXISTS)) { ++ idx_file.reset(new wordlist_index); ++ } else { ++ fullfilename.erase(fullfilename.length()-sizeof(".gz")+1, sizeof(".gz")-1); ++ idx_file.reset(new offset_index); ++ } ++ ++ if (!idx_file->load(fullfilename, wordcount, idxfilesize, ++ CreateCacheFile, EnableCollationLevel, ++ CollateFunction, sp)) ++ return false; ++ ++ if (synwordcount) { ++ fullfilename=ifofilename; ++ fullfilename.replace(fullfilename.length()-sizeof("ifo")+1, sizeof("ifo")-1, "syn"); ++ if (g_file_test(fullfilename.c_str(), G_FILE_TEST_EXISTS)) { ++ syn_file.reset(new synonym_file); ++ if (!syn_file->load(fullfilename, synwordcount, ++ CreateCacheFile, EnableCollationLevel, ++ CollateFunction, sp)) ++ return false; ++ } ++ } ++ ++ bool has_res = false; ++ gchar *dirname = g_path_get_dirname(ifofilename.c_str()); ++ fullfilename = dirname; ++ fullfilename += G_DIR_SEPARATOR_S "res"; ++ if (g_file_test(fullfilename.c_str(), G_FILE_TEST_IS_DIR)) { ++ has_res = true; ++ } else { ++ fullfilename = dirname; ++ fullfilename += G_DIR_SEPARATOR_S "res.rifo"; ++ if (g_file_test(fullfilename.c_str(), G_FILE_TEST_EXISTS)) { ++ has_res = true; ++ } ++ } ++ if (has_res) { ++ storage = new ResourceStorage(); ++ bool failed = storage->load(dirname); ++ if (failed) { ++ delete storage; ++ storage = NULL; ++ } ++ } ++ g_free(dirname); ++ ++ g_print("bookname: %s , wordcount %lu\n", bookname.c_str(), wordcount); ++ return true; ++} ++ ++bool Dict::load_ifofile(const std::string& ifofilename, gulong &idxfilesize, glong &wordcount, glong &synwordcount) ++{ ++ DictInfo dict_info; ++ if (!dict_info.load_from_ifo_file(ifofilename, false)) ++ return false; ++ if (dict_info.wordcount==0) ++ return false; ++ ++ ifo_file_name=dict_info.ifo_file_name; ++ bookname=dict_info.bookname; ++ ++ idxfilesize=dict_info.index_file_size; ++ wordcount=dict_info.wordcount; ++ synwordcount=dict_info.synwordcount; ++ ++ sametypesequence=dict_info.sametypesequence; ++ dicttype=dict_info.dicttype; ++ ++ return true; ++} ++ ++glong Dict::nsynarticles() ++{ ++ if (syn_file.get() == NULL) ++ return 0; ++ return syn_file->wordcount; ++} ++ ++bool Dict::GetWordPrev(glong idx, glong &pidx, bool isidx, int EnableCollationLevel, int servercollatefunc) ++{ ++ idxsyn_file *is_file; ++ if (isidx) ++ is_file = idx_file.get(); ++ else ++ is_file = syn_file.get(); ++ if (idx==INVALID_INDEX) { ++ pidx = is_file->wordcount-1; ++ return true; ++ } ++ pidx = idx; ++ gchar *cWord = g_strdup(is_file->getWord(pidx, EnableCollationLevel, servercollatefunc)); ++ const gchar *pWord; ++ bool found=false; ++ while (pidx>0) { ++ pWord = is_file->getWord(pidx-1, EnableCollationLevel, servercollatefunc); ++ if (strcmp(pWord, cWord)!=0) { ++ found=true; ++ break; ++ } ++ pidx--; ++ } ++ g_free(cWord); ++ if (found) { ++ pidx--; ++ return true; ++ } else { ++ return false; ++ } ++} ++ ++void Dict::GetWordNext(glong &idx, bool isidx, int EnableCollationLevel, int servercollatefunc) ++{ ++ idxsyn_file *is_file; ++ if (isidx) ++ is_file = idx_file.get(); ++ else ++ is_file = syn_file.get(); ++ gchar *cWord = g_strdup(is_file->getWord(idx, EnableCollationLevel, servercollatefunc)); ++ const gchar *pWord; ++ bool found=false; ++ while (idx < is_file->wordcount-1) { ++ pWord = is_file->getWord(idx+1, EnableCollationLevel, servercollatefunc); ++ if (strcmp(pWord, cWord)!=0) { ++ found=true; ++ break; ++ } ++ idx++; ++ } ++ g_free(cWord); ++ if (found) ++ idx++; ++ else ++ idx=INVALID_INDEX; ++} ++ ++gint Dict::GetOrigWordCount(glong& idx, bool isidx) ++{ ++ idxsyn_file *is_file; ++ if (isidx) ++ is_file = idx_file.get(); ++ else ++ is_file = syn_file.get(); ++ gchar *cWord = g_strdup(is_file->get_key(idx)); ++ const gchar *pWord; ++ gint count = 1; ++ glong idx1 = idx; ++ while (idx1>0) { ++ pWord = is_file->get_key(idx1-1); ++ if (strcmp(pWord, cWord)!=0) ++ break; ++ count++; ++ idx1--; ++ } ++ glong idx2=idx; ++ while (idx2<is_file->wordcount-1) { ++ pWord = is_file->get_key(idx2+1); ++ if (strcmp(pWord, cWord)!=0) ++ break; ++ count++; ++ idx2++; ++ } ++ idx=idx1; ++ g_free(cWord); ++ return count; ++} ++ ++bool Dict::LookupSynonym(const char *str, glong &synidx, glong &synidx_suggest, int EnableCollationLevel, int servercollatefunc) ++{ ++ if (syn_file.get() == NULL) { ++ synidx = UNSET_INDEX; ++ synidx_suggest = UNSET_INDEX; ++ return false; ++ } ++ return syn_file->Lookup(str, synidx, synidx_suggest, EnableCollationLevel, servercollatefunc); ++} ++ ++bool Dict::LookupWithRule(GPatternSpec *pspec, glong *aIndex, int iBuffLen) ++{ ++ int iIndexCount=0; ++ for (glong i=0; i<narticles() && iIndexCount<iBuffLen-1; i++) ++ // Need to deal with same word in index? But this will slow down processing in most case. ++ if (g_pattern_match_string(pspec, idx_file->getWord(i, 0, 0))) ++ aIndex[iIndexCount++]=i; ++ aIndex[iIndexCount]= -1; // -1 is the end. ++ return (iIndexCount>0); ++} ++ ++bool Dict::LookupWithRuleSynonym(GPatternSpec *pspec, glong *aIndex, int iBuffLen) ++{ ++ if (syn_file.get() == NULL) ++ return false; ++ int iIndexCount=0; ++ for (glong i=0; i<nsynarticles() && iIndexCount<iBuffLen-1; i++) ++ // Need to deal with same word in index? But this will slow down processing in most case. ++ if (g_pattern_match_string(pspec, syn_file->getWord(i, 0, 0))) ++ aIndex[iIndexCount++]=i; ++ aIndex[iIndexCount]= -1; // -1 is the end. ++ return (iIndexCount>0); ++} ++ ++bool Dict::LookupWithRegex(GRegex *regex, glong *aIndex, int iBuffLen) ++{ ++ int iIndexCount=0; ++ for (glong i=0; i<narticles() && iIndexCount<iBuffLen-1; i++) ++ // Need to deal with same word in index? But this will slow down processing in most case. ++ if (g_regex_match(regex, idx_file->getWord(i, 0, 0), (GRegexMatchFlags)0, NULL)) ++ aIndex[iIndexCount++]=i; ++ aIndex[iIndexCount]= -1; // -1 is the end. ++ return (iIndexCount>0); ++} ++ ++bool Dict::LookupWithRegexSynonym(GRegex *regex, glong *aIndex, int iBuffLen) ++{ ++ if (syn_file.get() == NULL) ++ return false; ++ int iIndexCount=0; ++ for (glong i=0; i<nsynarticles() && iIndexCount<iBuffLen-1; i++) ++ // Need to deal with same word in index? But this will slow down processing in most case. ++ if (g_regex_match(regex, syn_file->getWord(i, 0, 0), (GRegexMatchFlags)0, NULL)) ++ aIndex[iIndexCount++]=i; ++ aIndex[iIndexCount]= -1; // -1 is the end. ++ return (iIndexCount>0); ++} ++ ++//=================================================================== ++show_progress_t Libs::default_show_progress; ++ ++Libs::Libs(show_progress_t *sp, bool create, int enablelevel, int function) ++{ ++#ifdef SD_SERVER_CODE ++ root_info_item = NULL; ++#endif ++ set_show_progress(sp); ++ CreateCacheFile = create; ++ EnableCollationLevel = enablelevel; ++ CollateFunction = (CollateFunctions)function; ++ iMaxFuzzyDistance = MAX_FUZZY_DISTANCE; //need to read from cfg. ++ if (EnableCollationLevel == 0) { ++ } else if (EnableCollationLevel == 1) { ++ if (utf8_collate_init(CollateFunction)) ++ printf("Init collate function failed!\n"); ++ } else if (EnableCollationLevel == 2){ ++ if (utf8_collate_init_all()) ++ printf("Init collate functions failed!\n"); ++ } ++} ++ ++Libs::~Libs() ++{ ++#ifdef SD_SERVER_CODE ++ if (root_info_item) ++ delete root_info_item; ++#endif ++ for (std::vector<Dict *>::iterator p=oLib.begin(); p!=oLib.end(); ++p) ++ delete *p; ++ utf8_collate_end(); ++} ++ ++bool Libs::load_dict(const std::string& url, show_progress_t *sp) ++{ ++ Dict *lib=new Dict; ++ if (lib->load(url, CreateCacheFile, EnableCollationLevel, ++ CollateFunction, sp)) { ++ oLib.push_back(lib); ++ return true; ++ } else { ++ delete lib; ++ return false; ++ } ++} ++ ++#ifdef SD_SERVER_CODE ++void Libs::LoadFromXML() ++{ ++ root_info_item = new DictInfoItem(); ++ root_info_item->isdir = 1; ++ root_info_item->dir = new DictInfoDirItem(); ++ root_info_item->dir->name='/'; ++ LoadXMLDir("/usr/share/stardict/dic", root_info_item); ++ GenLinkDict(root_info_item); ++} ++ ++void Libs::GenLinkDict(DictInfoItem *info_item) ++{ ++ std::list<std::list<DictInfoItem *>::iterator> eraselist; ++ for (std::list<DictInfoItem *>::iterator i = info_item->dir->info_item_list.begin(); i!= info_item->dir->info_item_list.end(); ++i) { ++ if ((*i)->isdir == 1) { ++ GenLinkDict(*i); ++ } else if ((*i)->isdir == 2) { ++ std::map<std::string, DictInfoDictItem *>::iterator uid_iter; ++ uid_iter = uidmap.find(*((*i)->linkuid)); ++ if (uid_iter!=uidmap.end()) { ++ delete (*i)->linkuid; ++ (*i)->dict = uid_iter->second; ++ } else { ++ g_print("Error, linkdict uid not found! %s\n", (*i)->linkuid->c_str()); ++ delete (*i)->linkuid; ++ eraselist.push_back(i); ++ } ++ } ++ } ++ for (std::list<std::list<DictInfoItem *>::iterator>::iterator i = eraselist.begin(); i!= eraselist.end(); ++i) { ++ info_item->dir->info_item_list.erase(*i); ++ } ++} ++ ++void Libs::func_parse_start_element(GMarkupParseContext *context, const gchar *element_name, const gchar **attribute_names, const gchar **attribute_values, gpointer user_data, GError **error) ++{ ++ if (strcmp(element_name, "dict")==0) { ++ ParseUserData *Data = (ParseUserData *)user_data; ++ Data->indict = true; ++ Data->path.clear(); ++ Data->uid.clear(); ++ Data->level.clear(); ++ Data->download.clear(); ++ Data->from.clear(); ++ Data->to.clear(); ++ } else if (strcmp(element_name, "linkdict")==0) { ++ ParseUserData *Data = (ParseUserData *)user_data; ++ Data->inlinkdict = true; ++ Data->linkuid.clear(); ++ } ++} ++ ++void Libs::func_parse_end_element(GMarkupParseContext *context, const gchar *element_name, gpointer user_data, GError **error) ++{ ++ if (strcmp(element_name, "dict")==0) { ++ ParseUserData *Data = (ParseUserData *)user_data; ++ Data->indict = false; ++ if (!Data->path.empty() && !Data->uid.empty()) { ++ std::string url; ++ url = Data->dir; ++ url += G_DIR_SEPARATOR; ++ url += Data->path; ++ if (Data->oLibs->load_dict(url, Data->oLibs->show_progress)) { ++ DictInfoItem *sub_info_item = new DictInfoItem(); ++ sub_info_item->isdir = 0; ++ sub_info_item->dict = new DictInfoDictItem(); ++ sub_info_item->dict->uid = Data->uid; ++ sub_info_item->dict->download = Data->download; ++ sub_info_item->dict->from = Data->from; ++ sub_info_item->dict->to = Data->to; ++ if (Data->level.empty()) ++ sub_info_item->dict->level = 0; ++ else ++ sub_info_item->dict->level = atoi(Data->level.c_str()); ++ sub_info_item->dict->id = Data->oLibs->oLib.size()-1; ++ Data->info_item->dir->info_item_list.push_back(sub_info_item); ++ Data->oLibs->uidmap[Data->uid] = sub_info_item->dict; ++ } ++ } ++ } else if (strcmp(element_name, "linkdict")==0) { ++ ParseUserData *Data = (ParseUserData *)user_data; ++ Data->inlinkdict = false; ++ if (!Data->linkuid.empty()) { ++ DictInfoItem *sub_info_item = new DictInfoItem(); ++ sub_info_item->isdir = 2; ++ sub_info_item->linkuid = new std::string(Data->linkuid); ++ Data->info_item->dir->info_item_list.push_back(sub_info_item); ++ } ++ } ++} ++ ++void Libs::func_parse_text(GMarkupParseContext *context, const gchar *text, gsize text_len, gpointer user_data, GError **error) ++{ ++ const gchar *element = g_markup_parse_context_get_element(context); ++ if (!element) ++ return; ++ ParseUserData *Data = (ParseUserData *)user_data; ++ if (strcmp(element, "subdir")==0) { ++ std::string subdir; ++ subdir = Data->dir; ++ subdir += G_DIR_SEPARATOR; ++ subdir.append(text, text_len); ++ DictInfoItem *sub_info_item = new DictInfoItem(); ++ sub_info_item->isdir = 1; ++ sub_info_item->dir = new DictInfoDirItem(); ++ sub_info_item->dir->name.assign(text, text_len); ++ Data->oLibs->LoadXMLDir(subdir.c_str(), sub_info_item); ++ Data->info_item->dir->info_item_list.push_back(sub_info_item); ++ } else if (strcmp(element, "dirname")==0) { ++ Data->info_item->dir->dirname.assign(text, text_len); ++ } else if (strcmp(element, "path")==0) { ++ Data->path.assign(text, text_len); ++ } else if (strcmp(element, "uid")==0) { ++ if (Data->indict) { ++ std::string uid(text, text_len); ++ if (uid.find_first_of(' ')!=std::string::npos) { ++ g_print("Error: uid contains space! %s: %s\n", Data->dir, uid.c_str()); ++ } else { ++ std::map<std::string, DictInfoDictItem *>::iterator uid_iter; ++ uid_iter = Data->oLibs->uidmap.find(uid); ++ if (uid_iter!=Data->oLibs->uidmap.end()) { ++ g_print("Error: uid duplicated! %s: %s\n", Data->dir, uid.c_str()); ++ } else { ++ Data->uid = uid; ++ } ++ } ++ } else if (Data->inlinkdict) { ++ Data->linkuid.assign(text, text_len); ++ } ++ } else if (strcmp(element, "level")==0) { ++ Data->level.assign(text, text_len); ++ } else if (strcmp(element, "download")==0) { ++ Data->download.assign(text, text_len); ++ } else if (strcmp(element, "from")==0) { ++ Data->from.assign(text, text_len); ++ } else if (strcmp(element, "to")==0) { ++ Data->to.assign(text, text_len); ++ } ++} ++ ++void Libs::LoadXMLDir(const char *dir, DictInfoItem *info_item) ++{ ++ std::string filename; ++ filename = dir; ++ filename += G_DIR_SEPARATOR_S "stardictd.xml"; ++ struct stat filestat; ++ if (g_stat(filename.c_str(), &filestat)!=0) ++ return; ++ MapFile mf; ++ if (!mf.open(filename.c_str(), filestat.st_size)) ++ return; ++ ParseUserData Data; ++ Data.oLibs = this; ++ Data.dir = dir; ++ Data.info_item = info_item; ++ Data.indict = false; ++ Data.inlinkdict = false; ++ GMarkupParser parser; ++ parser.start_element = func_parse_start_element; ++ parser.end_element = func_parse_end_element; ++ parser.text = func_parse_text; ++ parser.passthrough = NULL; ++ parser.error = NULL; ++ GMarkupParseContext* context = g_markup_parse_context_new(&parser, (GMarkupParseFlags)0, &Data, NULL); ++ g_markup_parse_context_parse(context, mf.begin(), filestat.st_size, NULL); ++ g_markup_parse_context_end_parse(context, NULL); ++ g_markup_parse_context_free(context); ++ mf.close(); ++ info_item->dir->dictcount = 0; ++ for (std::list<DictInfoItem *>::iterator i = info_item->dir->info_item_list.begin(); i!= info_item->dir->info_item_list.end(); ++i) { ++ if ((*i)->isdir == 1) { ++ info_item->dir->dictcount += (*i)->dir->dictcount; ++ } else if ((*i)->isdir == 0) { ++ info_item->dir->dictcount++; ++ } ++ } ++} ++ ++const std::string &Libs::get_fromto_info() { ++ if(cache_fromto.empty()){ ++ std::map<std::string, std::list<FromTo> > map_fromto; ++ gen_fromto_info(root_info_item, map_fromto); ++ cache_fromto+="<lang>"; ++ for (std::map<std::string, std::list<FromTo> >::iterator map_it = map_fromto.begin(); map_it != map_fromto.end(); ++map_it){ ++ cache_fromto+="<from lang=\""; ++ cache_fromto+=map_it->first; ++ cache_fromto+="\">"; ++ std::list<FromTo> &fromTo = map_it->second; ++ for (std::list<FromTo>::iterator i = fromTo.begin() ; i!= fromTo.end(); ++i){ ++ cache_fromto+="<to lang=\""; ++ cache_fromto+= i->to; ++ cache_fromto+="\">"; ++ std::list<FromToInfo> &fromtoinfo = i->fromto_info; ++ for (std::list<FromToInfo>::iterator j = fromtoinfo.begin() ; j!= fromtoinfo.end(); ++j){ ++ cache_fromto+="<dict><uid>"; ++ cache_fromto+=j->uid; ++ cache_fromto+="</uid><bookname>"; ++ cache_fromto+= j->bookname; ++ cache_fromto+="</bookname></dict>"; ++ } ++ cache_fromto+="</to>"; ++ } ++ cache_fromto+="</from>"; ++ } ++ cache_fromto+="</lang>"; ++ } ++ return cache_fromto; ++} ++ ++void Libs::gen_fromto_info(struct DictInfoItem *info_item, std::map<std::string, std::list<FromTo> > &map_fromto) { ++ gchar *etext; ++ for(std::list<DictInfoItem *>::iterator i = info_item->dir->info_item_list.begin() ; i!= info_item->dir->info_item_list.end(); ++i){ ++ if ((*i)->isdir == 1) { ++ gen_fromto_info((*i), map_fromto); ++ } else { ++ std::string from_str = (*i)->dict->from; ++ std::string to_str = (*i)->dict->to; ++ if(from_str.empty() || to_str.empty()){ ++ continue; ++ } ++ std::string uid_str = (*i)->dict->uid; ++ etext = g_markup_escape_text(oLib[(*i)->dict->id]->dict_name().c_str(), -1); ++ std::string bookname_str = etext; ++ g_free(etext); ++ std::map<std::string, std::list<FromTo> >::iterator fromto1 = map_fromto.find(from_str); ++ if (fromto1==map_fromto.end()) { ++ //if an from_str element not already in map, add new from_str to map ++ FromToInfo fromtoinfo; ++ fromtoinfo.uid = uid_str; ++ fromtoinfo.bookname = bookname_str; ++ std::list<FromToInfo> list_fromtoinfo ; ++ list_fromtoinfo.push_back(fromtoinfo); ++ FromTo new_fromTo; ++ new_fromTo.to = to_str; ++ new_fromTo.fromto_info = list_fromtoinfo; ++ std::list<FromTo> list_fromTo; ++ list_fromTo.push_back(new_fromTo); ++ map_fromto[from_str] = list_fromTo; ++ } else { ++ // else if from_str already in map, so comparison to_str and from_to1 , then choose insert. ++ std::list<FromTo> &fromTo_list = fromto1->second; ++ std::string from_name1 = fromto1->first; ++ bool found = false; ++ for (std::list<FromTo>::iterator new_fromTo = fromTo_list.begin(); new_fromTo != fromTo_list.end(); ++new_fromTo) { ++ if(to_str == new_fromTo->to) { ++ std::list<FromToInfo> &fromtoinfo1 = new_fromTo->fromto_info; ++ FromToInfo fromtoinfo; ++ fromtoinfo.uid = uid_str; ++ fromtoinfo.bookname = bookname_str; ++ fromtoinfo1.push_back(fromtoinfo); ++ found = true; ++ break; ++ } ++ } ++ if(!found){ ++ FromToInfo fromtoinfo; ++ fromtoinfo.uid = uid_str; ++ fromtoinfo.bookname = bookname_str; ++ std::list<FromToInfo> fromtoinfo1; ++ fromtoinfo1.push_back(fromtoinfo); ++ FromTo fromTo; ++ fromTo.to = to_str; ++ fromTo.fromto_info = fromtoinfo1; ++ fromTo_list.push_back(fromTo); ++ } ++ } ++ } ++ } ++} ++ ++const std::string *Libs::get_dir_info(const char *path) ++{ ++ if (path[0]!='/') ++ return NULL; ++ DictInfoItem *info_item = root_info_item; ++ std::string item; ++ const char *p = path+1; ++ const char *p1; ++ bool found; ++ do { ++ p1 = strchr(p, '/'); ++ if (p1) { ++ item.assign(p, p1-p); ++ if (!item.empty()) { ++ found = false; ++ for (std::list<DictInfoItem *>::iterator i = info_item->dir->info_item_list.begin(); i!= info_item->dir->info_item_list.end(); ++i) { ++ if ((*i)->isdir == 1) { ++ if ((*i)->dir->name == item) { ++ info_item = (*i); ++ found = true; ++ break; ++ } ++ } ++ } ++ if (!found) ++ return NULL; ++ } ++ p = p1+1; ++ } ++ } while (p1); ++ if (*p) ++ return NULL; // Not end by '/'. ++ DictInfoDirItem *dir = info_item->dir; ++ if (dir->info_string.empty()) { ++ dir->info_string += "<parent>"; ++ dir->info_string += path; ++ dir->info_string += "</parent>"; ++ gchar *etext; ++ for (std::list<DictInfoItem *>::iterator i = info_item->dir->info_item_list.begin(); i!= info_item->dir->info_item_list.end(); ++i) { ++ if ((*i)->isdir == 1) { ++ dir->info_string += "<dir><name>"; ++ dir->info_string += (*i)->dir->name; ++ dir->info_string += "</name><dirname>"; ++ dir->info_string += (*i)->dir->dirname; ++ dir->info_string += "</dirname><dictcount>"; ++ gchar *dictcount = g_strdup_printf("%u", (*i)->dir->dictcount); ++ dir->info_string += dictcount; ++ g_free(dictcount); ++ dir->info_string += "</dictcount></dir>"; ++ } else { ++ dir->info_string += "<dict>"; ++ if ((*i)->isdir == 2) ++ dir->info_string += "<islink>1</islink>"; ++ if ((*i)->dict->level != 0) { ++ dir->info_string += "<level>"; ++ gchar *level = g_strdup_printf("%u", (*i)->dict->level); ++ dir->info_string += level; ++ g_free(level); ++ dir->info_string += "</level>"; ++ } ++ dir->info_string += "<uid>"; ++ dir->info_string += (*i)->dict->uid; ++ dir->info_string += "</uid><bookname>"; ++ etext = g_markup_escape_text(oLib[(*i)->dict->id]->dict_name().c_str(), -1); ++ dir->info_string += etext; ++ g_free(etext); ++ dir->info_string += "</bookname><wordcount>"; ++ gchar *wc = g_strdup_printf("%ld", oLib[(*i)->dict->id]->narticles()); ++ dir->info_string += wc; ++ g_free(wc); ++ dir->info_string += "</wordcount></dict>"; ++ } ++ } ++ } ++ return &(dir->info_string); ++} ++ ++int Libs::get_dict_level(const char *uid) ++{ ++ std::map<std::string, DictInfoDictItem *>::iterator uid_iter; ++ uid_iter = uidmap.find(uid); ++ if (uid_iter==uidmap.end()) ++ return -1; ++ return uid_iter->second->level; ++} ++ ++std::string Libs::get_dicts_list(const char *dictmask, int max_dict_count, int userLevel) ++{ ++ std::list<std::string> uid_list; ++ std::string uid; ++ const char *p, *p1; ++ p = dictmask; ++ do { ++ p1 = strchr(p, ' '); ++ if (p1) { ++ uid.assign(p, p1-p); ++ if (!uid.empty()) ++ uid_list.push_back(uid); ++ p = p1+1; ++ } ++ } while (p1); ++ uid = p; ++ if (!uid.empty()) ++ uid_list.push_back(uid); ++ ++ std::string dictmask_str; ++ int count = 0; ++ const std::string *info_string; ++ int level; ++ for (std::list<std::string>::iterator i = uid_list.begin(); i!= uid_list.end(); ++i) { ++ level = get_dict_level((*i).c_str()); ++ if (level < 0 || level > userLevel) ++ continue; ++ info_string = get_dict_info(i->c_str(), true); ++ if (info_string) { ++ if (count>=max_dict_count) ++ break; ++ dictmask_str += info_string->c_str(); ++ count++; ++ } ++ } ++ return dictmask_str; ++} ++ ++const std::string *Libs::get_dict_info(const char *uid, bool is_short) ++{ ++ std::map<std::string, DictInfoDictItem *>::iterator uid_iter; ++ uid_iter = uidmap.find(uid); ++ if (uid_iter==uidmap.end()) ++ return NULL; ++ DictInfoDictItem *dict; ++ dict = uid_iter->second; ++ if (is_short) { ++ if (dict->short_info_string.empty()) { ++ gchar *etext; ++ dict->short_info_string += "<dict><uid>"; ++ dict->short_info_string += uid; ++ dict->short_info_string += "</uid><bookname>"; ++ etext = g_markup_escape_text(oLib[dict->id]->dict_name().c_str(), -1); ++ dict->short_info_string += etext; ++ g_free(etext); ++ dict->short_info_string += "</bookname><wordcount>"; ++ gchar *wc = g_strdup_printf("%ld", oLib[dict->id]->narticles()); ++ dict->short_info_string += wc; ++ g_free(wc); ++ dict->short_info_string += "</wordcount></dict>"; ++ } ++ return &(dict->short_info_string); ++ } else { ++ if (dict->info_string.empty()) { ++ gchar *etext; ++ DictInfo dict_info; ++ if (!dict_info.load_from_ifo_file(oLib[dict->id]->ifofilename(), false)) ++ return NULL; ++ dict->info_string += "<dictinfo><bookname>"; ++ etext = g_markup_escape_text(dict_info.bookname.c_str(), -1); ++ dict->info_string += etext; ++ g_free(etext); ++ dict->info_string += "</bookname><wordcount>"; ++ gchar *wc = g_strdup_printf("%u", dict_info.wordcount); ++ dict->info_string += wc; ++ g_free(wc); ++ dict->info_string += "</wordcount>"; ++ if (dict_info.synwordcount!=0) { ++ dict->info_string += "<synwordcount>"; ++ wc = g_strdup_printf("%u", dict_info.synwordcount); ++ dict->info_string += wc; ++ g_free(wc); ++ dict->info_string += "</synwordcount>"; ++ } ++ dict->info_string += "<author>"; ++ etext = g_markup_escape_text(dict_info.author.c_str(), -1); ++ dict->info_string += etext; ++ g_free(etext); ++ dict->info_string += "</author><email>"; ++ etext = g_markup_escape_text(dict_info.email.c_str(), -1); ++ dict->info_string += etext; ++ g_free(etext); ++ dict->info_string += "</email><website>"; ++ etext = g_markup_escape_text(dict_info.website.c_str(), -1); ++ dict->info_string += etext; ++ g_free(etext); ++ dict->info_string += "</website><description>"; ++ etext = g_markup_escape_text(dict_info.description.c_str(), -1); ++ dict->info_string += etext; ++ g_free(etext); ++ dict->info_string += "</description><date>"; ++ etext = g_markup_escape_text(dict_info.date.c_str(), -1); ++ dict->info_string += etext; ++ g_free(etext); ++ dict->info_string += "</date><download>"; ++ etext = g_markup_escape_text(dict->download.c_str(), -1); ++ dict->info_string += etext; ++ g_free(etext); ++ dict->info_string += "</download></dictinfo>"; ++ } ++ return &(dict->info_string); ++ } ++} ++ ++void Libs::SetServerDictMask(std::vector<InstantDictIndex> &dictmask, const char *dicts, int max, int userLevel) ++{ ++ InstantDictIndex instance_dict_index; ++ instance_dict_index.type = InstantDictType_LOCAL; ++ dictmask.clear(); ++ std::list<std::string> uid_list; ++ std::string uid; ++ const char *p, *p1; ++ p = dicts; ++ do { ++ p1 = strchr(p, ' '); ++ if (p1) { ++ uid.assign(p, p1-p); ++ if (!uid.empty()) ++ uid_list.push_back(uid); ++ p = p1+1; ++ } ++ } while (p1); ++ uid = p; ++ if (!uid.empty()) ++ uid_list.push_back(uid); ++ int count = 0; ++ std::map<std::string, DictInfoDictItem *>::iterator uid_iter; ++ for (std::list<std::string>::iterator i = uid_list.begin(); i!= uid_list.end(); ++i) { ++ uid_iter = uidmap.find(*i); ++ if (uid_iter!=uidmap.end()) { ++ if (max>=0 && count >= max) ++ break; ++ if (userLevel>=0 && (unsigned int)userLevel< uid_iter->second->level) ++ continue; ++ instance_dict_index.index = uid_iter->second->id; ++ dictmask.push_back(instance_dict_index); ++ count++; ++ } ++ } ++} ++ ++void Libs::LoadCollateFile(std::vector<InstantDictIndex> &dictmask, CollateFunctions cltfuc) ++{ ++ for (std::vector<InstantDictIndex>::iterator i = dictmask.begin(); i!=dictmask.end(); ++i) { ++ if ((*i).type == InstantDictType_LOCAL) { ++ oLib[(*i).index]->idx_file->collate_load(cltfuc); ++ if (oLib[(*i).index]->syn_file.get() != NULL) ++ oLib[(*i).index]->syn_file->collate_load(cltfuc); ++ } ++ } ++} ++#endif ++ ++#ifdef SD_CLIENT_CODE ++bool Libs::find_lib_by_filename(const char *filename, size_t &iLib) ++{ ++ for (std::vector<Dict *>::size_type i =0; i < oLib.size(); i++) { ++ if (oLib[i]->ifofilename() == filename) { ++ iLib = i; ++ return true; ++ } ++ } ++ return false; ++} ++ ++void Libs::load(std::list<std::string> &load_list) ++{ ++ for (std::list<std::string>::iterator i = load_list.begin(); i != load_list.end(); ++i) { ++ load_dict(*i, show_progress); ++ } ++} ++ ++void Libs::reload(std::list<std::string> &load_list, int is_coll_enb, int collf) ++{ ++ if (is_coll_enb == EnableCollationLevel && collf == CollateFunction) { ++ std::vector<Dict *> prev(oLib); ++ oLib.clear(); ++ for (std::list<std::string>::iterator i = load_list.begin(); i != load_list.end(); ++i) { ++ std::vector<Dict *>::iterator it; ++ for (it=prev.begin(); it!=prev.end(); ++it) { ++ if ((*it)->ifofilename()==*i) ++ break; ++ } ++ if (it==prev.end()) { ++ load_dict(*i, show_progress); ++ } else { ++ Dict *res=*it; ++ prev.erase(it); ++ oLib.push_back(res); ++ } ++ } ++ for (std::vector<Dict *>::iterator it=prev.begin(); it!=prev.end(); ++it) { ++ delete *it; ++ } ++ } else { ++ for (std::vector<Dict *>::iterator it = oLib.begin(); it != oLib.end(); ++it) ++ delete *it; ++ oLib.clear(); ++ EnableCollationLevel = is_coll_enb; ++ CollateFunction = CollateFunctions(collf); ++ if (EnableCollationLevel == 0) { ++ } else if (EnableCollationLevel == 1) { ++ if (utf8_collate_init(CollateFunction)) ++ printf("Init collate function failed!\n"); ++ } else if (EnableCollationLevel == 2) { ++ if (utf8_collate_init_all()) ++ printf("Init collate functions failed!\n"); ++ } ++ load(load_list); ++ } ++} ++#endif ++ ++glong Libs::CltIndexToOrig(glong cltidx, size_t iLib, int servercollatefunc) ++{ ++ if (EnableCollationLevel == 0) ++ return cltidx; ++ if (EnableCollationLevel == 1) { ++ if (cltidx == INVALID_INDEX) ++ return cltidx; ++ return oLib[iLib]->idx_file->clt_file->GetOrigIndex(cltidx); ++ } ++ if (servercollatefunc == 0) ++ return cltidx; ++ if (cltidx == INVALID_INDEX) ++ return cltidx; ++ oLib[iLib]->idx_file->collate_load((CollateFunctions)(servercollatefunc-1)); ++ return oLib[iLib]->idx_file->clt_files[servercollatefunc-1]->GetOrigIndex(cltidx); ++} ++ ++glong Libs::CltSynIndexToOrig(glong cltidx, size_t iLib, int servercollatefunc) ++{ ++ if (EnableCollationLevel == 0) ++ return cltidx; ++ if (EnableCollationLevel == 1) { ++ if (cltidx == UNSET_INDEX || cltidx == INVALID_INDEX) ++ return cltidx; ++ return oLib[iLib]->syn_file->clt_file->GetOrigIndex(cltidx); ++ } ++ if (servercollatefunc == 0) ++ return cltidx; ++ if (cltidx == UNSET_INDEX || cltidx == INVALID_INDEX) ++ return cltidx; ++ oLib[iLib]->syn_file->collate_load((CollateFunctions)(servercollatefunc-1)); ++ return oLib[iLib]->syn_file->clt_files[servercollatefunc-1]->GetOrigIndex(cltidx); ++} ++ ++const gchar *Libs::GetSuggestWord(const gchar *sWord, CurrentIndex *iCurrent, std::vector<InstantDictIndex> &dictmask, int servercollatefunc) ++{ ++ const gchar *poCurrentWord = NULL; ++ const gchar *word; ++ gint best =0; ++ gint back; ++ std::vector<InstantDictIndex>::size_type iLib; ++ std::vector<Dict *>::size_type iRealLib; ++ for (iLib=0; iLib < dictmask.size(); iLib++) { ++ if (dictmask[iLib].type != InstantDictType_LOCAL) ++ continue; ++ iRealLib = dictmask[iLib].index; ++ if ( poCurrentWord == NULL ) { ++ poCurrentWord = poGetWord(iCurrent[iLib].idx_suggest, iRealLib, servercollatefunc); ++ best = prefix_match (sWord, poCurrentWord); ++ } else { ++ word = poGetWord(iCurrent[iLib].idx_suggest, iRealLib, servercollatefunc); ++ back = prefix_match (sWord, word); ++ if (back > best) { ++ best = back; ++ poCurrentWord = word; ++ } else if (back == best) { ++ gint x = stardict_server_collate(poCurrentWord, word, EnableCollationLevel, CollateFunction, servercollatefunc); ++ if (x > 0) { ++ poCurrentWord = word; ++ } ++ } ++ } ++ } ++ for (iLib=0; iLib<dictmask.size(); iLib++) { ++ if (dictmask[iLib].type != InstantDictType_LOCAL) ++ continue; ++ if (iCurrent[iLib].synidx_suggest==UNSET_INDEX) ++ continue; ++ iRealLib = dictmask[iLib].index; ++ if ( poCurrentWord == NULL ) { ++ poCurrentWord = poGetSynonymWord(iCurrent[iLib].synidx_suggest, iRealLib, servercollatefunc); ++ best = prefix_match (sWord, poCurrentWord); ++ } else { ++ word = poGetSynonymWord(iCurrent[iLib].synidx_suggest, iRealLib, servercollatefunc); ++ back = prefix_match (sWord, word); ++ if (back > best) { ++ best = back; ++ poCurrentWord = word; ++ } else if (back == best) { ++ gint x = stardict_server_collate(poCurrentWord, word, EnableCollationLevel, CollateFunction, servercollatefunc); ++ if (x > 0) { ++ poCurrentWord = word; ++ } ++ } ++ } ++ } ++ return poCurrentWord; ++} ++ ++const gchar *Libs::poGetCurrentWord(CurrentIndex * iCurrent, std::vector<InstantDictIndex> &dictmask, int servercollatefunc) ++{ ++ const gchar *poCurrentWord = NULL; ++ const gchar *word; ++ std::vector<InstantDictIndex>::size_type iLib; ++ std::vector<Dict *>::size_type iRealLib; ++ for (iLib=0; iLib < dictmask.size(); iLib++) { ++ if (dictmask[iLib].type != InstantDictType_LOCAL) ++ continue; ++ iRealLib = dictmask[iLib].index; ++ if (iCurrent[iLib].idx==INVALID_INDEX) ++ continue; ++ if ( iCurrent[iLib].idx>=narticles(iRealLib) || iCurrent[iLib].idx<0) ++ continue; ++ if ( poCurrentWord == NULL ) { ++ poCurrentWord = poGetWord(iCurrent[iLib].idx, iRealLib, servercollatefunc); ++ } else { ++ word = poGetWord(iCurrent[iLib].idx, iRealLib, servercollatefunc); ++ gint x = stardict_server_collate(poCurrentWord, word, EnableCollationLevel, CollateFunction, servercollatefunc); ++ if (x > 0) { ++ poCurrentWord = word; ++ } ++ } ++ } ++ for (iLib=0; iLib<dictmask.size(); iLib++) { ++ if (dictmask[iLib].type != InstantDictType_LOCAL) ++ continue; ++ iRealLib = dictmask[iLib].index; ++ if (iCurrent[iLib].synidx==UNSET_INDEX) ++ continue; ++ if (iCurrent[iLib].synidx==INVALID_INDEX) ++ continue; ++ if ( iCurrent[iLib].synidx>=nsynarticles(iRealLib) || iCurrent[iLib].synidx<0) ++ continue; ++ if ( poCurrentWord == NULL ) { ++ poCurrentWord = poGetSynonymWord(iCurrent[iLib].synidx, iRealLib, servercollatefunc); ++ } else { ++ word = poGetSynonymWord(iCurrent[iLib].synidx, iRealLib, servercollatefunc); ++ gint x = stardict_server_collate(poCurrentWord, word, EnableCollationLevel, CollateFunction, servercollatefunc); ++ if (x > 0) { ++ poCurrentWord = word; ++ } ++ } ++ } ++ return poCurrentWord; ++} ++ ++const gchar * ++Libs::poGetNextWord(const gchar *sWord, CurrentIndex *iCurrent, std::vector<InstantDictIndex> &dictmask, int servercollatefunc) ++{ ++ // the input can be: ++ // (word,iCurrent),read word,write iNext to iCurrent,and return next word. used by TopWin::NextCallback(); ++ // (NULL,iCurrent),read iCurrent,write iNext to iCurrent,and return next word. used by AppCore::ListWords(); ++ const gchar *poCurrentWord = NULL; ++ std::vector<Dict *>::size_type iCurrentLib=0, iCurrentRealLib=0; ++ bool isLib = false; ++ const gchar *word; ++ ++ std::vector<InstantDictIndex>::size_type iLib; ++ std::vector<Dict *>::size_type iRealLib; ++ for (iLib=0; iLib < dictmask.size(); iLib++) { ++ if (dictmask[iLib].type != InstantDictType_LOCAL) ++ continue; ++ iRealLib = dictmask[iLib].index; ++ if (sWord) { ++ oLib[iRealLib]->Lookup(sWord, iCurrent[iLib].idx, iCurrent[iLib].idx_suggest, EnableCollationLevel, servercollatefunc); ++ } ++ if (iCurrent[iLib].idx==INVALID_INDEX) ++ continue; ++ if (iCurrent[iLib].idx>=narticles(iRealLib) || iCurrent[iLib].idx<0) ++ continue; ++ if (poCurrentWord == NULL ) { ++ poCurrentWord = poGetWord(iCurrent[iLib].idx, iRealLib, servercollatefunc); ++ iCurrentLib = iLib; ++ iCurrentRealLib = iRealLib; ++ isLib=true; ++ } else { ++ gint x; ++ word = poGetWord(iCurrent[iLib].idx, iRealLib, servercollatefunc); ++ x = stardict_server_collate(poCurrentWord, word, EnableCollationLevel, CollateFunction, servercollatefunc); ++ if (x > 0) { ++ poCurrentWord = word; ++ iCurrentLib = iLib; ++ iCurrentRealLib = iRealLib; ++ isLib=true; ++ } ++ } ++ } ++ for (iLib=0; iLib < dictmask.size(); iLib++) { ++ if (dictmask[iLib].type != InstantDictType_LOCAL) ++ continue; ++ iRealLib = dictmask[iLib].index; ++ if (sWord) { ++ oLib[iRealLib]->LookupSynonym(sWord, iCurrent[iLib].synidx, iCurrent[iLib].synidx_suggest, EnableCollationLevel, servercollatefunc); ++ } ++ if (iCurrent[iLib].synidx==UNSET_INDEX) ++ continue; ++ if (iCurrent[iLib].synidx==INVALID_INDEX) ++ continue; ++ if (iCurrent[iLib].synidx>=nsynarticles(iRealLib) || iCurrent[iLib].synidx<0) ++ continue; ++ if (poCurrentWord == NULL ) { ++ poCurrentWord = poGetSynonymWord(iCurrent[iLib].synidx, iRealLib, servercollatefunc); ++ iCurrentLib = iLib; ++ iCurrentRealLib = iRealLib; ++ isLib=false; ++ } else { ++ gint x; ++ word = poGetSynonymWord(iCurrent[iLib].synidx, iRealLib, servercollatefunc); ++ x = stardict_server_collate(poCurrentWord, word, EnableCollationLevel, CollateFunction, servercollatefunc); ++ if (x > 0 ) { ++ poCurrentWord = word; ++ iCurrentLib = iLib; ++ iCurrentRealLib = iRealLib; ++ isLib=false; ++ } ++ } ++ } ++ if (poCurrentWord) { ++ for (iLib=0; iLib < dictmask.size(); iLib++) { ++ if (dictmask[iLib].type != InstantDictType_LOCAL) ++ continue; ++ iRealLib = dictmask[iLib].index; ++ if (isLib && (iLib == iCurrentLib)) ++ continue; ++ if (iCurrent[iLib].idx==INVALID_INDEX) ++ continue; ++ if (iCurrent[iLib].idx>=narticles(iRealLib) || iCurrent[iLib].idx<0) ++ continue; ++ word = poGetWord(iCurrent[iLib].idx, iRealLib, servercollatefunc); ++ if (strcmp(poCurrentWord, word) == 0) { ++ GetWordNext(iCurrent[iLib].idx, iRealLib, true, servercollatefunc); ++ } ++ } ++ for (iLib=0; iLib < dictmask.size(); iLib++) { ++ if (dictmask[iLib].type != InstantDictType_LOCAL) ++ continue; ++ iRealLib = dictmask[iLib].index; ++ if ((!isLib) && (iLib == iCurrentLib)) ++ continue; ++ if (iCurrent[iLib].synidx==UNSET_INDEX) ++ continue; ++ if (iCurrent[iLib].synidx==INVALID_INDEX) ++ continue; ++ if (iCurrent[iLib].synidx>=nsynarticles(iRealLib) || iCurrent[iLib].synidx<0) ++ continue; ++ word = poGetSynonymWord(iCurrent[iLib].synidx, iRealLib, servercollatefunc); ++ if (strcmp(poCurrentWord, word) == 0) { ++ GetWordNext(iCurrent[iLib].synidx, iRealLib, false, servercollatefunc); ++ } ++ } ++ //GetWordNext will change poCurrentWord's content, so do it at the last. ++ if (isLib) { ++ GetWordNext(iCurrent[iCurrentLib].idx, iCurrentRealLib, true, servercollatefunc); ++ } else { ++ GetWordNext(iCurrent[iCurrentLib].synidx, iCurrentRealLib, false, servercollatefunc); ++ } ++ poCurrentWord = poGetCurrentWord(iCurrent, dictmask, servercollatefunc); ++ } ++ return poCurrentWord; ++} ++ ++const gchar * ++Libs::poGetPreWord(const gchar *sWord, CurrentIndex* iCurrent, std::vector<InstantDictIndex> &dictmask, int servercollatefunc) ++{ ++ // used by TopWin::PreviousCallback(); the iCurrent is cached by AppCore::TopWinWordChange(); ++ const gchar *poCurrentWord = NULL; ++ std::vector<Dict *>::size_type iCurrentLib=0, iCurrentRealLib=0; ++ bool isLib = false; ++ ++ const gchar *word; ++ glong pidx; ++ std::vector<InstantDictIndex>::size_type iLib; ++ std::vector<Dict *>::size_type iRealLib; ++ for (iLib=0;iLib<dictmask.size();iLib++) { ++ if (dictmask[iLib].type != InstantDictType_LOCAL) ++ continue; ++ iRealLib = dictmask[iLib].index; ++ if (sWord) { ++ oLib[iRealLib]->Lookup(sWord, iCurrent[iLib].idx, iCurrent[iLib].idx_suggest, EnableCollationLevel, servercollatefunc); ++ } ++ if (iCurrent[iLib].idx!=INVALID_INDEX) { ++ if ( iCurrent[iLib].idx>=narticles(iRealLib) || iCurrent[iLib].idx<=0) ++ continue; ++ } ++ if ( poCurrentWord == NULL ) { ++ if (GetWordPrev(iCurrent[iLib].idx, pidx, iRealLib, true, servercollatefunc)) { ++ poCurrentWord = poGetWord(pidx, iRealLib, servercollatefunc); ++ iCurrentLib = iLib; ++ iCurrentRealLib = iRealLib; ++ isLib=true; ++ } ++ } else { ++ if (GetWordPrev(iCurrent[iLib].idx, pidx, iRealLib, true, servercollatefunc)) { ++ gint x; ++ word = poGetWord(pidx, iRealLib, servercollatefunc); ++ x = stardict_server_collate(poCurrentWord, word, EnableCollationLevel, CollateFunction, servercollatefunc); ++ if (x < 0 ) { ++ poCurrentWord = word; ++ iCurrentLib = iLib; ++ iCurrentRealLib = iRealLib; ++ isLib=true; ++ } ++ } ++ } ++ } ++ for (iLib=0;iLib<dictmask.size();iLib++) { ++ if (dictmask[iLib].type != InstantDictType_LOCAL) ++ continue; ++ iRealLib = dictmask[iLib].index; ++ if (sWord) { ++ oLib[iRealLib]->LookupSynonym(sWord, iCurrent[iLib].synidx, iCurrent[iLib].synidx_suggest, EnableCollationLevel, servercollatefunc); ++ } ++ if (iCurrent[iLib].synidx==UNSET_INDEX) ++ continue; ++ if (iCurrent[iLib].synidx!=INVALID_INDEX) { ++ if ( iCurrent[iLib].synidx>=nsynarticles(iRealLib) || iCurrent[iLib].synidx<=0) ++ continue; ++ } ++ if ( poCurrentWord == NULL ) { ++ if (GetWordPrev(iCurrent[iLib].synidx, pidx, iRealLib, false, servercollatefunc)) { ++ poCurrentWord = poGetSynonymWord(pidx, iRealLib, servercollatefunc); ++ iCurrentLib = iLib; ++ iCurrentRealLib = iRealLib; ++ isLib=false; ++ } ++ } else { ++ if (GetWordPrev(iCurrent[iLib].synidx, pidx, iRealLib, false, servercollatefunc)) { ++ gint x; ++ word = poGetSynonymWord(pidx,iRealLib, servercollatefunc); ++ x = stardict_server_collate(poCurrentWord, word, EnableCollationLevel, CollateFunction, servercollatefunc); ++ if (x < 0 ) { ++ poCurrentWord = word; ++ iCurrentLib = iLib; ++ iCurrentRealLib = iRealLib; ++ isLib=false; ++ } ++ } ++ } ++ } ++ if (poCurrentWord) { ++ for (iLib=0;iLib<dictmask.size();iLib++) { ++ if (dictmask[iLib].type != InstantDictType_LOCAL) ++ continue; ++ iRealLib = dictmask[iLib].index; ++ if (isLib && (iLib == iCurrentLib)) ++ continue; ++ if (iCurrent[iLib].idx!=INVALID_INDEX) { ++ if (iCurrent[iLib].idx>=narticles(iRealLib) || iCurrent[iLib].idx<=0) ++ continue; ++ } ++ if (GetWordPrev(iCurrent[iLib].idx, pidx, iRealLib, true, servercollatefunc)) { ++ word = poGetWord(pidx, iRealLib, servercollatefunc); ++ if (strcmp(poCurrentWord, word) == 0) { ++ iCurrent[iLib].idx=pidx; ++ } ++ } ++ } ++ for (iLib=0;iLib<dictmask.size();iLib++) { ++ if (dictmask[iLib].type != InstantDictType_LOCAL) ++ continue; ++ iRealLib = dictmask[iLib].index; ++ if ((!isLib) && (iLib == iCurrentLib)) ++ continue; ++ if (iCurrent[iLib].synidx==UNSET_INDEX) ++ continue; ++ if (iCurrent[iLib].synidx!=INVALID_INDEX) { ++ if (iCurrent[iLib].synidx>=nsynarticles(iRealLib) || iCurrent[iLib].synidx<=0) ++ continue; ++ } ++ if (GetWordPrev(iCurrent[iLib].synidx, pidx, iRealLib, false, servercollatefunc)) { ++ word = poGetSynonymWord(pidx, iRealLib, servercollatefunc); ++ if (strcmp(poCurrentWord, word) == 0) { ++ iCurrent[iLib].synidx=pidx; ++ } ++ } ++ } ++ if (isLib) { ++ GetWordPrev(iCurrent[iCurrentLib].idx, pidx, iCurrentRealLib, true, servercollatefunc); ++ iCurrent[iCurrentLib].idx = pidx; ++ } else { ++ GetWordPrev(iCurrent[iCurrentLib].synidx, pidx, iCurrentRealLib, false, servercollatefunc); ++ iCurrent[iCurrentLib].synidx = pidx; ++ } ++ } ++ return poCurrentWord; ++} ++ ++bool Libs::LookupSynonymSimilarWord(const gchar* sWord, glong &iSynonymWordIndex, glong &synidx_suggest, size_t iLib, int servercollatefunc) ++{ ++ if (oLib[iLib]->syn_file.get() == NULL) ++ return false; ++ ++ glong iIndex; ++ glong iIndex_suggest; ++ bool bFound=false; ++ gchar *casestr; ++ bool bLookup; ++ ++ if (!bFound) { ++ // to lower case. ++ casestr = g_utf8_strdown(sWord, -1); ++ if (strcmp(casestr, sWord)) { ++ bLookup = oLib[iLib]->LookupSynonym(casestr, iIndex, iIndex_suggest, EnableCollationLevel, servercollatefunc); ++ if(bLookup) ++ bFound=true; ++ } ++ g_free(casestr); ++ // to upper case. ++ if (!bFound) { ++ casestr = g_utf8_strup(sWord, -1); ++ if (strcmp(casestr, sWord)) { ++ bLookup = oLib[iLib]->LookupSynonym(casestr, iIndex, iIndex_suggest, EnableCollationLevel, servercollatefunc); ++ if(bLookup) ++ bFound=true; ++ } ++ g_free(casestr); ++ } ++ // Upper the first character and lower others. ++ if (!bFound) { ++ gchar *nextchar = g_utf8_next_char(sWord); ++ gchar *firstchar = g_utf8_strup(sWord, nextchar - sWord); ++ nextchar = g_utf8_strdown(nextchar, -1); ++ casestr = g_strdup_printf("%s%s", firstchar, nextchar); ++ g_free(firstchar); ++ g_free(nextchar); ++ if (strcmp(casestr, sWord)) { ++ bLookup = oLib[iLib]->LookupSynonym(casestr, iIndex, iIndex_suggest, EnableCollationLevel, servercollatefunc); ++ if(bLookup) ++ bFound=true; ++ } ++ g_free(casestr); ++ } ++ if (!bFound) { ++ iIndex = iSynonymWordIndex; ++ glong pidx; ++ const gchar *cword; ++ do { ++ if (GetWordPrev(iIndex, pidx, iLib, false, servercollatefunc)) { ++ cword = poGetSynonymWord(pidx, iLib, servercollatefunc); ++ if (stardict_casecmp(cword, sWord, EnableCollationLevel, CollateFunction, servercollatefunc)==0) { ++ iIndex = pidx; ++ bFound=true; ++ } else { ++ break; ++ } ++ } else { ++ break; ++ } ++ } while (true); ++ if (!bFound) { ++ if (iIndex!=INVALID_INDEX) { ++ cword = poGetSynonymWord(iIndex, iLib, servercollatefunc); ++ if (stardict_casecmp(cword, sWord, EnableCollationLevel, CollateFunction, servercollatefunc)==0) { ++ bFound=true; ++ } ++ } ++ } ++ } ++ } ++ if (bFound) { ++ iSynonymWordIndex = iIndex; ++ synidx_suggest = iIndex_suggest; ++ } ++ return bFound; ++} ++ ++bool Libs::LookupSimilarWord(const gchar* sWord, glong & iWordIndex, glong &idx_suggest, size_t iLib, int servercollatefunc) ++{ ++ glong iIndex; ++ bool bFound=false; ++ gchar *casestr; ++ ++ if (!bFound) { ++ // to lower case. ++ casestr = g_utf8_strdown(sWord, -1); ++ if (strcmp(casestr, sWord)) { ++ if(oLib[iLib]->Lookup(casestr, iIndex, idx_suggest, EnableCollationLevel, servercollatefunc)) ++ bFound=true; ++ } ++ g_free(casestr); ++ // to upper case. ++ if (!bFound) { ++ casestr = g_utf8_strup(sWord, -1); ++ if (strcmp(casestr, sWord)) { ++ if(oLib[iLib]->Lookup(casestr, iIndex, idx_suggest, EnableCollationLevel, servercollatefunc)) ++ bFound=true; ++ } ++ g_free(casestr); ++ } ++ // Upper the first character and lower others. ++ if (!bFound) { ++ gchar *nextchar = g_utf8_next_char(sWord); ++ gchar *firstchar = g_utf8_strup(sWord, nextchar - sWord); ++ nextchar = g_utf8_strdown(nextchar, -1); ++ casestr = g_strdup_printf("%s%s", firstchar, nextchar); ++ g_free(firstchar); ++ g_free(nextchar); ++ if (strcmp(casestr, sWord)) { ++ if(oLib[iLib]->Lookup(casestr, iIndex, idx_suggest, EnableCollationLevel, servercollatefunc)) ++ bFound=true; ++ } ++ g_free(casestr); ++ } ++ if (!bFound) { ++ iIndex = iWordIndex; ++ glong pidx; ++ const gchar *cword; ++ do { ++ if (GetWordPrev(iIndex, pidx, iLib, true, servercollatefunc)) { ++ cword = poGetWord(pidx, iLib, servercollatefunc); ++ if (stardict_casecmp(cword, sWord, EnableCollationLevel, CollateFunction, servercollatefunc)==0) { ++ iIndex = pidx; ++ bFound=true; ++ } else { ++ break; ++ } ++ } else { ++ break; ++ } ++ } while (true); ++ if (!bFound) { ++ if (iIndex!=INVALID_INDEX) { ++ cword = poGetWord(iIndex, iLib, servercollatefunc); ++ if (stardict_casecmp(cword, sWord, EnableCollationLevel, CollateFunction, servercollatefunc)==0) { ++ bFound=true; ++ } ++ } ++ } ++ } ++ } ++ ++ if (bIsPureEnglish(sWord)) { ++ // If not Found , try other status of sWord. ++ size_t iWordLen=strlen(sWord); ++ bool isupcase; ++ ++ gchar *sNewWord = (gchar *)g_malloc(iWordLen + 1); ++ ++ //cut one char "s" or "d" ++ if(!bFound && iWordLen>1) { ++ isupcase = sWord[iWordLen-1]=='S' || !strncmp(&sWord[iWordLen-2],"ED",2); ++ if (isupcase || sWord[iWordLen-1]=='s' || !strncmp(&sWord[iWordLen-2],"ed",2)) { ++ strcpy(sNewWord,sWord); ++ sNewWord[iWordLen-1]='\0'; // cut "s" or "d" ++ if (oLib[iLib]->Lookup(sNewWord, iIndex, idx_suggest, EnableCollationLevel, servercollatefunc)) ++ bFound=true; ++ else if (isupcase || g_ascii_isupper(sWord[0])) { ++ casestr = g_ascii_strdown(sNewWord, -1); ++ if (strcmp(casestr, sNewWord)) { ++ if(oLib[iLib]->Lookup(casestr, iIndex, idx_suggest, EnableCollationLevel, servercollatefunc)) ++ bFound=true; ++ } ++ g_free(casestr); ++ } ++ } ++ } ++ ++ //cut "ly" ++ if(!bFound && iWordLen>2) { ++ isupcase = !strncmp(&sWord[iWordLen-2],"LY",2); ++ if (isupcase || (!strncmp(&sWord[iWordLen-2],"ly",2))) { ++ strcpy(sNewWord,sWord); ++ sNewWord[iWordLen-2]='\0'; // cut "ly" ++ if (iWordLen>5 && sNewWord[iWordLen-3]==sNewWord[iWordLen-4] ++ && !bIsVowel(sNewWord[iWordLen-4]) && ++ bIsVowel(sNewWord[iWordLen-5])) {//doubled ++ ++ sNewWord[iWordLen-3]='\0'; ++ if(oLib[iLib]->Lookup(sNewWord, iIndex, idx_suggest, EnableCollationLevel, servercollatefunc)) ++ bFound=true; ++ else { ++ if (isupcase || g_ascii_isupper(sWord[0])) { ++ casestr = g_ascii_strdown(sNewWord, -1); ++ if (strcmp(casestr, sNewWord)) { ++ if(oLib[iLib]->Lookup(casestr, iIndex, idx_suggest, EnableCollationLevel, servercollatefunc)) ++ bFound=true; ++ } ++ g_free(casestr); ++ } ++ if (!bFound) ++ sNewWord[iWordLen-3]=sNewWord[iWordLen-4]; //restore ++ } ++ } ++ if (!bFound) { ++ if (oLib[iLib]->Lookup(sNewWord, iIndex, idx_suggest, EnableCollationLevel, servercollatefunc)) ++ bFound=true; ++ else if (isupcase || g_ascii_isupper(sWord[0])) { ++ casestr = g_ascii_strdown(sNewWord, -1); ++ if (strcmp(casestr, sNewWord)) { ++ if(oLib[iLib]->Lookup(casestr, iIndex, idx_suggest, EnableCollationLevel, servercollatefunc)) ++ bFound=true; ++ } ++ g_free(casestr); ++ } ++ } ++ } ++ } ++ ++ //cut "ing" ++ if(!bFound && iWordLen>3) { ++ isupcase = !strncmp(&sWord[iWordLen-3],"ING",3); ++ if (isupcase || !strncmp(&sWord[iWordLen-3],"ing",3) ) { ++ strcpy(sNewWord,sWord); ++ sNewWord[iWordLen-3]='\0'; ++ if ( iWordLen>6 && (sNewWord[iWordLen-4]==sNewWord[iWordLen-5]) ++ && !bIsVowel(sNewWord[iWordLen-5]) && ++ bIsVowel(sNewWord[iWordLen-6])) { //doubled ++ sNewWord[iWordLen-4]='\0'; ++ if (oLib[iLib]->Lookup(sNewWord, iIndex, idx_suggest, EnableCollationLevel, servercollatefunc)) ++ bFound=true; ++ else { ++ if (isupcase || g_ascii_isupper(sWord[0])) { ++ casestr = g_ascii_strdown(sNewWord, -1); ++ if (strcmp(casestr, sNewWord)) { ++ if(oLib[iLib]->Lookup(casestr, iIndex, idx_suggest, EnableCollationLevel, servercollatefunc)) ++ bFound=true; ++ } ++ g_free(casestr); ++ } ++ if (!bFound) ++ sNewWord[iWordLen-4]=sNewWord[iWordLen-5]; //restore ++ } ++ } ++ if( !bFound ) { ++ if (oLib[iLib]->Lookup(sNewWord, iIndex, idx_suggest, EnableCollationLevel, servercollatefunc)) ++ bFound=true; ++ else if (isupcase || g_ascii_isupper(sWord[0])) { ++ casestr = g_ascii_strdown(sNewWord, -1); ++ if (strcmp(casestr, sNewWord)) { ++ if(oLib[iLib]->Lookup(casestr, iIndex, idx_suggest, EnableCollationLevel, servercollatefunc)) ++ bFound=true; ++ } ++ g_free(casestr); ++ } ++ } ++ if(!bFound) { ++ if (isupcase) ++ strcat(sNewWord,"E"); // add a char "E" ++ else ++ strcat(sNewWord,"e"); // add a char "e" ++ if(oLib[iLib]->Lookup(sNewWord, iIndex, idx_suggest, EnableCollationLevel, servercollatefunc)) ++ bFound=true; ++ else if (isupcase || g_ascii_isupper(sWord[0])) { ++ casestr = g_ascii_strdown(sNewWord, -1); ++ if (strcmp(casestr, sNewWord)) { ++ if(oLib[iLib]->Lookup(casestr, iIndex, idx_suggest, EnableCollationLevel, servercollatefunc)) ++ bFound=true; ++ } ++ g_free(casestr); ++ } ++ } ++ } ++ } ++ ++ //cut two char "es" ++ if(!bFound && iWordLen>3) { ++ isupcase = (!strncmp(&sWord[iWordLen-2],"ES",2) && ++ (sWord[iWordLen-3] == 'S' || ++ sWord[iWordLen-3] == 'X' || ++ sWord[iWordLen-3] == 'O' || ++ (iWordLen >4 && sWord[iWordLen-3] == 'H' && ++ (sWord[iWordLen-4] == 'C' || ++ sWord[iWordLen-4] == 'S')))); ++ if (isupcase || ++ (!strncmp(&sWord[iWordLen-2],"es",2) && ++ (sWord[iWordLen-3] == 's' || sWord[iWordLen-3] == 'x' || ++ sWord[iWordLen-3] == 'o' || ++ (iWordLen >4 && sWord[iWordLen-3] == 'h' && ++ (sWord[iWordLen-4] == 'c' || sWord[iWordLen-4] == 's'))))) { ++ strcpy(sNewWord,sWord); ++ sNewWord[iWordLen-2]='\0'; ++ if(oLib[iLib]->Lookup(sNewWord, iIndex, idx_suggest, EnableCollationLevel, servercollatefunc)) ++ bFound=true; ++ else if (isupcase || g_ascii_isupper(sWord[0])) { ++ casestr = g_ascii_strdown(sNewWord, -1); ++ if (strcmp(casestr, sNewWord)) { ++ if(oLib[iLib]->Lookup(casestr, iIndex, idx_suggest, EnableCollationLevel, servercollatefunc)) ++ bFound=true; ++ } ++ g_free(casestr); ++ } ++ } ++ } ++ ++ //cut "ed" ++ if (!bFound && iWordLen>3) { ++ isupcase = !strncmp(&sWord[iWordLen-2],"ED",2); ++ if (isupcase || !strncmp(&sWord[iWordLen-2],"ed",2)) { ++ strcpy(sNewWord,sWord); ++ sNewWord[iWordLen-2]='\0'; ++ if (iWordLen>5 && (sNewWord[iWordLen-3]==sNewWord[iWordLen-4]) ++ && !bIsVowel(sNewWord[iWordLen-4]) && ++ bIsVowel(sNewWord[iWordLen-5])) {//doubled ++ sNewWord[iWordLen-3]='\0'; ++ if (oLib[iLib]->Lookup(sNewWord, iIndex, idx_suggest, EnableCollationLevel, servercollatefunc)) ++ bFound=true; ++ else { ++ if (isupcase || g_ascii_isupper(sWord[0])) { ++ casestr = g_ascii_strdown(sNewWord, -1); ++ if (strcmp(casestr, sNewWord)) { ++ if(oLib[iLib]->Lookup(casestr, iIndex, idx_suggest, EnableCollationLevel, servercollatefunc)) ++ bFound=true; ++ } ++ g_free(casestr); ++ } ++ if (!bFound) ++ sNewWord[iWordLen-3]=sNewWord[iWordLen-4]; //restore ++ } ++ } ++ if (!bFound) { ++ if (oLib[iLib]->Lookup(sNewWord, iIndex, idx_suggest, EnableCollationLevel, servercollatefunc)) ++ bFound=true; ++ else if (isupcase || g_ascii_isupper(sWord[0])) { ++ casestr = g_ascii_strdown(sNewWord, -1); ++ if (strcmp(casestr, sNewWord)) { ++ if(oLib[iLib]->Lookup(casestr, iIndex, idx_suggest, EnableCollationLevel, servercollatefunc)) ++ bFound=true; ++ } ++ g_free(casestr); ++ } ++ } ++ } ++ } ++ ++ // cut "ied" , add "y". ++ if (!bFound && iWordLen>3) { ++ isupcase = !strncmp(&sWord[iWordLen-3],"IED",3); ++ if (isupcase || (!strncmp(&sWord[iWordLen-3],"ied",3))) { ++ strcpy(sNewWord,sWord); ++ sNewWord[iWordLen-3]='\0'; ++ if (isupcase) ++ strcat(sNewWord,"Y"); // add a char "Y" ++ else ++ strcat(sNewWord,"y"); // add a char "y" ++ if (oLib[iLib]->Lookup(sNewWord, iIndex, idx_suggest, EnableCollationLevel, servercollatefunc)) ++ bFound=true; ++ else if (isupcase || g_ascii_isupper(sWord[0])) { ++ casestr = g_ascii_strdown(sNewWord, -1); ++ if (strcmp(casestr, sNewWord)) { ++ if(oLib[iLib]->Lookup(casestr, iIndex, idx_suggest, EnableCollationLevel, servercollatefunc)) ++ bFound=true; ++ } ++ g_free(casestr); ++ } ++ } ++ } ++ ++ // cut "ies" , add "y". ++ if (!bFound && iWordLen>3) { ++ isupcase = !strncmp(&sWord[iWordLen-3],"IES",3); ++ if (isupcase || (!strncmp(&sWord[iWordLen-3],"ies",3))) { ++ strcpy(sNewWord,sWord); ++ sNewWord[iWordLen-3]='\0'; ++ if (isupcase) ++ strcat(sNewWord,"Y"); // add a char "Y" ++ else ++ strcat(sNewWord,"y"); // add a char "y" ++ if(oLib[iLib]->Lookup(sNewWord, iIndex, idx_suggest, EnableCollationLevel, servercollatefunc)) ++ bFound=true; ++ else if (isupcase || g_ascii_isupper(sWord[0])) { ++ casestr = g_ascii_strdown(sNewWord, -1); ++ if (strcmp(casestr, sNewWord)) { ++ if(oLib[iLib]->Lookup(casestr, iIndex, idx_suggest, EnableCollationLevel, servercollatefunc)) ++ bFound=true; ++ } ++ g_free(casestr); ++ } ++ } ++ } ++ ++ // cut "er". ++ if (!bFound && iWordLen>2) { ++ isupcase = !strncmp(&sWord[iWordLen-2],"ER",2); ++ if (isupcase || (!strncmp(&sWord[iWordLen-2],"er",2))) { ++ strcpy(sNewWord,sWord); ++ sNewWord[iWordLen-2]='\0'; ++ if(oLib[iLib]->Lookup(sNewWord, iIndex, idx_suggest, EnableCollationLevel, servercollatefunc)) ++ bFound=true; ++ else if (isupcase || g_ascii_isupper(sWord[0])) { ++ casestr = g_ascii_strdown(sNewWord, -1); ++ if (strcmp(casestr, sNewWord)) { ++ if(oLib[iLib]->Lookup(casestr, iIndex, idx_suggest, EnableCollationLevel, servercollatefunc)) ++ bFound=true; ++ } ++ g_free(casestr); ++ } ++ } ++ } ++ ++ // cut "est". ++ if (!bFound && iWordLen>3) { ++ isupcase = !strncmp(&sWord[iWordLen-3], "EST", 3); ++ if (isupcase || (!strncmp(&sWord[iWordLen-3],"est", 3))) { ++ strcpy(sNewWord,sWord); ++ sNewWord[iWordLen-3]='\0'; ++ if(oLib[iLib]->Lookup(sNewWord, iIndex, idx_suggest, EnableCollationLevel, servercollatefunc)) ++ bFound=true; ++ else if (isupcase || g_ascii_isupper(sWord[0])) { ++ casestr = g_ascii_strdown(sNewWord, -1); ++ if (strcmp(casestr, sNewWord)) { ++ if(oLib[iLib]->Lookup(casestr, iIndex, idx_suggest, EnableCollationLevel, servercollatefunc)) ++ bFound=true; ++ } ++ g_free(casestr); ++ } ++ } ++ } ++ ++ g_free(sNewWord); ++ } ++ ++ if (bFound) ++ iWordIndex = iIndex; ++#if 0 ++ else { ++ //don't change iWordIndex here. ++ //when LookupSimilarWord all failed too, we want to use the old LookupWord index to list words. ++ //iWordIndex = INVALID_INDEX; ++ } ++#endif ++ return bFound; ++} ++ ++bool Libs::SimpleLookupWord(const gchar* sWord, glong & iWordIndex, glong &idx_suggest, size_t iLib, int servercollatefunc) ++{ ++ bool bFound = oLib[iLib]->Lookup(sWord, iWordIndex, idx_suggest, EnableCollationLevel, servercollatefunc); ++ if (!bFound) ++ bFound = LookupSimilarWord(sWord, iWordIndex, idx_suggest, iLib, servercollatefunc); ++ return bFound; ++} ++ ++bool Libs::SimpleLookupSynonymWord(const gchar* sWord, glong & iWordIndex, glong &synidx_suggest, size_t iLib, int servercollatefunc) ++{ ++ bool bFound = oLib[iLib]->LookupSynonym(sWord, iWordIndex, synidx_suggest, EnableCollationLevel, servercollatefunc); ++ if (!bFound) ++ bFound = LookupSynonymSimilarWord(sWord, iWordIndex, synidx_suggest, iLib, servercollatefunc); ++ return bFound; ++} ++ ++struct Fuzzystruct { ++ char * pMatchWord; ++ int iMatchWordDistance; ++}; ++ ++static inline bool operator<(const Fuzzystruct & lh, const Fuzzystruct & rh) { ++ if (lh.iMatchWordDistance!=rh.iMatchWordDistance) ++ return lh.iMatchWordDistance<rh.iMatchWordDistance; ++ ++ if (lh.pMatchWord && rh.pMatchWord) ++ return stardict_strcmp(lh.pMatchWord, rh.pMatchWord)<0; ++ ++ return false; ++} ++ ++static inline void unicode_strdown(gunichar *str) ++{ ++ while (*str) { ++ *str=g_unichar_tolower(*str); ++ ++str; ++ } ++} ++ ++bool Libs::LookupWithFuzzy(const gchar *sWord, gchar *reslist[], gint reslist_size, std::vector<InstantDictIndex> &dictmask) ++{ ++ if (sWord[0] == '\0') ++ return false; ++ ++ std::vector<Fuzzystruct> oFuzzystruct(reslist_size); ++ ++ for (int i=0; i<reslist_size; i++) { ++ oFuzzystruct[i].pMatchWord = NULL; ++ oFuzzystruct[i].iMatchWordDistance = iMaxFuzzyDistance; ++ } ++ int iMaxDistance = iMaxFuzzyDistance; ++ int iDistance; ++ bool Found = false; ++ EditDistance oEditDistance; ++ ++ glong iCheckWordLen; ++ const char *sCheck; ++ gunichar *ucs4_str1, *ucs4_str2; ++ glong ucs4_str2_len; ++ ++ ucs4_str2 = g_utf8_to_ucs4_fast(sWord, -1, &ucs4_str2_len); ++ unicode_strdown(ucs4_str2); ++ ++ std::vector<Dict *>::size_type iRealLib; ++ for (std::vector<InstantDictIndex>::size_type iLib=0; iLib<dictmask.size(); iLib++) { ++ if (dictmask[iLib].type != InstantDictType_LOCAL) ++ continue; ++ iRealLib = dictmask[iLib].index; ++ for (gint synLib=0; synLib<2; synLib++) { ++ if (synLib==1) { ++ if (oLib[iRealLib]->syn_file.get()==NULL) ++ break; ++ } ++ show_progress->notify_about_work(); ++ ++ //if (stardict_strcmp(sWord, poGetWord(0,iRealLib))>=0 && stardict_strcmp(sWord, poGetWord(narticles(iRealLib)-1,iRealLib))<=0) { ++ //there are Chinese dicts and English dicts... ++ if (TRUE) { ++ glong iwords; ++ if (synLib==0) ++ iwords = narticles(iRealLib); ++ else ++ iwords = nsynarticles(iRealLib); ++ for (glong index=0; index<iwords; index++) { ++ // Need to deal with same word in index? But this will slow down processing in most case. ++ if (synLib==0) ++ sCheck = poGetOrigWord(index,iRealLib); ++ else ++ sCheck = poGetOrigSynonymWord(index,iRealLib); ++ // tolower and skip too long or too short words ++ iCheckWordLen = g_utf8_strlen(sCheck, -1); ++ if (iCheckWordLen-ucs4_str2_len>=iMaxDistance || ++ ucs4_str2_len-iCheckWordLen>=iMaxDistance) ++ continue; ++ ucs4_str1 = g_utf8_to_ucs4_fast(sCheck, -1, NULL); ++ if (iCheckWordLen > ucs4_str2_len) ++ ucs4_str1[ucs4_str2_len]=0; ++ unicode_strdown(ucs4_str1); ++ ++ iDistance = oEditDistance.CalEditDistance(ucs4_str1, ucs4_str2, iMaxDistance); ++ g_free(ucs4_str1); ++ if (iDistance<iMaxDistance && iDistance < ucs4_str2_len) { ++ // when ucs4_str2_len=1,2 we need less fuzzy. ++ Found = true; ++ bool bAlreadyInList = false; ++ int iMaxDistanceAt=0; ++ for (int j=0; j<reslist_size; j++) { ++ if (oFuzzystruct[j].pMatchWord && ++ strcmp(oFuzzystruct[j].pMatchWord,sCheck)==0 ) {//already in list ++ bAlreadyInList = true; ++ break; ++ } ++ //find the position,it will certainly be found (include the first time) as iMaxDistance is set by last time. ++ if (oFuzzystruct[j].iMatchWordDistance == iMaxDistance ) { ++ iMaxDistanceAt = j; ++ } ++ } ++ if (!bAlreadyInList) { ++ if (oFuzzystruct[iMaxDistanceAt].pMatchWord) ++ g_free(oFuzzystruct[iMaxDistanceAt].pMatchWord); ++ oFuzzystruct[iMaxDistanceAt].pMatchWord = g_strdup(sCheck); ++ oFuzzystruct[iMaxDistanceAt].iMatchWordDistance = iDistance; ++ // calc new iMaxDistance ++ iMaxDistance = iDistance; ++ for (int j=0; j<reslist_size; j++) { ++ if (oFuzzystruct[j].iMatchWordDistance > iMaxDistance) ++ iMaxDistance = oFuzzystruct[j].iMatchWordDistance; ++ } // calc new iMaxDistance ++ } // add to list ++ } // find one ++ } // each word ++ } // ok for search ++ } // synLib ++ } // each lib ++ g_free(ucs4_str2); ++ ++ if (Found)// sort with distance ++ std::sort(oFuzzystruct.begin(), oFuzzystruct.end()); ++ ++ for (gint i=0; i<reslist_size; ++i) ++ reslist[i]=oFuzzystruct[i].pMatchWord; ++ ++ return Found; ++} ++ ++static inline bool less_for_compare(const char *lh, const char *rh) { ++ return stardict_strcmp(lh, rh)<0; ++} ++ ++gint Libs::LookupWithRule(const gchar *word, gchar **ppMatchWord, std::vector<InstantDictIndex> &dictmask) ++{ ++ glong aiIndex[MAX_MATCH_ITEM_PER_LIB+1]; ++ gint iMatchCount = 0; ++ GPatternSpec *pspec = g_pattern_spec_new(word); ++ ++ const gchar * sMatchWord; ++ bool bAlreadyInList; ++ std::vector<Dict *>::size_type iRealLib; ++ for (std::vector<InstantDictIndex>::size_type iLib=0; iLib<dictmask.size(); iLib++) { ++ //if(oLibs.LookdupWordsWithRule(pspec,aiIndex,MAX_MATCH_ITEM_PER_LIB+1-iMatchCount,iLib)) ++ // -iMatchCount,so save time,but may got less result and the word may repeat. ++ if (dictmask[iLib].type != InstantDictType_LOCAL) ++ continue; ++ iRealLib = dictmask[iLib].index; ++ if (oLib[iRealLib]->LookupWithRule(pspec, aiIndex, MAX_MATCH_ITEM_PER_LIB+1)) { ++ show_progress->notify_about_work(); ++ for (int i=0; aiIndex[i]!=-1; i++) { ++ sMatchWord = poGetOrigWord(aiIndex[i],iRealLib); ++ bAlreadyInList = false; ++ for (int j=0; j<iMatchCount; j++) { ++ if (strcmp(ppMatchWord[j],sMatchWord)==0) {//already in list ++ bAlreadyInList = true; ++ break; ++ } ++ } ++ if (!bAlreadyInList) ++ ppMatchWord[iMatchCount++] = g_strdup(sMatchWord); ++ } ++ } ++ if (oLib[iRealLib]->LookupWithRuleSynonym(pspec, aiIndex, MAX_MATCH_ITEM_PER_LIB+1)) { ++ show_progress->notify_about_work(); ++ for (int i=0; aiIndex[i]!=-1; i++) { ++ sMatchWord = poGetOrigSynonymWord(aiIndex[i],iRealLib); ++ bAlreadyInList = false; ++ for (int j=0; j<iMatchCount; j++) { ++ if (strcmp(ppMatchWord[j],sMatchWord)==0) {//already in list ++ bAlreadyInList = true; ++ break; ++ } ++ } ++ if (!bAlreadyInList) ++ ppMatchWord[iMatchCount++] = g_strdup(sMatchWord); ++ } ++ } ++ } ++ g_pattern_spec_free(pspec); ++ ++ if (iMatchCount)// sort it. ++ std::sort(ppMatchWord, ppMatchWord+iMatchCount, less_for_compare); ++ return iMatchCount; ++} ++ ++gint Libs::LookupWithRegex(const gchar *word, gchar **ppMatchWord, std::vector<InstantDictIndex> &dictmask) ++{ ++ glong aiIndex[MAX_MATCH_ITEM_PER_LIB+1]; ++ gint iMatchCount = 0; ++ GRegex *regex = g_regex_new(word, G_REGEX_OPTIMIZE, (GRegexMatchFlags)0, NULL); ++ ++ const gchar * sMatchWord; ++ bool bAlreadyInList; ++ std::vector<Dict *>::size_type iRealLib; ++ for (std::vector<InstantDictIndex>::size_type iLib=0; iLib<dictmask.size(); iLib++) { ++ //if(oLibs.LookdupWordsWithRule(pspec,aiIndex,MAX_MATCH_ITEM_PER_LIB+1-iMatchCount,iLib)) ++ // -iMatchCount,so save time,but may got less result and the word may repeat. ++ if (dictmask[iLib].type != InstantDictType_LOCAL) ++ continue; ++ iRealLib = dictmask[iLib].index; ++ if (oLib[iRealLib]->LookupWithRegex(regex, aiIndex, MAX_MATCH_ITEM_PER_LIB+1)) { ++ show_progress->notify_about_work(); ++ for (int i=0; aiIndex[i]!=-1; i++) { ++ sMatchWord = poGetOrigWord(aiIndex[i],iRealLib); ++ bAlreadyInList = false; ++ for (int j=0; j<iMatchCount; j++) { ++ if (strcmp(ppMatchWord[j],sMatchWord)==0) {//already in list ++ bAlreadyInList = true; ++ break; ++ } ++ } ++ if (!bAlreadyInList) ++ ppMatchWord[iMatchCount++] = g_strdup(sMatchWord); ++ } ++ } ++ if (oLib[iRealLib]->LookupWithRegexSynonym(regex, aiIndex, MAX_MATCH_ITEM_PER_LIB+1)) { ++ show_progress->notify_about_work(); ++ for (int i=0; aiIndex[i]!=-1; i++) { ++ sMatchWord = poGetOrigSynonymWord(aiIndex[i],iRealLib); ++ bAlreadyInList = false; ++ for (int j=0; j<iMatchCount; j++) { ++ if (strcmp(ppMatchWord[j],sMatchWord)==0) {//already in list ++ bAlreadyInList = true; ++ break; ++ } ++ } ++ if (!bAlreadyInList) ++ ppMatchWord[iMatchCount++] = g_strdup(sMatchWord); ++ } ++ } ++ } ++ g_regex_unref(regex); ++ ++ if (iMatchCount)// sort it. ++ std::sort(ppMatchWord, ppMatchWord+iMatchCount, less_for_compare); ++ return iMatchCount; ++} ++ ++bool Libs::LookupData(const gchar *sWord, std::vector<gchar *> *reslist, updateSearchDialog_func search_func, gpointer search_data, bool *cancel, std::vector<InstantDictIndex> &dictmask) ++{ ++ std::vector<std::string> SearchWords; ++ std::string SearchWord; ++ const char *p=sWord; ++ while (*p) { ++ if (*p=='\\') { ++ p++; ++ switch (*p) { ++ case ' ': ++ SearchWord+=' '; ++ break; ++ case '\\': ++ SearchWord+='\\'; ++ break; ++ case 't': ++ SearchWord+='\t'; ++ break; ++ case 'n': ++ SearchWord+='\n'; ++ break; ++ default: ++ SearchWord+=*p; ++ } ++ } else if (*p == ' ') { ++ if (!SearchWord.empty()) { ++ SearchWords.push_back(SearchWord); ++ SearchWord.clear(); ++ } ++ } else { ++ SearchWord+=*p; ++ } ++ p++; ++ } ++ if (!SearchWord.empty()) { ++ SearchWords.push_back(SearchWord); ++ SearchWord.clear(); ++ } ++ if (SearchWords.empty()) ++ return false; ++ ++ glong search_count=0; ++ glong total_count=0; ++ if (search_func) { ++ for (std::vector<InstantDictIndex>::size_type i=0; i<dictmask.size(); ++i) { ++ if (dictmask[i].type == InstantDictType_LOCAL) ++ total_count += narticles(dictmask[i].index); ++ } ++ } ++ ++ guint32 max_size =0; ++ gchar *origin_data = NULL; ++ std::vector<InstantDictIndex>::size_type iRealLib; ++ for (std::vector<InstantDictIndex>::size_type i=0; i<dictmask.size(); ++i) { ++ if (dictmask[i].type != InstantDictType_LOCAL) ++ continue; ++ iRealLib = dictmask[i].index; ++ if (!oLib[iRealLib]->containSearchData()) ++ continue; ++ const gulong iwords = narticles(iRealLib); ++ const gchar *key; ++ guint32 offset, size; ++ for (gulong j=0; j<iwords; ++j) { ++ if (search_func) { ++ if (*cancel) ++ goto search_out; ++ if (search_count % 10000 == 0) { ++ search_func(search_data, (gdouble)search_count/(gdouble)total_count); ++ } ++ search_count++; ++ } ++ oLib[iRealLib]->get_key_and_data(j, &key, &offset, &size); ++ if (size>max_size) { ++ origin_data = (gchar *)g_realloc(origin_data, size); ++ max_size = size; ++ } ++ if (oLib[iRealLib]->SearchData(SearchWords, offset, size, origin_data)) { ++ if (reslist[i].empty() || strcmp(reslist[i].back(), key)) ++ reslist[i].push_back(g_strdup(key)); ++ } ++ } ++ } ++search_out: ++ g_free(origin_data); ++ KMP_end(); ++ ++ std::vector<InstantDictIndex>::size_type i; ++ for (i=0; i<dictmask.size(); ++i) ++ if (!reslist[i].empty()) ++ break; ++ ++ return i!=dictmask.size(); ++} ++ ++int Libs::GetStorageType(size_t iLib) ++{ ++ if (oLib[iLib]->storage == NULL) ++ return -1; ++ return oLib[iLib]->storage->is_file_or_db; ++} ++ ++const char *Libs::GetStorageFilePath(size_t iLib, const char *key) ++{ ++ if (oLib[iLib]->storage == NULL) ++ return NULL; ++ return oLib[iLib]->storage->get_file_path(key); ++} ++ ++const char *Libs::GetStorageFileContent(size_t iLib, const char *key) ++{ ++ if (oLib[iLib]->storage == NULL) ++ return NULL; ++ return oLib[iLib]->storage->get_file_content(key); ++} +diff -Nur stardict-3.0.1.orig//src/lib/treedict.cpp stardict-3.0.1/src/lib/treedict.cpp +--- stardict-3.0.1.orig//src/lib/treedict.cpp 2007-09-20 20:09:52.000000000 -0500 ++++ stardict-3.0.1/src/lib/treedict.cpp 2010-05-24 00:53:36.378667536 -0500 +@@ -29,6 +29,7 @@ + #include "getuint32.h" + + #include "treedict.hpp" ++#include <cstring> + + GtkTreeStore *TreeDict::model=NULL; + +diff -Nur stardict-3.0.1.orig//src/lib/treedict.cpp~ stardict-3.0.1/src/lib/treedict.cpp~ +--- stardict-3.0.1.orig//src/lib/treedict.cpp~ 1969-12-31 18:00:00.000000000 -0600 ++++ stardict-3.0.1/src/lib/treedict.cpp~ 2007-09-20 20:09:52.000000000 -0500 +@@ -0,0 +1,197 @@ ++/* ++ * This file part of StarDict - A international dictionary for GNOME. ++ * http://stardict.sourceforge.net ++ * ++ * 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 3 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 Library 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. ++ */ ++ ++/* ++ * Implementation of class to work with GtkTree ++ * based StarDict's dictionaries ++ */ ++#ifdef HAVE_CONFIG_H ++# include "config.h" ++#endif ++ ++#include "file.hpp" ++#include "getuint32.h" ++ ++#include "treedict.hpp" ++ ++GtkTreeStore *TreeDict::model=NULL; ++ ++TreeDict::TreeDict() ++{ ++ if (model) ++ return; ++ ++ // It is said G_TYPE_UINT will always be 32 bit. ++ // see http://bugzilla.gnome.org/show_bug.cgi?id=337966 ++ model = gtk_tree_store_new (3, G_TYPE_STRING, G_TYPE_UINT, G_TYPE_UINT); //word, offset, size ++} ++ ++bool TreeDict::load(const std::string& ifofilename) ++{ ++ gulong tdxfilesize; ++ if (!load_ifofile(ifofilename, &tdxfilesize)) ++ return false; ++ ++ std::string fullfilename(ifofilename); ++ fullfilename.replace(fullfilename.length()-sizeof("ifo")+1, sizeof("ifo")-1, "dict.dz"); ++ ++ if (g_file_test(fullfilename.c_str(), G_FILE_TEST_EXISTS)) { ++ dictdzfile.reset(new dictData); ++ if (!dictdzfile->open(fullfilename, 0)) { ++ //g_print("open file %s failed!\n",fullfilename); ++ return false; ++ } ++ } else { ++ fullfilename.erase(fullfilename.length()-sizeof(".dz")+1, sizeof(".dz")-1); ++ dictfile = fopen(fullfilename.c_str(),"rb"); ++ if (!dictfile) { ++ //g_print("open file %s failed!\n",fullfilename); ++ return false; ++ } ++ } ++ ++ fullfilename=ifofilename; ++ fullfilename.replace(fullfilename.length()-sizeof("ifo")+1, sizeof("ifo")-1, "tdx.gz"); ++ ++ gchar *buffer= NULL; ++ if (g_file_test(fullfilename.c_str(), G_FILE_TEST_EXISTS)) { ++ gzFile in; ++ in = gzopen(fullfilename.c_str(),"rb"); ++ if (in == NULL) { ++ //g_print("Open file %s failed!\n",idxfilename); ++ return false; ++ } ++ ++ buffer = (gchar *)g_malloc(tdxfilesize); ++ ++ gulong len; ++ len = gzread(in, buffer, tdxfilesize); ++ if (len < 0) { ++ g_free(buffer); ++ return false; ++ } ++ gzclose(in); ++ if (len != tdxfilesize) { ++ g_free(buffer); ++ return false; ++ } ++ } else { ++ fullfilename.erase(fullfilename.length()-sizeof(".gz")+1, sizeof(".gz")-1); ++ FILE *file; ++ if (!(file = fopen (fullfilename.c_str(), "rb"))) { ++ //g_print("Open file %s failed!\n",fullfilename); ++ return false; ++ } ++ buffer = (gchar *)g_malloc(tdxfilesize); ++ gulong read_len; ++ read_len = fread(buffer, 1, tdxfilesize, file); ++ fclose(file); ++ if (read_len!=tdxfilesize) { ++ g_free(buffer); ++ return false; ++ } ++ } ++ ++ gchar *tmp_buffer = buffer; ++ load_model(&tmp_buffer, NULL, 1); // tmp_buffer will be changed. ++ g_free(buffer); ++ return true; ++} ++ ++bool TreeDict::load_ifofile(const std::string& ifofilename, gulong *tdxfilesize) ++{ ++ DictInfo dict_info; ++ if (!dict_info.load_from_ifo_file(ifofilename, true)) ++ return false; ++ ++ *tdxfilesize = dict_info.index_file_size; ++ sametypesequence=dict_info.sametypesequence; ++ ++ return true; ++} ++ ++void TreeDict::load_model(gchar **buffer, GtkTreeIter *parent, guint32 count) ++{ ++ GtkTreeIter iter; ++ gchar *p1; ++ guint32 offset, size, subentry_count; ++ ++ for (guint32 i=0; i< count; i++) { ++ p1 = *buffer + strlen(*buffer) +1; ++ offset = g_ntohl(get_uint32(p1)); ++ p1 += sizeof(guint32); ++ size = g_ntohl(get_uint32(p1)); ++ p1 += sizeof(guint32); ++ subentry_count = g_ntohl(get_uint32(p1)); ++ p1 += sizeof(guint32); ++ gtk_tree_store_append(model, &iter, parent); ++ gtk_tree_store_set(model, &iter, 0, *buffer, 1, offset, 2, size, -1); ++ *buffer = p1; ++ if (subentry_count) ++ load_model(buffer, &iter, subentry_count); ++ } ++} ++ ++ ++/**************************************************/ ++TreeDicts::TreeDicts() ++{ ++} ++ ++TreeDicts::~TreeDicts() ++{ ++ for (std::vector<TreeDict *>::iterator it=oTreeDict.begin(); ++ it!=oTreeDict.end(); ++it) ++ delete *it; ++} ++ ++void TreeDicts::load_dict(const std::string& url) ++{ ++ TreeDict *lib = new TreeDict; ++ if (lib->load(url)) ++ oTreeDict.push_back(lib); ++ else ++ delete lib; ++} ++ ++class TreeDictLoader { ++public: ++ TreeDictLoader(TreeDicts& td_) : td(td_) {} ++ void operator()(const std::string& url, bool disable) { ++ if (!disable) ++ td.load_dict(url); ++ } ++private: ++ TreeDicts& td; ++}; ++ ++GtkTreeStore* TreeDicts::Load(const strlist_t& tree_dicts_dirs, ++ const strlist_t& order_list, ++ const strlist_t& disable_list) ++{ ++ TreeDictLoader load(*this); ++ for_each_file(tree_dicts_dirs, ".ifo", order_list, disable_list, load); ++ ++ return TreeDict::get_model(); ++} ++ ++gchar* TreeDicts::poGetWordData(guint32 offset, guint32 size, int iTreeDict) ++{ ++ return oTreeDict[iTreeDict]->GetWordData(offset, size); ++} +diff -Nur stardict-3.0.1.orig//src/pangoview.cpp stardict-3.0.1/src/pangoview.cpp +--- stardict-3.0.1.orig//src/pangoview.cpp 2007-09-25 02:11:48.000000000 -0500 ++++ stardict-3.0.1/src/pangoview.cpp 2010-05-24 00:53:36.378667536 -0500 +@@ -22,6 +22,8 @@ + # include "config.h" + #endif + ++#include <cstring> ++ + #include "gtktextviewpango.h" + #include "utils.h" + #include "skin.h"//for SkinCursor definition +diff -Nur stardict-3.0.1.orig//src/pangoview.cpp~ stardict-3.0.1/src/pangoview.cpp~ +--- stardict-3.0.1.orig//src/pangoview.cpp~ 1969-12-31 18:00:00.000000000 -0600 ++++ stardict-3.0.1/src/pangoview.cpp~ 2007-09-25 02:11:48.000000000 -0500 +@@ -0,0 +1,499 @@ ++/* ++ * This file part of StarDict - A international dictionary for GNOME. ++ * http://stardict.sourceforge.net ++ * Copyright (C) 2005-2006 Evgeniy <dushistov@mail.ru> ++ * ++ * 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 3 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 Library 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. ++ */ ++ ++#ifdef HAVE_CONFIG_H ++# include "config.h" ++#endif ++ ++#include "gtktextviewpango.h" ++#include "utils.h" ++#include "skin.h"//for SkinCursor definition ++ ++#include "pangoview.h" ++ ++class TextPangoWidget : public PangoWidgetBase { ++public: ++ TextPangoWidget(); ++ GtkWidget *widget() { return GTK_WIDGET(textview_); } ++ ++ void clear(); ++ void append_mark(const char *mark); ++ void append_pixbuf(GdkPixbuf *pixbuf, const char *label); ++ void append_widget(GtkWidget *widget); ++ void begin_update(); ++ void end_update(); ++ std::string get_text(); ++ void append_pango_text_with_links(const std::string&, ++ const LinksPosList&); ++protected: ++ void do_set_text(const char *str); ++ void do_append_text(const char *str); ++ void do_append_pango_text(const char *str); ++ void do_set_pango_text(const char *str); ++private: ++ GtkTextView *textview_; ++ std::list<GtkTextMark *> marklist_; ++ struct TextBufPos { ++ gint beg_; ++ gint end_; ++ std::string link_; ++ TextBufPos(gint beg, gint end, std::string link): beg_(beg), end_(end), link_(link) {} ++ }; ++ typedef std::vector<TextBufPos> TextBufLinks; ++ ++ TextBufLinks tb_links_; ++ GtkTextIter iter_; ++ SkinCursor hand_cursor_, regular_cursor_; ++ ++ static gboolean on_mouse_move(GtkWidget *, GdkEventMotion *, gpointer); ++ static gboolean on_button_release(GtkWidget *, GdkEventButton *, gpointer); ++ ++ void goto_begin(); ++ void goto_end(); ++ TextBufLinks::const_iterator find_link(gint x, gint y); ++}; ++ ++class LabelPangoWidget : public PangoWidgetBase { ++public: ++ LabelPangoWidget(); ++ GtkWidget *widget() { return GTK_WIDGET(label_); } ++ ++ void clear(); ++ void append_mark(const char *mark) {} ++ void append_pixbuf(GdkPixbuf *pixbuf, const char *label); ++ void append_widget(GtkWidget *widget); ++ std::string get_text(); ++ void modify_bg(GtkStateType state, const GdkColor *color); ++protected: ++ void do_set_text(const char *str); ++ void do_append_text(const char *str); ++ void do_append_pango_text(const char *str); ++ void do_set_pango_text(const char *str); ++private: ++ GtkLabel *label_; ++ GtkWidget *viewport_; ++}; ++ ++ ++void PangoWidgetBase::begin_update() ++{ ++ update_ = true; ++} ++ ++void PangoWidgetBase::end_update() ++{ ++ if (update_) { ++ update_ = false; ++ do_append_pango_text(cache_.c_str()); ++ cache_.clear(); ++ } ++} ++ ++void PangoWidgetBase::append_text(const char *str) ++{ ++ if (update_) { ++ gchar *mark = g_markup_escape_text(str, -1); ++ cache_ += mark; ++ g_free(mark); ++ } else { ++ do_append_text(str); ++ } ++} ++ ++void PangoWidgetBase::append_pango_text(const char *str) ++{ ++ if (update_) ++ cache_ += str; ++ else ++ do_append_pango_text(str); ++} ++ ++void PangoWidgetBase::append_pango_text_with_links(const std::string& str, ++ const LinksPosList&) ++{ ++ append_pango_text(str.c_str()); ++} ++ ++void PangoWidgetBase::set_pango_text(const char *str) ++{ ++ if (update_) ++ cache_ = str; ++ else ++ do_set_pango_text(str); ++} ++ ++ ++void TextPangoWidget::begin_update() ++{ ++ PangoWidgetBase::begin_update(); ++ gtk_text_buffer_begin_user_action( ++ gtk_text_view_get_buffer(textview_)); ++} ++ ++ ++void TextPangoWidget::end_update() ++{ ++ PangoWidgetBase::end_update(); ++ gtk_text_buffer_end_user_action(gtk_text_view_get_buffer(textview_)); ++} ++ ++ ++TextPangoWidget::TextPangoWidget() ++{ ++ hand_cursor_.reset(gdk_cursor_new(GDK_HAND2)); ++ regular_cursor_.reset(gdk_cursor_new(GDK_XTERM)); ++ textview_ = GTK_TEXT_VIEW(gtk_text_view_new()); ++ gtk_widget_show(GTK_WIDGET(textview_)); ++ gtk_text_view_set_editable(textview_, FALSE); ++ gtk_text_view_set_cursor_visible(textview_, FALSE); ++ gtk_text_view_set_wrap_mode(textview_, GTK_WRAP_WORD_CHAR); ++ gtk_text_view_set_left_margin(textview_, 5); ++ gtk_text_view_set_right_margin(textview_, 5); ++ ++ g_signal_connect(textview_, "button-release-event", ++ G_CALLBACK(on_button_release), this); ++ g_signal_connect(textview_, "motion-notify-event", ++ G_CALLBACK(on_mouse_move), this); ++ ++ gtk_text_buffer_get_iter_at_offset(gtk_text_view_get_buffer(textview_), ++ &iter_, 0); ++ scroll_win_ = GTK_SCROLLED_WINDOW(gtk_scrolled_window_new(NULL, NULL)); ++ gtk_widget_show(GTK_WIDGET(scroll_win_)); ++ ++ ++ gtk_scrolled_window_set_policy(scroll_win_, ++ //altought textview's set_wrap_mode will cause ++ //this can be GTK_POLICY_NEVER,but... ++ //there are widgets that may make this broken. ++ GTK_POLICY_AUTOMATIC, ++ GTK_POLICY_AUTOMATIC); ++ gtk_container_add(GTK_CONTAINER(scroll_win_), GTK_WIDGET(textview_)); ++ gtk_scrolled_window_set_shadow_type(scroll_win_, GTK_SHADOW_IN); ++} ++ ++void LabelPangoWidget::modify_bg(GtkStateType state, const GdkColor *color) ++{ ++ gtk_widget_modify_bg(viewport_, state, color); ++} ++ ++LabelPangoWidget::LabelPangoWidget() ++{ ++ label_ = GTK_LABEL(gtk_label_new(NULL)); ++ gtk_label_set_justify(label_, GTK_JUSTIFY_LEFT); ++ scroll_win_ = GTK_SCROLLED_WINDOW(gtk_scrolled_window_new(NULL, NULL)); ++ gtk_scrolled_window_set_shadow_type(scroll_win_, GTK_SHADOW_NONE); ++ gtk_scrolled_window_set_policy(scroll_win_, GTK_POLICY_NEVER, ++ GTK_POLICY_AUTOMATIC); ++ ++ viewport_ = ++ gtk_viewport_new(gtk_scrolled_window_get_hadjustment(scroll_win_), ++ gtk_scrolled_window_get_vadjustment(scroll_win_)); ++ gtk_widget_add_events(viewport_, GDK_BUTTON1_MOTION_MASK); ++ gtk_widget_add_events(viewport_, GDK_BUTTON_RELEASE_MASK); ++ gtk_viewport_set_shadow_type(GTK_VIEWPORT(viewport_), GTK_SHADOW_NONE); ++ gtk_container_add(GTK_CONTAINER(scroll_win_), viewport_); ++ gtk_container_add(GTK_CONTAINER(viewport_), GTK_WIDGET(label_)); ++} ++ ++PangoWidgetBase *PangoWidgetBase::create(bool autoresize) ++{ ++ if (!autoresize) ++ return new TextPangoWidget; ++ else ++ return new LabelPangoWidget; ++} ++ ++void TextPangoWidget::do_set_text(const char *text) ++{ ++ GtkTextBuffer *buffer = gtk_text_view_get_buffer(textview_); ++ ++ std::list<GtkTextMark *>::const_iterator it; ++ for (it = marklist_.begin(); it != marklist_.end(); ++it) ++ gtk_text_buffer_delete_mark(buffer, *it); ++ marklist_.clear(); ++ tb_links_.clear(); ++ ++ gtk_text_buffer_set_text(buffer, text, -1); ++} ++ ++void LabelPangoWidget::do_set_text(const char *text) ++{ ++ scroll_to(0); ++ // this should speed up the next two line. ++ gtk_label_set_markup(label_, ""); ++ // so Popup()'s gtk_widget_size_request(label, &requisition); can ++ gtk_widget_set_size_request(GTK_WIDGET(label_), -1, -1); ++ // get its original width. ++ gtk_label_set_line_wrap(label_, FALSE); ++ gchar *mstr = g_markup_escape_text(text, -1); ++ gtk_label_set_text(label_, mstr); ++ g_free(mstr); ++} ++ ++void LabelPangoWidget::append_pixbuf(GdkPixbuf *pixbuf, const char *label) ++{ ++ if (label) { ++ gchar *markup = g_markup_printf_escaped("<span foreground=\"red\">[Image:%s]</span>", label); ++ append_pango_text(markup); ++ g_free(markup); ++ } else { ++ append_pango_text("<span foreground=\"red\">[Image]</span>"); ++ } ++} ++ ++void LabelPangoWidget::append_widget(GtkWidget *widget) ++{ ++ append_pango_text("<span foreground=\"red\">[Widget]</span>"); ++ if (widget) { ++ gtk_widget_destroy(widget); ++ } ++} ++ ++void PangoWidgetBase::set_text(const char *str) ++{ ++ if (update_) { ++ gchar *mark = g_markup_escape_text(str, -1); ++ cache_ = mark; ++ g_free(mark); ++ } else { ++ do_set_text(str); ++ } ++} ++ ++void TextPangoWidget::do_append_text(const char *str) ++{ ++ gtk_text_buffer_insert(gtk_text_view_get_buffer(textview_), ++ &iter_, str, strlen(str)); ++} ++ ++void LabelPangoWidget::do_append_text(const char *str) ++{ ++ set_text((std::string(gtk_label_get_text(label_)) + str).c_str()); ++} ++ ++ ++void TextPangoWidget::do_append_pango_text(const char *str) ++{ ++ ++ gtk_text_buffer_insert_markup(gtk_text_view_get_buffer(textview_), ++ &iter_, str); ++} ++ ++void TextPangoWidget::do_set_pango_text(const char *str) ++{ ++ clear(); ++ goto_begin(); ++ do_append_pango_text(str); ++} ++ ++void LabelPangoWidget::do_set_pango_text(const char *str) ++{ ++ scroll_to(0); ++ // this should speed up the next two line. ++ gtk_label_set_markup(label_, ""); ++ // so Popup()'s gtk_widget_size_request(label,&requisition); can ++ gtk_widget_set_size_request(GTK_WIDGET(label_), -1, -1); ++ // get its original width. ++ gtk_label_set_line_wrap(label_, FALSE); ++ gtk_label_set_markup(label_, str); ++} ++ ++void LabelPangoWidget::do_append_pango_text(const char *str) ++{ ++ do_set_pango_text((std::string(gtk_label_get_label(label_)) + str).c_str()); ++} ++ ++void TextPangoWidget::append_mark(const char *mark) ++{ ++ GtkTextBuffer *buffer=gtk_text_view_get_buffer(textview_); ++ if (update_) { ++ if (!cache_.empty()) { ++ do_append_pango_text(cache_.c_str()); ++ cache_.clear(); ++ } ++ } ++ marklist_.push_back( ++ gtk_text_buffer_create_mark(buffer, mark, &iter_, TRUE)); ++} ++ ++void TextPangoWidget::clear() ++{ ++ GtkTextBuffer *buffer = gtk_text_view_get_buffer(textview_); ++ ++ std::list<GtkTextMark *>::const_iterator it; ++ for (it = marklist_.begin(); it != marklist_.end(); ++it) ++ gtk_text_buffer_delete_mark(buffer, *it); ++ ++ marklist_.clear(); ++ tb_links_.clear(); ++ ++ GtkTextIter start, end; ++ gtk_text_buffer_get_bounds(buffer, &start, &end); ++ gtk_text_buffer_delete(buffer, &start, &end); ++ scroll_to(0); ++ cache_.clear(); ++} ++ ++void LabelPangoWidget::clear() ++{ ++ do_set_text(""); ++ cache_.clear(); ++} ++ ++void TextPangoWidget::goto_begin() ++{ ++ gtk_text_buffer_get_iter_at_offset( ++ gtk_text_view_get_buffer(textview_), &iter_, 0 ++ ); ++} ++ ++void TextPangoWidget::goto_end() ++{ ++ gtk_text_buffer_get_iter_at_offset(gtk_text_view_get_buffer(textview_), &iter_, -1); ++} ++ ++std::string TextPangoWidget::get_text() ++{ ++ std::string res; ++ ++ GtkTextIter start, end; ++ GtkTextBuffer *buffer=gtk_text_view_get_buffer(textview_); ++ gtk_text_buffer_get_bounds(buffer, &start, &end); ++ gchar *text = gtk_text_buffer_get_text(buffer, &start, &end, FALSE); ++ res = text; ++ g_free(text); ++ ++ return res; ++} ++ ++std::string LabelPangoWidget::get_text() ++{ ++ return gtk_label_get_text(label_); ++} ++ ++ ++void TextPangoWidget::append_pango_text_with_links(const std::string& str, ++ const LinksPosList& links) ++{ ++ if (links.empty()) { ++ append_pango_text(str.c_str()); ++ return; ++ } ++ ++ do_append_pango_text(cache_.c_str()); ++ cache_.clear(); ++ ++ gint beg = gtk_text_iter_get_offset(&iter_); ++ ++ for (LinksPosList::const_iterator it = links.begin(); ++ it != links.end(); ++it) { ++ tb_links_.push_back(TextBufPos(beg + it->pos_, beg + it->pos_ + it->len_, it->link_)); ++ } ++ ++ gtk_text_buffer_insert_markup(gtk_text_view_get_buffer(textview_), ++ &iter_, str.c_str()); ++} ++ ++void TextPangoWidget::append_pixbuf(GdkPixbuf *pixbuf, const char *label) ++{ ++ do_append_pango_text(cache_.c_str()); ++ cache_.clear(); ++ gtk_text_buffer_insert_pixbuf (gtk_text_view_get_buffer(textview_), &iter_, pixbuf); ++} ++ ++void TextPangoWidget::append_widget(GtkWidget *widget) ++{ ++ do_append_pango_text(cache_.c_str()); ++ cache_.clear(); ++ GtkTextChildAnchor *anchor = gtk_text_buffer_create_child_anchor (gtk_text_view_get_buffer(textview_), &iter_); ++ gtk_text_view_add_child_at_anchor (textview_, widget, anchor); ++} ++ ++TextPangoWidget::TextBufLinks::const_iterator TextPangoWidget::find_link(gint x, ++ gint y) ++{ ++ GtkTextIter iter; ++ gtk_text_view_get_iter_at_location(textview_, &iter, x, y); ++ gint pos = gtk_text_iter_get_offset(&iter); ++ TextBufLinks::const_iterator it; ++ for (it = tb_links_.begin(); it != tb_links_.end(); ++it) { ++ if (pos < it->beg_) ++ return tb_links_.end(); ++ if (it->beg_ <= pos && pos < it->end_) ++ break; ++ } ++ return it; ++} ++ ++gboolean TextPangoWidget::on_mouse_move(GtkWidget *widget, GdkEventMotion *event, ++ gpointer userdata) ++{ ++ TextPangoWidget *tpw = static_cast<TextPangoWidget *>(userdata); ++ GtkTextWindowType win_type = ++ gtk_text_view_get_window_type(tpw->textview_, event->window); ++ gint x, y; ++ gtk_text_view_window_to_buffer_coords(tpw->textview_, win_type, ++ gint(event->x), gint(event->y), ++ &x, &y); ++ ++ TextBufLinks::const_iterator it = tpw->find_link(x, y); ++ if (it != tpw->tb_links_.end()) { ++ gdk_window_set_cursor( ++ gtk_text_view_get_window(tpw->textview_, ++ GTK_TEXT_WINDOW_TEXT), ++ get_impl(tpw->hand_cursor_)); ++ } else { ++ gdk_window_set_cursor( ++ gtk_text_view_get_window(tpw->textview_, ++ GTK_TEXT_WINDOW_TEXT), ++ get_impl(tpw->regular_cursor_)); ++ } ++ ++ gdk_window_get_pointer(widget->window, NULL, NULL, NULL); ++ ++ return FALSE; ++} ++ ++ ++ ++gboolean TextPangoWidget::on_button_release(GtkWidget *, GdkEventButton *event, ++ gpointer userdata) ++{ ++ if (event->button != 1) ++ return FALSE; ++ TextPangoWidget *tpw = static_cast<TextPangoWidget *>(userdata); ++ GtkTextBuffer *buf = gtk_text_view_get_buffer(tpw->textview_); ++ /* we shouldn't follow a link if the user has selected something */ ++ GtkTextIter beg, end; ++ gtk_text_buffer_get_selection_bounds (buf, &beg, &end); ++ if (gtk_text_iter_get_offset (&beg) != gtk_text_iter_get_offset (&end)) ++ return FALSE; ++ GtkTextWindowType win_type = ++ gtk_text_view_get_window_type(tpw->textview_, event->window); ++ gint x, y; ++ gtk_text_view_window_to_buffer_coords(tpw->textview_, win_type, ++ gint(event->x), gint(event->y), ++ &x, &y); ++ TextBufLinks::const_iterator it = tpw->find_link(x, y); ++ if (it != tpw->tb_links_.end()) { ++ tpw->on_link_click_.emit(it->link_); ++ } ++ return FALSE; ++} +diff -Nur stardict-3.0.1.orig//src/prefsdlg.cpp stardict-3.0.1/src/prefsdlg.cpp +--- stardict-3.0.1.orig//src/prefsdlg.cpp 2007-10-30 03:14:07.000000000 -0500 ++++ stardict-3.0.1/src/prefsdlg.cpp 2010-05-24 00:53:36.380667202 -0500 +@@ -21,6 +21,8 @@ + # include "config.h" + #endif + ++#include <cstdlib> ++ + #include <glib/gi18n.h> + #include <glib/gstdio.h> + +diff -Nur stardict-3.0.1.orig//src/prefsdlg.cpp~ stardict-3.0.1/src/prefsdlg.cpp~ +--- stardict-3.0.1.orig//src/prefsdlg.cpp~ 1969-12-31 18:00:00.000000000 -0600 ++++ stardict-3.0.1/src/prefsdlg.cpp~ 2007-10-30 03:14:07.000000000 -0500 +@@ -0,0 +1,1908 @@ ++/* ++ * This file part of StarDict - A international dictionary for GNOME. ++ * http://stardict.sourceforge.net ++ * ++ * 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 3 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 Library 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. ++ */ ++ ++#ifdef HAVE_CONFIG_H ++# include "config.h" ++#endif ++ ++#include <glib/gi18n.h> ++#include <glib/gstdio.h> ++ ++#ifdef _WIN32 ++# include <gdk/gdkwin32.h> ++#endif ++ ++#include "stardict.h" ++#include "conf.h" ++#include "desktop.hpp" ++#include "utils.h" ++#include "iskeyspressed.hpp" ++#include "lib/md5.h" ++ ++#include "prefsdlg.h" ++ ++#ifndef CONFIG_GPE ++enum { ++ LOGO = 0, ++ DICTIONARY_SCAN_SETTINGS, ++ DICTIONARY_FONT_SETTINGS, ++ DICTIONARY_CACHE_SETTINGS, ++ DICTIONARY_EXPORT_SETTINGS, ++ DICTIONARY_SOUND_SETTINGS, ++ DICIONARY_ARTICLE_RENDERING, ++ NETWORK_NETDICT, ++ MAINWIN_INPUT_SETTINGS, ++ MAINWIN_OPTIONS_SETTINGS, ++ MAINWIN_SEARCH_WEBSITE_SETTINGS, ++ NOTIFICATION_AREA_ICON_OPITIONS_SETTINGS, ++ FLOATWIN_OPTIONS_SETTINGS, ++ FLOATWIN_SIZE_SETTINGS, ++}; ++ ++enum ++{ ++ CATEGORY_COLUMN = 0, ++ PAGE_NUM_COLUMN, ++ NUM_COLUMNS ++}; ++ ++ ++struct CategoriesTreeItem { ++ gchar *category; ++ ++ CategoriesTreeItem *children; ++ ++ gint notebook_page; ++}; ++ ++static CategoriesTreeItem dictionary_behavior [] = { ++ {N_("Scan Selection"), NULL, DICTIONARY_SCAN_SETTINGS}, ++ {N_("Font"), NULL, DICTIONARY_FONT_SETTINGS}, ++ {N_("Cache"), NULL, DICTIONARY_CACHE_SETTINGS}, ++ {N_("Export"), NULL, DICTIONARY_EXPORT_SETTINGS}, ++ {N_("Sound"), NULL, DICTIONARY_SOUND_SETTINGS}, ++ {N_("Article rendering"), NULL, DICIONARY_ARTICLE_RENDERING }, ++ { NULL } ++}; ++ ++static CategoriesTreeItem network_behavior [] = { ++ {N_("Net Dict"), NULL, NETWORK_NETDICT}, ++ { NULL } ++}; ++ ++static CategoriesTreeItem mainwin_behavior [] = ++{ ++ {N_("Input"), NULL, MAINWIN_INPUT_SETTINGS}, ++ {N_("Options"), NULL, MAINWIN_OPTIONS_SETTINGS}, ++ {N_("Search website"), NULL, MAINWIN_SEARCH_WEBSITE_SETTINGS}, ++ ++ { NULL } ++}; ++ ++static CategoriesTreeItem NotificationAreaIcon_behavior [] = ++{ ++ {N_("Options"), NULL, NOTIFICATION_AREA_ICON_OPITIONS_SETTINGS}, ++ ++ { NULL } ++}; ++ ++static CategoriesTreeItem floatwin_behavior [] = ++{ ++ {N_("Options"), NULL, FLOATWIN_OPTIONS_SETTINGS}, ++ {N_("Settings"), NULL, FLOATWIN_SIZE_SETTINGS}, ++ ++ { NULL } ++}; ++ ++static CategoriesTreeItem toplevel [] = ++{ ++ {N_("Dictionary"), dictionary_behavior, LOGO}, ++ ++ {N_("Network"), network_behavior, LOGO}, ++ ++ {N_("Main window"), mainwin_behavior, LOGO}, ++ ++ {N_("Notification area icon"), NotificationAreaIcon_behavior, LOGO}, ++ ++ {N_("Floating window"), floatwin_behavior, LOGO}, ++ ++ { NULL } ++}; ++ ++static gint last_selected_page_num = DICTIONARY_SCAN_SETTINGS; ++#endif ++ ++void PrefsDlg::response_handler (GtkDialog *dialog, gint res_id, PrefsDlg *oPrefsDlg) ++{ ++ if (res_id==GTK_RESPONSE_HELP) ++ show_help("stardict-prefs"); ++} ++ ++#ifndef CONFIG_GPE ++GtkTreeModel* PrefsDlg::create_categories_tree_model () ++{ ++ GtkTreeStore *model; ++ GtkTreeIter iter; ++ CategoriesTreeItem *category = toplevel; ++ ++ model = gtk_tree_store_new (NUM_COLUMNS, G_TYPE_STRING, G_TYPE_INT); ++ ++ while (category->category) { ++ CategoriesTreeItem *sub_category = category->children; ++ gtk_tree_store_append (model, &iter, NULL); ++ gtk_tree_store_set (model, &iter, CATEGORY_COLUMN, gettext (category->category), PAGE_NUM_COLUMN, category->notebook_page, -1); ++ ++ while (sub_category->category) { ++ GtkTreeIter child_iter; ++ gtk_tree_store_append (model, &child_iter, &iter); ++ gtk_tree_store_set (model, &child_iter, ++ CATEGORY_COLUMN, gettext (sub_category->category), ++ PAGE_NUM_COLUMN, sub_category->notebook_page, ++ -1); ++ sub_category++; ++ } ++ category++; ++ } ++ return GTK_TREE_MODEL (model); ++} ++ ++void PrefsDlg::categories_tree_selection_cb (GtkTreeSelection *selection, PrefsDlg *oPrefsDlg) ++{ ++ GtkTreeIter iter; ++ GValue value = {0, }; ++ ++ if (! gtk_tree_selection_get_selected (selection, NULL, &iter)) ++ return; ++ ++ gtk_tree_model_get_value (oPrefsDlg->categories_tree_model, &iter, ++ PAGE_NUM_COLUMN, ++ &value); ++ ++ last_selected_page_num = g_value_get_int (&value); ++ ++ if (oPrefsDlg->notebook != NULL) ++ gtk_notebook_set_current_page (GTK_NOTEBOOK (oPrefsDlg->notebook), ++ last_selected_page_num); ++ g_value_unset (&value); ++} ++ ++gboolean PrefsDlg::selection_init (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, PrefsDlg *oPrefsDlg) ++{ ++ GValue value = {0, }; ++ gint page_num; ++ ++ gtk_tree_model_get_value (oPrefsDlg->categories_tree_model, iter, ++ PAGE_NUM_COLUMN, ++ &value); ++ ++ page_num = g_value_get_int (&value); ++ ++ g_value_unset (&value); ++ ++ if (page_num == last_selected_page_num) ++ { ++ GtkTreeSelection *selection; ++ ++ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (oPrefsDlg->categories_tree)); ++ ++ gtk_tree_selection_select_iter (selection, iter); ++ ++ gtk_notebook_set_current_page (GTK_NOTEBOOK (oPrefsDlg->notebook), page_num); ++ ++ return TRUE; ++ } ++ return FALSE; ++} ++ ++void PrefsDlg::categories_tree_realize (GtkWidget *widget, PrefsDlg *oPrefsDlg) ++{ ++ gtk_tree_view_expand_all(GTK_TREE_VIEW(widget)); ++ ++ gtk_tree_model_foreach(oPrefsDlg->categories_tree_model, ++ GtkTreeModelForeachFunc(selection_init), ++ oPrefsDlg); ++} ++ ++void PrefsDlg::create_categories_tree(void) ++{ ++ GtkWidget *sw; ++ GtkTreeModel *model; ++ GtkWidget *treeview; ++ GtkCellRenderer *renderer; ++ GtkTreeSelection *selection; ++ GtkTreeViewColumn *column; ++ gint col_offset; ++ ++ 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_AUTOMATIC, ++ GTK_POLICY_AUTOMATIC); ++ ++ gtk_widget_set_size_request (sw, 140, 240); ++ ++ model = create_categories_tree_model (); ++ ++ treeview = gtk_tree_view_new_with_model (model); ++ g_object_unref (G_OBJECT (model)); ++ gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (treeview), TRUE); ++ ++ categories_tree = treeview; ++ categories_tree_model = model; ++ ++ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview)); ++ ++ gtk_tree_selection_set_mode (selection, ++ GTK_SELECTION_SINGLE); ++ ++ /* add column for category */ ++ renderer = gtk_cell_renderer_text_new (); ++ g_object_set (G_OBJECT (renderer), "xalign", 0.0, NULL); ++ ++ col_offset = gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview), ++ -1, _("Categories"), ++ renderer, "text", ++ CATEGORY_COLUMN, ++ NULL); ++ ++ column = gtk_tree_view_get_column (GTK_TREE_VIEW (treeview), col_offset - 1); ++ gtk_tree_view_column_set_clickable (GTK_TREE_VIEW_COLUMN (column), FALSE); ++ ++ g_signal_connect (selection, "changed", ++ G_CALLBACK (categories_tree_selection_cb), ++ this); ++ ++ gtk_container_add (GTK_CONTAINER (sw), treeview); ++ ++ g_signal_connect (G_OBJECT (treeview), "realize", ++ G_CALLBACK (categories_tree_realize), ++ this); ++ ++ gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE); ++ ++ categories_window=sw; ++} ++ ++void PrefsDlg::setup_logo_page() ++{ ++ GtkWidget *image = gtk_image_new_from_pixbuf(stardict_logo); ++ gtk_notebook_append_page(GTK_NOTEBOOK(notebook),image,NULL); ++} ++#endif ++ ++static GtkWidget *prepare_page(GtkNotebook *notebook, const gchar *caption, ++ const gchar *stock_id) ++{ ++ GtkWidget *vbox = gtk_vbox_new(FALSE, 12); ++#ifdef CONFIG_GPE ++ gtk_container_set_border_width(GTK_CONTAINER (vbox), 5); ++ GtkWidget *nb_label = gtk_label_new(caption); ++ gtk_notebook_append_page(notebook, vbox, nb_label); ++#else ++ gtk_notebook_append_page(notebook, vbox, NULL); ++#endif ++ ++ GtkWidget *vbox1 = gtk_vbox_new(FALSE, 6); ++ gtk_box_pack_start(GTK_BOX(vbox), vbox1, FALSE, FALSE, 6); ++ GtkWidget *hbox = gtk_hbox_new(FALSE, 6); ++ gtk_box_pack_start(GTK_BOX(vbox1), hbox, FALSE, FALSE, 0); ++ GtkWidget *image = ++ gtk_image_new_from_stock(stock_id, ++ GTK_ICON_SIZE_LARGE_TOOLBAR); ++ gtk_box_pack_start(GTK_BOX(hbox), image, FALSE, FALSE, 0); ++ GtkWidget *label = gtk_label_new(NULL); ++ glib::CharStr label_caption( ++ g_markup_printf_escaped("<span weight=\"bold\" size=\"x-large\">%s</span>", caption)); ++ gtk_label_set_markup(GTK_LABEL(label), get_impl(label_caption)); ++ gtk_box_pack_start(GTK_BOX(hbox),label, FALSE, FALSE, 0); ++ GtkWidget *hseparator = gtk_hseparator_new(); ++ gtk_box_pack_start(GTK_BOX(vbox1),hseparator,FALSE,FALSE,0); ++ ++ return vbox; ++} ++ ++void PrefsDlg::on_setup_dictionary_scan_ckbutton_toggled(GtkToggleButton *button, PrefsDlg *oPrefsDlg) ++{ ++ gboolean b = gtk_toggle_button_get_active(button); ++ gtk_widget_set_sensitive(oPrefsDlg->scan_modifier_key_vbox,b); ++ conf->set_bool_at("dictionary/only_scan_while_modifier_key", b); ++} ++ ++#ifdef _WIN32 ++void PrefsDlg::on_setup_dictionary_scan_clipboard_ckbutton_toggled(GtkToggleButton *button, PrefsDlg *oPrefsDlg) ++{ ++ gboolean b = gtk_toggle_button_get_active(button); ++ if (b) { ++ if (conf->get_bool_at("dictionary/scan_selection")) ++ gpAppFrame->oClipboard.start(); ++ } else { ++ if (conf->get_bool_at("dictionary/scan_selection")) ++ gpAppFrame->oClipboard.stop(); ++ } ++ conf->set_bool_at("dictionary/scan_clipboard", b); ++} ++ ++void PrefsDlg::on_setup_dictionary_use_scan_hotkey_ckbutton_toggled(GtkToggleButton *button, PrefsDlg *oPrefsDlg) ++{ ++ gboolean b = gtk_toggle_button_get_active(button); ++ if (b) ++ gpAppFrame->oHotkey.start_scan(); ++ else ++ gpAppFrame->oHotkey.stop_scan(); ++ conf->set_bool_at("dictionary/use_scan_hotkey", b); ++} ++#endif ++ ++void PrefsDlg::on_setup_dictionary_scan_combobox_changed(GtkComboBox *combobox, PrefsDlg *oPrefsDlg) ++{ ++ gint key = gtk_combo_box_get_active(combobox); ++ conf->set_int_at("dictionary/scan_modifier_key", key); ++} ++ ++void PrefsDlg::on_setup_dictionary_scan_hide_ckbutton_toggled(GtkToggleButton *button, PrefsDlg *oPrefsDlg) ++{ ++ gboolean hide = gtk_toggle_button_get_active(button); ++ conf->set_bool_at("dictionary/hide_floatwin_when_modifier_key_released", hide); ++} ++ ++void PrefsDlg::setup_dictionary_scan_page() ++{ ++ GtkWidget *vbox = prepare_page(GTK_NOTEBOOK(notebook), _("Scan Selection"), GTK_STOCK_CONVERT); ++ GtkWidget *vbox1 = gtk_vbox_new(false, 0); ++ gtk_box_pack_start(GTK_BOX(vbox),vbox1,false,false, 0); ++ GtkWidget *check_button = gtk_check_button_new_with_mnemonic(_("_Only scan while the modifier key is being pressed.")); ++ gtk_box_pack_start(GTK_BOX(vbox1),check_button,false,false,0); ++ bool only_scan_while_modifier_key= ++ conf->get_bool_at("dictionary/only_scan_while_modifier_key"); ++ ++ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_button), ++ only_scan_while_modifier_key); ++ g_signal_connect(G_OBJECT(check_button), "toggled", ++ G_CALLBACK(on_setup_dictionary_scan_ckbutton_toggled), this); ++ ++ scan_modifier_key_vbox = gtk_vbox_new(FALSE, 6); ++ gtk_box_pack_start(GTK_BOX(vbox1), scan_modifier_key_vbox, ++ FALSE, FALSE, 12); ++ gtk_widget_set_sensitive(scan_modifier_key_vbox, ++ only_scan_while_modifier_key); ++ ++ check_button = gtk_check_button_new_with_mnemonic(_("H_ide floating window when modifier key released.")); ++ gtk_box_pack_start(GTK_BOX(scan_modifier_key_vbox),check_button,false,false,0); ++ ++ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_button), ++ conf->get_bool_at("dictionary/hide_floatwin_when_modifier_key_released")); ++ g_signal_connect (G_OBJECT (check_button), "toggled", G_CALLBACK (on_setup_dictionary_scan_hide_ckbutton_toggled), this); ++ ++ GtkWidget *hbox = gtk_hbox_new(false, 12); ++ gtk_box_pack_start(GTK_BOX(scan_modifier_key_vbox), hbox,false,false,0); ++ GtkWidget *label=gtk_label_new(NULL); ++ gtk_label_set_markup_with_mnemonic(GTK_LABEL(label), _("Scan modifier _key:")); ++ gtk_box_pack_start(GTK_BOX(hbox),label,false,false,0); ++ gtk_misc_set_alignment (GTK_MISC (label), 0, .5); ++ GtkWidget *combobox = gtk_combo_box_new_text(); ++ gtk_combo_box_set_focus_on_click(GTK_COMBO_BOX(combobox), FALSE); ++ ++ for (std::list<std::string>::const_iterator p=key_combs.begin(); ++ p!=key_combs.end(); ++p) { ++ gtk_combo_box_append_text(GTK_COMBO_BOX(combobox), p->c_str()); ++ } ++ ++ int scan_modifier_key= ++ conf->get_int_at("dictionary/scan_modifier_key"); ++ ++ gtk_combo_box_set_active(GTK_COMBO_BOX(combobox), scan_modifier_key); ++ ++ gtk_label_set_mnemonic_widget(GTK_LABEL(label), combobox); ++ gtk_box_pack_start(GTK_BOX(hbox), combobox, FALSE, FALSE, 0); ++ g_signal_connect (G_OBJECT (combobox), "changed", G_CALLBACK (on_setup_dictionary_scan_combobox_changed), this); ++ ++#ifdef _WIN32 ++ check_button = gtk_check_button_new_with_mnemonic(_("_Scan clipboard.")); ++ gtk_box_pack_start(GTK_BOX(vbox1),check_button,false,false,0); ++ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_button), conf->get_bool_at("dictionary/scan_clipboard")); ++ g_signal_connect(G_OBJECT(check_button), "toggled", ++ G_CALLBACK(on_setup_dictionary_scan_clipboard_ckbutton_toggled), this); ++ ++ check_button = gtk_check_button_new_with_mnemonic(_("_Use scan hotkey: Ctrl+Alt+F1.")); ++ gtk_box_pack_start(GTK_BOX(vbox1),check_button,false,false,0); ++ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_button), conf->get_bool_at("dictionary/use_scan_hotkey")); ++ g_signal_connect(G_OBJECT(check_button), "toggled", ++ G_CALLBACK(on_setup_dictionary_use_scan_hotkey_ckbutton_toggled), this); ++#endif ++} ++ ++void PrefsDlg::change_font_for_all_widgets(const std::string& fontname) ++{ ++ gchar *aa = ++ g_strdup_printf("style \"custom-font\" { font_name= \"%s\" }\n" ++ "class \"GtkWidget\" style \"custom-font\"\n", fontname.c_str()); ++ gtk_rc_parse_string(aa); ++ g_free(aa); ++ GdkScreen *screen = gtk_window_get_screen(parent_window); ++ GtkSettings *settings=gtk_settings_get_for_screen(screen); ++ gtk_rc_reset_styles(settings); ++#ifndef CONFIG_GPE ++ resize_categories_tree(); ++#endif ++} ++ ++void PrefsDlg::on_setup_dictionary_font_ckbutton_toggled(GtkToggleButton *button, PrefsDlg *oPrefsDlg) ++{ ++ gboolean b = gtk_toggle_button_get_active(button); ++ gtk_widget_set_sensitive(oPrefsDlg->custom_font_hbox, b); ++ conf->set_bool_at("dictionary/use_custom_font", b); ++ if (b) { ++ const std::string &custom_font= ++ conf->get_string_at("dictionary/custom_font"); ++ oPrefsDlg->change_font_for_all_widgets(custom_font); ++ } else ++ oPrefsDlg->change_font_for_all_widgets(""); ++} ++ ++void PrefsDlg::on_setup_dictionary_font_button_clicked(GtkWidget *widget, PrefsDlg *oPrefsDlg) ++{ ++ GtkWidget *dlg = gtk_font_selection_dialog_new(_("Choose dictionary font")); ++ gtk_window_set_transient_for (GTK_WINDOW (dlg), GTK_WINDOW (oPrefsDlg->window)); ++ const gchar *text = gtk_button_get_label(GTK_BUTTON(widget)); ++ if (strcmp(text,_("Choose"))) ++ gtk_font_selection_dialog_set_font_name(GTK_FONT_SELECTION_DIALOG(dlg), text); ++ gtk_font_selection_dialog_set_preview_text(GTK_FONT_SELECTION_DIALOG(dlg),_("Dictionary font")); ++ gint result = gtk_dialog_run (GTK_DIALOG (dlg)); ++ if (result==GTK_RESPONSE_OK) { ++ gchar *font_name = ++ gtk_font_selection_dialog_get_font_name(GTK_FONT_SELECTION_DIALOG(dlg)); ++ if (font_name) { ++ gtk_button_set_label(GTK_BUTTON(widget),font_name); ++ conf->set_string_at("dictionary/custom_font", std::string(font_name)); ++ } ++ if (font_name && font_name[0]) { ++ oPrefsDlg->change_font_for_all_widgets(font_name); ++ } ++ } ++ gtk_widget_destroy (dlg); ++} ++ ++void PrefsDlg::setup_dictionary_font_page() ++{ ++ GtkWidget *vbox = prepare_page(GTK_NOTEBOOK(notebook), _("Font"), GTK_STOCK_SELECT_FONT); ++ GtkWidget *vbox1 = gtk_vbox_new(false,6); ++ gtk_box_pack_start(GTK_BOX(vbox),vbox1,false,false, 0); ++ GtkWidget *check_button = gtk_check_button_new_with_mnemonic(_("_Use custom font.")); ++ gtk_box_pack_start(GTK_BOX(vbox1),check_button,false,false,0); ++ bool use_custom_font= ++ conf->get_bool_at("dictionary/use_custom_font"); ++ ++ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_button), ++ use_custom_font); ++ g_signal_connect (G_OBJECT (check_button), "toggled", G_CALLBACK (on_setup_dictionary_font_ckbutton_toggled), this); ++ custom_font_hbox = gtk_hbox_new(false, 12); ++ gtk_box_pack_start(GTK_BOX(vbox1),custom_font_hbox,false,false,0); ++ gtk_widget_set_sensitive(custom_font_hbox, use_custom_font); ++ GtkWidget *label=gtk_label_new(NULL); ++ gtk_label_set_markup_with_mnemonic(GTK_LABEL(label), _("Dictionary _font:")); ++ gtk_box_pack_start(GTK_BOX(custom_font_hbox),label,false,false,0); ++ gtk_misc_set_alignment (GTK_MISC (label), 0, .5); ++ GtkWidget *button; ++ const std::string &custom_font= ++ conf->get_string_at("dictionary/custom_font"); ++ ++ if (!custom_font.empty()) ++ button = gtk_button_new_with_label(custom_font.c_str()); ++ else ++ button=gtk_button_new_with_label(_("Choose")); ++ ++ gtk_label_set_mnemonic_widget(GTK_LABEL(label), button); ++ gtk_box_pack_start(GTK_BOX(custom_font_hbox),button,false,false,0); ++ g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (on_setup_dictionary_font_button_clicked), this); ++} ++ ++void PrefsDlg::on_setup_dictionary_cache_CreateCacheFile_ckbutton_toggled(GtkToggleButton *button, PrefsDlg *oPrefsDlg) ++{ ++ gboolean enable = gtk_toggle_button_get_active(button); ++ conf->set_bool_at("dictionary/create_cache_file",enable); ++} ++ ++void PrefsDlg::on_setup_dictionary_cache_EnableCollation_ckbutton_toggled(GtkToggleButton *button, PrefsDlg *oPrefsDlg) ++{ ++ gboolean enable = gtk_toggle_button_get_active(button); ++ gtk_widget_set_sensitive(oPrefsDlg->collation_hbox, enable); ++ conf->set_bool_at("dictionary/enable_collation",enable); ++} ++ ++void PrefsDlg::on_setup_dictionary_collation_combobox_changed(GtkComboBox *combobox, PrefsDlg *oPrefsDlg) ++{ ++ gint key = gtk_combo_box_get_active(combobox); ++ conf->set_int_at("dictionary/collate_function", key); ++} ++ ++static void clean_dir(const gchar *dirname) ++{ ++ GDir *dir = g_dir_open(dirname, 0, NULL); ++ if (dir) { ++ const gchar *filename; ++ gchar fullfilename[256]; ++ while ((filename = g_dir_read_name(dir))!=NULL) { ++ sprintf(fullfilename, "%s" G_DIR_SEPARATOR_S "%s", dirname, filename); ++ if (g_file_test(fullfilename, G_FILE_TEST_IS_DIR)) { ++ clean_dir(fullfilename); ++ } else if (g_str_has_suffix(filename,".oft") || g_str_has_suffix(filename,".clt")) { ++ g_unlink(fullfilename); ++ } ++ } ++ g_dir_close(dir); ++ } ++} ++ ++void PrefsDlg::on_setup_dictionary_cache_cleanbutton_clicked(GtkWidget *widget, PrefsDlg *oPrefsDlg) ++{ ++ std::string dirname = gStarDictDataDir+ G_DIR_SEPARATOR_S "dic"; ++ clean_dir(dirname.c_str()); ++ dirname = g_get_user_cache_dir(); ++ dirname += G_DIR_SEPARATOR_S "stardict"; ++ clean_dir(dirname.c_str()); ++ g_rmdir(dirname.c_str()); ++} ++ ++void PrefsDlg::setup_dictionary_cache_page() ++{ ++ GtkWidget *vbox = prepare_page(GTK_NOTEBOOK(notebook), _("Cache"), GTK_STOCK_HARDDISK); ++ GtkWidget *vbox1 = gtk_vbox_new(false, 6); ++ gtk_box_pack_start(GTK_BOX(vbox),vbox1,false,false, 0); ++ GtkWidget *check_button; ++ check_button = gtk_check_button_new_with_mnemonic(_("Create c_ache files to speed up loading.")); ++ bool enable = conf->get_bool_at("dictionary/create_cache_file"); ++ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_button), enable); ++ g_signal_connect (G_OBJECT (check_button), "toggled", G_CALLBACK (on_setup_dictionary_cache_CreateCacheFile_ckbutton_toggled), (gpointer)this); ++ gtk_box_pack_start(GTK_BOX(vbox1),check_button,false,false,0); ++ check_button = gtk_check_button_new_with_mnemonic(_("_Sort word list by collation function.")); ++ enable = conf->get_bool_at("dictionary/enable_collation"); ++ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_button), enable); ++ g_signal_connect (G_OBJECT (check_button), "toggled", G_CALLBACK (on_setup_dictionary_cache_EnableCollation_ckbutton_toggled), (gpointer)this); ++ gtk_box_pack_start(GTK_BOX(vbox1),check_button,false,false,0); ++ collation_hbox = gtk_hbox_new(false,6); ++ gtk_box_pack_start(GTK_BOX(vbox1),collation_hbox,false,false,0); ++ GtkWidget *label=gtk_label_new(NULL); ++ gtk_misc_set_alignment (GTK_MISC (label), 0, .5); ++ gtk_label_set_markup_with_mnemonic(GTK_LABEL(label), _("\tCollation _function:")); ++ gtk_box_pack_start(GTK_BOX(collation_hbox),label,false,false,0); ++ GtkWidget *combobox = gtk_combo_box_new_text(); ++ gtk_combo_box_set_focus_on_click(GTK_COMBO_BOX(combobox), FALSE); ++ gtk_combo_box_append_text(GTK_COMBO_BOX(combobox), "utf8_general_ci"); ++ gtk_combo_box_append_text(GTK_COMBO_BOX(combobox), "utf8_unicode_ci"); ++ gtk_combo_box_append_text(GTK_COMBO_BOX(combobox), "utf8_bin"); ++ gtk_combo_box_append_text(GTK_COMBO_BOX(combobox), "utf8_czech_ci"); ++ gtk_combo_box_append_text(GTK_COMBO_BOX(combobox), "utf8_danish_ci"); ++ gtk_combo_box_append_text(GTK_COMBO_BOX(combobox), "utf8_esperanto_ci"); ++ gtk_combo_box_append_text(GTK_COMBO_BOX(combobox), "utf8_estonian_ci"); ++ gtk_combo_box_append_text(GTK_COMBO_BOX(combobox), "utf8_hungarian_ci"); ++ gtk_combo_box_append_text(GTK_COMBO_BOX(combobox), "utf8_icelandic_ci"); ++ gtk_combo_box_append_text(GTK_COMBO_BOX(combobox), "utf8_latvian_ci"); ++ gtk_combo_box_append_text(GTK_COMBO_BOX(combobox), "utf8_lithuanian_ci"); ++ gtk_combo_box_append_text(GTK_COMBO_BOX(combobox), "utf8_persian_ci"); ++ gtk_combo_box_append_text(GTK_COMBO_BOX(combobox), "utf8_polish_ci"); ++ gtk_combo_box_append_text(GTK_COMBO_BOX(combobox), "utf8_roman_ci"); ++ gtk_combo_box_append_text(GTK_COMBO_BOX(combobox), "utf8_romanian_ci"); ++ gtk_combo_box_append_text(GTK_COMBO_BOX(combobox), "utf8_slovak_ci"); ++ gtk_combo_box_append_text(GTK_COMBO_BOX(combobox), "utf8_slovenian_ci"); ++ gtk_combo_box_append_text(GTK_COMBO_BOX(combobox), "utf8_spanish_ci"); ++ gtk_combo_box_append_text(GTK_COMBO_BOX(combobox), "utf8_spanish2_ci"); ++ gtk_combo_box_append_text(GTK_COMBO_BOX(combobox), "utf8_swedish_ci"); ++ gtk_combo_box_append_text(GTK_COMBO_BOX(combobox), "utf8_turkish_ci"); ++ int collate_function = conf->get_int_at("dictionary/collate_function"); ++ gtk_combo_box_set_active(GTK_COMBO_BOX(combobox), collate_function); ++ gtk_label_set_mnemonic_widget(GTK_LABEL(label), combobox); ++ gtk_box_pack_start(GTK_BOX(collation_hbox), combobox, FALSE, FALSE, 0); ++ g_signal_connect (G_OBJECT (combobox), "changed", G_CALLBACK (on_setup_dictionary_collation_combobox_changed), this); ++ gtk_widget_set_sensitive(collation_hbox, enable); ++ ++ label = gtk_label_new(_("After enabled collation, when load the dictionaries for the first time, it will take some time for sorting, please wait for a moment.")); ++ gtk_label_set_line_wrap(GTK_LABEL(label), TRUE); ++ gtk_box_pack_start(GTK_BOX(vbox1),label,false,false,0); ++ ++ GtkWidget *hbox = gtk_hbox_new(false,6); ++ gtk_box_pack_start(GTK_BOX(vbox1),hbox,false,false,0); ++ GtkWidget *button = gtk_button_new_with_mnemonic(_("C_lean all cache files")); ++ gtk_button_set_image(GTK_BUTTON(button), gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_BUTTON)); ++ gtk_box_pack_end(GTK_BOX(hbox),button,false,false,0); ++ g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (on_setup_dictionary_cache_cleanbutton_clicked), this); ++} ++ ++ ++ ++void PrefsDlg::on_setup_dictionary_export_ckbutton_toggled(GtkToggleButton *button, PrefsDlg *oPrefsDlg) ++{ ++ gboolean enable = gtk_toggle_button_get_active(button); ++ conf->set_bool_at("dictionary/only_export_word", enable); ++} ++ ++void PrefsDlg::on_setup_dictionary_export_browse_button_clicked(GtkButton *button, PrefsDlg *oPrefsDlg) ++{ ++ GtkWidget *dialog; ++ dialog = gtk_file_chooser_dialog_new (_("Open file..."), ++ GTK_WINDOW(oPrefsDlg->window), ++ GTK_FILE_CHOOSER_ACTION_OPEN, ++ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, ++ GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, ++ NULL); ++ gtk_file_chooser_set_filename(GTK_FILE_CHOOSER (dialog), gtk_entry_get_text(oPrefsDlg->eExportFile)); ++ if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) { ++ gchar *filename; ++ filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog)); ++ gtk_entry_set_text(oPrefsDlg->eExportFile, filename); ++ g_free (filename); ++ } ++ gtk_widget_destroy (dialog); ++} ++ ++void PrefsDlg::setup_dictionary_export_page() ++{ ++ GtkWidget *vbox = prepare_page(GTK_NOTEBOOK(notebook), _("Export"), GTK_STOCK_SAVE); ++ GtkWidget *vbox1 = gtk_vbox_new(false, 6); ++ gtk_box_pack_start(GTK_BOX(vbox),vbox1,false,false, 0); ++ ++ GtkWidget *check_button; ++ check_button = gtk_check_button_new_with_mnemonic(_("_Only export words.")); ++ bool enable= conf->get_bool_at("dictionary/only_export_word"); ++ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_button), enable); ++ g_signal_connect (G_OBJECT (check_button), "toggled", G_CALLBACK (on_setup_dictionary_export_ckbutton_toggled), this); ++ gtk_box_pack_start(GTK_BOX(vbox1),check_button,false,false,0); ++ ++ GtkWidget *hbox1 = gtk_hbox_new(FALSE, 6); ++ GtkWidget *label=gtk_label_new(_("File name:")); ++ gtk_box_pack_start(GTK_BOX(hbox1), label, FALSE, FALSE, 0); ++ GtkWidget *e = gtk_entry_new(); ++ const std::string &exportfile= conf->get_string_at("dictionary/export_file"); ++ gtk_entry_set_text(GTK_ENTRY(e), exportfile.c_str()); ++ gtk_box_pack_start(GTK_BOX(hbox1), e, TRUE, TRUE, 0); ++ eExportFile=GTK_ENTRY(e); ++ ++ GtkWidget *button; ++ button = gtk_button_new_with_mnemonic(_("_Browse...")); ++ GTK_WIDGET_UNSET_FLAGS (button, GTK_CAN_FOCUS); ++ g_signal_connect(G_OBJECT(button),"clicked", G_CALLBACK(on_setup_dictionary_export_browse_button_clicked), this); ++ gtk_box_pack_start (GTK_BOX (hbox1), button, FALSE, FALSE, 0); ++ gtk_box_pack_start(GTK_BOX(vbox1), hbox1, FALSE, FALSE, 0); ++} ++ ++void PrefsDlg::on_markup_search_word(GtkToggleButton *button, PrefsDlg *) ++{ ++ conf->set_bool_at("dictionary/markup_search_word", ++ gtk_toggle_button_get_active(button)); ++} ++ ++void PrefsDlg::setup_dict_article_rendering() ++{ ++ GtkWidget *vbox = prepare_page(GTK_NOTEBOOK(notebook), _("Article rendering"), ++ GTK_STOCK_CONVERT); ++ GtkWidget *vbox1 = gtk_vbox_new(FALSE, 6); ++ gtk_box_pack_start(GTK_BOX(vbox), vbox1, FALSE, FALSE, 0); ++ ++ GtkWidget *ck_btn = ++ gtk_check_button_new_with_mnemonic(_("_Highlight search term")); ++ gtk_box_pack_start(GTK_BOX(vbox1), ck_btn, FALSE, FALSE, 0); ++ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ck_btn), ++ conf->get_bool_at("dictionary/markup_search_word")); ++ g_signal_connect(G_OBJECT(ck_btn), "toggled", ++ G_CALLBACK(on_markup_search_word), this); ++} ++ ++void PrefsDlg::on_setup_dictionary_sound_ckbutton_toggled(GtkToggleButton *button, PrefsDlg *oPrefsDlg) ++{ ++ gboolean enable = gtk_toggle_button_get_active(button); ++ conf->set_bool_at("dictionary/enable_sound_event",enable); ++} ++ ++#ifndef _WIN32 ++void PrefsDlg::on_setup_dictionary_use_tts_program_ckbutton_toggled(GtkToggleButton *button, PrefsDlg *oPrefsDlg) ++{ ++ gboolean enable = gtk_toggle_button_get_active(button); ++ gtk_widget_set_sensitive(oPrefsDlg->use_tts_program_hbox,enable); ++ conf->set_bool("/apps/stardict/preferences/dictionary/use_tts_program", enable); ++ gpAppFrame->oReadWord.use_command_tts = enable; ++ gpAppFrame->oMidWin.oToolWin.UpdatePronounceMenu(); ++} ++#endif ++ ++void PrefsDlg::setup_dictionary_sound_page() ++{ ++ GtkWidget *vbox = prepare_page(GTK_NOTEBOOK(notebook), _("Sound"), GTK_STOCK_YES); ++ GtkWidget *vbox1 = gtk_vbox_new(false, 6); ++ gtk_box_pack_start(GTK_BOX(vbox),vbox1,false,false, 0); ++ ++ GtkWidget *check_button; ++ check_button = gtk_check_button_new_with_mnemonic(_("_Enable sound event.")); ++ bool enable= ++ conf->get_bool_at("dictionary/enable_sound_event"); ++ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_button), enable); ++ g_signal_connect (G_OBJECT (check_button), "toggled", G_CALLBACK (on_setup_dictionary_sound_ckbutton_toggled), (gpointer)this); ++ gtk_box_pack_start(GTK_BOX(vbox1),check_button,false,false,0); ++ GtkWidget *label; ++#if defined(CONFIG_GTK) || defined(CONFIG_GPE) ++ GtkWidget *hbox2 = gtk_hbox_new(FALSE, 6); ++ label=gtk_label_new(_("Command for playing wav files:")); ++ gtk_box_pack_start(GTK_BOX(hbox2), label, FALSE, FALSE, 0); ++ GtkWidget *e = gtk_entry_new(); ++ gtk_widget_set_size_request(e, 50, -1); ++ const std::string &playcmd= ++ conf->get_string_at("dictionary/play_command"); ++ gtk_entry_set_text(GTK_ENTRY(e), playcmd.c_str()); ++ gtk_box_pack_start(GTK_BOX(hbox2), e, TRUE, TRUE, 0); ++ gtk_widget_set_sensitive(hbox2, enable); ++ ePlayCommand=GTK_ENTRY(e); ++ gtk_box_pack_start(GTK_BOX(vbox1), hbox2, FALSE, FALSE, 0); ++#endif ++ ++ label = gtk_label_new(_("RealPeopleTTS search path:")); ++ gtk_misc_set_alignment(GTK_MISC(label), 0, .5); ++ gtk_box_pack_start(GTK_BOX(vbox1),label,false,false,0); ++ tts_textview = gtk_text_view_new(); ++ gtk_widget_set_size_request(tts_textview, -1, 70); ++ gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(tts_textview), GTK_WRAP_CHAR); ++ const std::string &ttspath = conf->get_string_at("dictionary/tts_path"); ++ GtkTextBuffer *text_view_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(tts_textview)); ++ gtk_text_buffer_set_text(text_view_buffer, ttspath.c_str(), -1); ++ GtkWidget *scrolled_window = gtk_scrolled_window_new(NULL, NULL); ++ gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_window), ++ GTK_SHADOW_ETCHED_IN); ++ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window), ++ GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); ++ gtk_container_add(GTK_CONTAINER(scrolled_window), tts_textview); ++ gtk_box_pack_start(GTK_BOX(vbox1),scrolled_window,false,false,0); ++ ++#ifndef _WIN32 ++ check_button = gtk_check_button_new_with_mnemonic(_("_Use TTS program.")); ++ enable = conf->get_bool("/apps/stardict/preferences/dictionary/use_tts_program"); ++ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_button), enable); ++ g_signal_connect (G_OBJECT (check_button), "toggled", G_CALLBACK (on_setup_dictionary_use_tts_program_ckbutton_toggled), this); ++ gtk_box_pack_start(GTK_BOX(vbox1),check_button,false,false,0); ++ use_tts_program_hbox = gtk_hbox_new(FALSE, 6); ++ gtk_box_pack_start(GTK_BOX(vbox1),use_tts_program_hbox,false,false,0); ++ gtk_widget_set_sensitive(use_tts_program_hbox,enable); ++ label = gtk_label_new(_("Commandline:")); ++ gtk_misc_set_alignment(GTK_MISC(label), 0, .5); ++ gtk_box_pack_start(GTK_BOX(use_tts_program_hbox),label,false,false,0); ++ GtkWidget *comboboxentry = gtk_combo_box_entry_new_text(); ++ gtk_widget_set_size_request(comboboxentry, 30, -1); ++ gtk_combo_box_append_text(GTK_COMBO_BOX(comboboxentry), "echo %s | festival --tts &"); ++ gtk_combo_box_append_text(GTK_COMBO_BOX(comboboxentry), "espeak %s &"); ++ eTTSCommandline = GTK_ENTRY(GTK_BIN(comboboxentry)->child); ++ const std::string &tts_program_cmdline = conf->get_string("/apps/stardict/preferences/dictionary/tts_program_cmdline"); ++ gtk_entry_set_text(eTTSCommandline, tts_program_cmdline.c_str()); ++ gtk_box_pack_start(GTK_BOX(use_tts_program_hbox),comboboxentry,true,true,0); ++#endif ++} ++ ++void PrefsDlg::on_setup_network_netdict_ckbutton_toggled(GtkToggleButton *button, PrefsDlg *oPrefsDlg) ++{ ++ conf->set_bool_at("network/enable_netdict", ++ gtk_toggle_button_get_active(button)); ++} ++ ++static void on_account_passwd_entry_activated(GtkEntry *entry, GtkDialog *dialog) ++{ ++ gtk_dialog_response(dialog, GTK_RESPONSE_OK); ++} ++ ++void PrefsDlg::on_setup_network_account_button_clicked(GtkWidget *widget, PrefsDlg *oPrefsDlg) ++{ ++ GtkWidget *account_dialog; ++ account_dialog = ++ gtk_dialog_new_with_buttons (_("Account"), ++ GTK_WINDOW (oPrefsDlg->window), ++ GTK_DIALOG_DESTROY_WITH_PARENT, ++ GTK_STOCK_CANCEL, ++ GTK_RESPONSE_CANCEL, ++ GTK_STOCK_OK, ++ GTK_RESPONSE_OK, ++ NULL); ++ GtkWidget *table = gtk_table_new(2, 2, FALSE); ++ gtk_container_add(GTK_CONTAINER(GTK_DIALOG(account_dialog)->vbox), table); ++ gtk_container_set_border_width(GTK_CONTAINER(table), 6); ++ GtkWidget *label = gtk_label_new_with_mnemonic(_("_User Name:")); ++ gtk_misc_set_alignment(GTK_MISC(label), 0, .5); ++ GtkWidget *user_entry = gtk_entry_new (); ++ gtk_label_set_mnemonic_widget(GTK_LABEL(label), user_entry); ++ gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1, GTK_FILL, (GtkAttachOptions)0, 6, 4); ++ gtk_table_attach(GTK_TABLE(table), user_entry, 1, 2, 0, 1, GTK_EXPAND, (GtkAttachOptions)0, 0, 4); ++ label = gtk_label_new_with_mnemonic(_("_Password:")); ++ gtk_misc_set_alignment (GTK_MISC (label), 0, .5); ++ GtkWidget *passwd_entry = gtk_entry_new (); ++ gtk_entry_set_visibility(GTK_ENTRY(passwd_entry), FALSE); ++ g_signal_connect(G_OBJECT(passwd_entry),"activate", G_CALLBACK(on_account_passwd_entry_activated), account_dialog); ++ gtk_label_set_mnemonic_widget (GTK_LABEL (label), passwd_entry); ++ gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2, GTK_FILL, (GtkAttachOptions)0, 6, 4); ++ gtk_table_attach(GTK_TABLE(table), passwd_entry, 1, 2, 1, 2, GTK_EXPAND, (GtkAttachOptions)0, 0, 4); ++ gtk_dialog_set_default_response(GTK_DIALOG(account_dialog), GTK_RESPONSE_OK); ++ gtk_window_set_resizable(GTK_WINDOW(account_dialog), FALSE); ++ gtk_widget_show_all(GTK_WIDGET(account_dialog)); ++ while (gtk_dialog_run(GTK_DIALOG(account_dialog))==GTK_RESPONSE_OK) { ++ const gchar *user = gtk_entry_get_text(GTK_ENTRY(user_entry)); ++ if (!user[0]) { ++ conf->set_string_at("network/user", ""); ++ conf->set_string_at("network/md5passwd", ""); ++ gtk_button_set_label(oPrefsDlg->bAccount, "Guest"); ++ gpAppFrame->oStarDictClient.set_auth("", ""); ++ break; ++ } ++ gchar *error_msg = NULL; ++ const gchar *passwd = gtk_entry_get_text(GTK_ENTRY(passwd_entry)); ++ if (!passwd[0]) ++ error_msg = _("Please input the password."); ++ if (error_msg) { ++ GtkWidget *message_dlg = ++ gtk_message_dialog_new( ++ GTK_WINDOW(account_dialog), ++ (GtkDialogFlags) (GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT), ++ GTK_MESSAGE_INFO, GTK_BUTTONS_OK, ++ error_msg); ++ gtk_dialog_set_default_response(GTK_DIALOG(message_dlg), GTK_RESPONSE_OK); ++ gtk_window_set_resizable(GTK_WINDOW(message_dlg), FALSE); ++ gtk_dialog_run(GTK_DIALOG(message_dlg)); ++ gtk_widget_destroy(message_dlg); ++ continue; ++ } ++ conf->set_string_at("network/user", user); ++ struct MD5Context ctx; ++ unsigned char digest[16]; ++ MD5Init(&ctx); ++ MD5Update(&ctx, (const unsigned char*)passwd, strlen(passwd)); ++ MD5Final(digest, &ctx ); ++ char hex[33]; ++ for (int i = 0; i < 16; i++) ++ sprintf( hex+2*i, "%02x", digest[i] ); ++ hex[32] = '\0'; ++ conf->set_string_at("network/md5passwd", hex); ++ gtk_button_set_label(oPrefsDlg->bAccount, user); ++ gpAppFrame->oStarDictClient.set_auth(user, hex); ++ break; ++ } ++ gtk_widget_destroy(account_dialog); ++} ++ ++void PrefsDlg::on_register_end(const char *msg) ++{ ++ gtk_button_set_label(bAccount, register_user.c_str()); ++ conf->set_string_at("network/user", register_user); ++ conf->set_string_at("network/md5passwd", register_hex); ++ gpAppFrame->oStarDictClient.set_auth(register_user.c_str(), register_hex.c_str()); ++ ++ GtkWidget *message_dlg = gtk_message_dialog_new(GTK_WINDOW(window), ++ (GtkDialogFlags) (GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT), ++ GTK_MESSAGE_INFO, GTK_BUTTONS_OK, msg); ++ gtk_dialog_set_default_response(GTK_DIALOG(message_dlg), GTK_RESPONSE_OK); ++ gtk_window_set_resizable(GTK_WINDOW(message_dlg), FALSE); ++ g_signal_connect_swapped (message_dlg, "response", G_CALLBACK (gtk_widget_destroy), message_dlg); ++ gtk_widget_show(message_dlg); ++} ++ ++static void on_register_email_button_activated(GtkEntry *entry, GtkDialog *dialog) ++{ ++ gtk_dialog_response(dialog, GTK_RESPONSE_OK); ++} ++ ++void PrefsDlg::on_setup_network_register_button_clicked(GtkWidget *widget, PrefsDlg *oPrefsDlg) ++{ ++ GtkWidget *register_dialog; ++ register_dialog = ++ gtk_dialog_new_with_buttons (_("Register"), ++ GTK_WINDOW (oPrefsDlg->window), ++ GTK_DIALOG_DESTROY_WITH_PARENT, ++ GTK_STOCK_CANCEL, ++ GTK_RESPONSE_CANCEL, ++ GTK_STOCK_OK, ++ GTK_RESPONSE_OK, ++ NULL); ++ GtkWidget *table = gtk_table_new(3, 2, FALSE); ++ gtk_container_add(GTK_CONTAINER(GTK_DIALOG(register_dialog)->vbox), table); ++ gtk_container_set_border_width(GTK_CONTAINER(table), 6); ++ GtkWidget *label = gtk_label_new_with_mnemonic(_("_User Name:")); ++ gtk_misc_set_alignment(GTK_MISC(label), 0, .5); ++ GtkWidget *user_entry = gtk_entry_new (); ++ gtk_label_set_mnemonic_widget(GTK_LABEL(label), user_entry); ++ gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1, GTK_FILL, (GtkAttachOptions)0, 6, 4); ++ gtk_table_attach(GTK_TABLE(table), user_entry, 1, 2, 0, 1, GTK_EXPAND, (GtkAttachOptions)0, 0, 4); ++ label = gtk_label_new_with_mnemonic(_("_Password:")); ++ gtk_misc_set_alignment (GTK_MISC (label), 0, .5); ++ GtkWidget *passwd_entry = gtk_entry_new (); ++ gtk_entry_set_visibility(GTK_ENTRY(passwd_entry), FALSE); ++ gtk_label_set_mnemonic_widget (GTK_LABEL (label), passwd_entry); ++ gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2, GTK_FILL, (GtkAttachOptions)0, 6, 4); ++ gtk_table_attach(GTK_TABLE(table), passwd_entry, 1, 2, 1, 2, GTK_EXPAND, (GtkAttachOptions)0, 0, 4); ++ label = gtk_label_new_with_mnemonic(_("_Email:")); ++ gtk_misc_set_alignment(GTK_MISC(label), 0, .5); ++ GtkWidget *email_entry = gtk_entry_new (); ++ g_signal_connect(G_OBJECT(email_entry),"activate", G_CALLBACK(on_register_email_button_activated), register_dialog); ++ gtk_label_set_mnemonic_widget(GTK_LABEL(label), email_entry); ++ gtk_table_attach(GTK_TABLE(table), label, 0, 1, 2, 3, GTK_FILL, (GtkAttachOptions)0, 6, 4); ++ gtk_table_attach(GTK_TABLE(table), email_entry, 1, 2, 2, 3, GTK_EXPAND, (GtkAttachOptions)0, 0, 4); ++ gtk_dialog_set_default_response(GTK_DIALOG(register_dialog), GTK_RESPONSE_OK); ++ gtk_window_set_resizable(GTK_WINDOW(register_dialog), FALSE); ++ gtk_widget_show_all(GTK_WIDGET(register_dialog)); ++ while (gtk_dialog_run(GTK_DIALOG(register_dialog))==GTK_RESPONSE_OK) { ++ gchar *error_msg = NULL; ++ const gchar *user = gtk_entry_get_text(GTK_ENTRY(user_entry)); ++ const gchar *passwd = gtk_entry_get_text(GTK_ENTRY(passwd_entry)); ++ const gchar *email = gtk_entry_get_text(GTK_ENTRY(email_entry)); ++ if (!user[0]) ++ error_msg = _("Please input the user name."); ++ else if (!passwd[0]) ++ error_msg = _("Please input the password."); ++ else if (!email[0]) ++ error_msg = _("Please input the email."); ++ else if (strchr(email, '@')==NULL) ++ error_msg = _("Please input a valid email."); ++ if (error_msg) { ++ GtkWidget *message_dlg = ++ gtk_message_dialog_new( ++ GTK_WINDOW(register_dialog), ++ (GtkDialogFlags) (GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT), ++ GTK_MESSAGE_INFO, GTK_BUTTONS_OK, ++ error_msg); ++ gtk_dialog_set_default_response(GTK_DIALOG(message_dlg), GTK_RESPONSE_OK); ++ gtk_window_set_resizable(GTK_WINDOW(message_dlg), FALSE); ++ gtk_dialog_run(GTK_DIALOG(message_dlg)); ++ gtk_widget_destroy(message_dlg); ++ continue; ++ } ++ struct MD5Context ctx; ++ unsigned char digest[16]; ++ MD5Init(&ctx); ++ MD5Update(&ctx, (const unsigned char*)passwd, strlen(passwd)); ++ MD5Final(digest, &ctx ); ++ char hex[33]; ++ for (int i = 0; i < 16; i++) ++ sprintf( hex+2*i, "%02x", digest[i] ); ++ hex[32] = '\0'; ++ const gchar *server = gtk_entry_get_text(oPrefsDlg->eStarDictServer); ++ int port = atoi(gtk_entry_get_text(oPrefsDlg->eStarDictServerPort)); ++ gpAppFrame->oStarDictClient.set_server(server, port); ++ gpAppFrame->oStarDictClient.set_auth("", ""); ++ oPrefsDlg->register_user = user; ++ oPrefsDlg->register_hex = hex; ++ STARDICT::Cmd *c = new STARDICT::Cmd(STARDICT::CMD_REGISTER, user, hex, email); ++ gpAppFrame->oStarDictClient.send_commands(1, c); ++ break; ++ } ++ gtk_widget_destroy(register_dialog); ++} ++ ++void PrefsDlg::setup_network_netdict() ++{ ++ GtkWidget *vbox = prepare_page(GTK_NOTEBOOK(notebook), _("Net Dict"), ++ GTK_STOCK_NETWORK); ++ GtkWidget *vbox1 = gtk_vbox_new(FALSE, 6); ++ gtk_box_pack_start(GTK_BOX(vbox), vbox1, FALSE, FALSE, 0); ++ ++ GtkWidget *ck_btn = ++ gtk_check_button_new_with_mnemonic(_("Enable _network dictionaries.")); ++ gtk_box_pack_start(GTK_BOX(vbox1), ck_btn, FALSE, FALSE, 0); ++ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ck_btn), ++ conf->get_bool_at("network/enable_netdict")); ++ g_signal_connect(G_OBJECT(ck_btn), "toggled", ++ G_CALLBACK(on_setup_network_netdict_ckbutton_toggled), this); ++ ++ GtkWidget *table; ++ table = gtk_table_new(3, 2, FALSE); ++ gtk_table_set_row_spacings(GTK_TABLE(table), 6); ++ gtk_table_set_col_spacings(GTK_TABLE(table), 6); ++ gtk_box_pack_start(GTK_BOX(vbox1),table,false,false,0); ++ GtkWidget *label=gtk_label_new(_("StarDict server:")); ++ gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1, GTK_FILL, GTK_FILL, 0, 0); ++ GtkWidget *comboboxentry = gtk_combo_box_entry_new_text(); ++ gtk_table_attach(GTK_TABLE(table), comboboxentry, 1, 2, 0, 1, GTK_FILL, GTK_FILL, 0, 0); ++ gtk_combo_box_append_text(GTK_COMBO_BOX(comboboxentry), "dict.stardict.org"); ++ gtk_combo_box_append_text(GTK_COMBO_BOX(comboboxentry), "dict.stardict.cn"); ++ eStarDictServer=GTK_ENTRY(GTK_BIN(comboboxentry)->child); ++ const std::string &server= conf->get_string_at("network/server"); ++ gtk_entry_set_text(eStarDictServer, server.c_str()); ++ label=gtk_label_new(_("Port:")); ++ gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2, GTK_FILL, GTK_FILL, 0, 0); ++ GtkWidget *e = gtk_entry_new(); ++ int port = conf->get_int_at("network/port"); ++ gchar *str = g_strdup_printf("%d", port); ++ gtk_entry_set_text(GTK_ENTRY(e), str); ++ g_free(str); ++ gtk_table_attach(GTK_TABLE(table), e, 1, 2, 1, 2, GTK_FILL, GTK_FILL, 0, 0); ++ eStarDictServerPort=GTK_ENTRY(e); ++ label=gtk_label_new(_("Account:")); ++ gtk_table_attach(GTK_TABLE(table), label, 0, 1, 2, 3, GTK_FILL, GTK_FILL, 0, 0); ++ const std::string &user= conf->get_string_at("network/user"); ++ GtkWidget *button; ++ if (user.empty()) ++ button = gtk_button_new_with_label("Guest"); ++ else ++ button = gtk_button_new_with_label(user.c_str()); ++ g_signal_connect(G_OBJECT(button),"clicked", G_CALLBACK(on_setup_network_account_button_clicked), this); ++ gtk_table_attach(GTK_TABLE(table), button, 1, 2, 2, 3, GTK_FILL, GTK_FILL, 0, 0); ++ bAccount = GTK_BUTTON(button); ++ button = gtk_button_new_with_mnemonic(_("_Register an account")); ++ g_signal_connect(G_OBJECT(button),"clicked", G_CALLBACK(on_setup_network_register_button_clicked), this); ++ GtkWidget *hbox1 = gtk_hbox_new(FALSE, 6); ++ gtk_box_pack_start(GTK_BOX(hbox1),button,false,false,0); ++ gtk_box_pack_start(GTK_BOX(vbox1),hbox1,false,false,0); ++} ++ ++void PrefsDlg::on_setup_mainwin_searchWhileTyping_ckbutton_toggled(GtkToggleButton *button, PrefsDlg *oPrefsDlg) ++{ ++ conf->set_bool_at("main_window/search_while_typing", ++ gtk_toggle_button_get_active(button)); ++} ++ ++void PrefsDlg::on_setup_mainwin_showfirstWhenNotfound_ckbutton_toggled(GtkToggleButton *button, PrefsDlg *oPrefsDlg) ++{ ++ conf->set_bool_at("main_window/showfirst_when_notfound", ++ gtk_toggle_button_get_active(button)); ++} ++ ++void PrefsDlg::on_setup_mainwin_input_timeout_spinbutton_changed(GtkSpinButton *button, PrefsDlg *oPrefsDlg) ++{ ++ gint timeout = gtk_spin_button_get_value_as_int(button); ++ conf->set_int_at("main_window/word_change_timeout", timeout); ++ gpAppFrame->word_change_timeout = timeout; ++} ++ ++void PrefsDlg::setup_mainwin_input_page() ++{ ++ GtkWidget *vbox = prepare_page(GTK_NOTEBOOK(notebook), _("Input"), ++ GTK_STOCK_EDIT); ++ ++ GtkWidget *vbox1 = gtk_vbox_new(FALSE, 6); ++ gtk_box_pack_start(GTK_BOX(vbox),vbox1,FALSE,FALSE, 0); ++ ++ GtkWidget *check_button = ++ gtk_check_button_new_with_mnemonic(_("_Search while typing.")); ++ gtk_box_pack_start(GTK_BOX(vbox1), check_button, FALSE, FALSE, 0); ++ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_button), ++ conf->get_bool_at("main_window/search_while_typing")); ++ g_signal_connect(G_OBJECT(check_button), "toggled", ++ G_CALLBACK(on_setup_mainwin_searchWhileTyping_ckbutton_toggled), this); ++ GtkWidget *hbox = gtk_hbox_new(false, 5); ++ gtk_box_pack_start(GTK_BOX(vbox1),hbox,FALSE,FALSE, 0); ++ GtkWidget *label=gtk_label_new(NULL); ++ gtk_label_set_markup_with_mnemonic(GTK_LABEL(label), _("Word change _timeout:")); ++ gtk_box_pack_start(GTK_BOX(hbox),label,FALSE,FALSE, 0); ++ GtkWidget *spin_button; ++ spin_button = gtk_spin_button_new_with_range(50,2000,50); ++ gtk_label_set_mnemonic_widget(GTK_LABEL(label), spin_button); ++ gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin_button), GTK_UPDATE_IF_VALID); ++ gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin_button), conf->get_int_at("main_window/word_change_timeout")); ++ g_signal_connect(G_OBJECT(spin_button), "value-changed", G_CALLBACK(on_setup_mainwin_input_timeout_spinbutton_changed), this); ++ gtk_box_pack_start(GTK_BOX(hbox),spin_button,FALSE,FALSE, 0); ++ label=gtk_label_new(_("(default:300)")); ++ gtk_box_pack_start(GTK_BOX(hbox),label,FALSE,FALSE, 0); ++ check_button = gtk_check_button_new_with_mnemonic(_("Show the _first word when not found.")); ++ gtk_box_pack_start(GTK_BOX(vbox1),check_button,FALSE,FALSE,0); ++ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_button), conf->get_bool_at("main_window/showfirst_when_notfound")); ++ g_signal_connect(G_OBJECT(check_button), "toggled", ++ G_CALLBACK(on_setup_mainwin_showfirstWhenNotfound_ckbutton_toggled), this); ++} ++ ++void PrefsDlg::on_setup_mainwin_startup_ckbutton_toggled(GtkToggleButton *button, PrefsDlg *oPrefsDlg) ++{ ++ conf->set_bool_at("main_window/hide_on_startup", ++ gtk_toggle_button_get_active(button)); ++} ++ ++#ifdef _WIN32 ++void PrefsDlg::on_setup_mainwin_autorun_ckbutton_toggled(GtkToggleButton *button, PrefsDlg *oPrefsDlg) ++{ ++ gboolean b = gtk_toggle_button_get_active(button); ++ HKEY hKEY; ++ LONG lRet; ++ if (b) { ++ lRet =RegOpenKeyEx(HKEY_CURRENT_USER,"Software\\Microsoft\\Windows\\CurrentVersion\\Run",0,KEY_ALL_ACCESS,&hKEY); ++ if(lRet==ERROR_SUCCESS) { ++ std::string path = gStarDictDataDir+ G_DIR_SEPARATOR_S "stardict.exe"; ++ RegSetValueEx(hKEY, "StarDict", 0, REG_SZ, (const BYTE*)path.c_str(), path.length()+1); ++ RegCloseKey(hKEY); ++ } ++ } else { ++ lRet =RegOpenKeyEx(HKEY_CURRENT_USER,"Software\\Microsoft\\Windows\\CurrentVersion\\Run",0,KEY_ALL_ACCESS,&hKEY); ++ if(lRet==ERROR_SUCCESS) { ++ RegDeleteValue(hKEY, "StarDict"); ++ RegCloseKey(hKEY); ++ } ++ } ++} ++ ++void PrefsDlg::on_setup_mainwin_use_mainwindow_hotkey_ckbutton_toggled(GtkToggleButton *button, PrefsDlg *oPrefsDlg) ++{ ++ gboolean b = gtk_toggle_button_get_active(button); ++ if (b) ++ gpAppFrame->oHotkey.start_mainwindow(); ++ else ++ gpAppFrame->oHotkey.stop_mainwindow(); ++ conf->set_bool_at("dictionary/use_mainwindow_hotkey", b); ++} ++#endif ++ ++void PrefsDlg::on_setup_mainwin_transparent_scale_changed(GtkRange *range, PrefsDlg *oPrefsDlg) ++{ ++ gint transparent = (gint)gtk_range_get_value(range); ++ conf->set_int_at("main_window/transparent", transparent); ++ gtk_window_set_opacity(GTK_WINDOW(gpAppFrame->window), (100-transparent)/100.0); ++} ++ ++void PrefsDlg::setup_mainwin_options_page() ++{ ++ GtkWidget *vbox = prepare_page(GTK_NOTEBOOK(notebook), _("Options"), GTK_STOCK_EXECUTE); ++ GtkWidget *vbox1 = gtk_vbox_new(FALSE, 6); ++ gtk_box_pack_start(GTK_BOX(vbox),vbox1,FALSE,FALSE, 0); ++ ++ GtkWidget *check_button; ++#ifdef _WIN32 ++ check_button = gtk_check_button_new_with_mnemonic(_("_Auto run StarDict after boot.")); ++ gtk_box_pack_start(GTK_BOX(vbox1),check_button,FALSE,FALSE,0); ++ gboolean autorun; ++ ++ HKEY hKEY; ++ LONG lRet =RegOpenKeyEx(HKEY_CURRENT_USER,"Software\\Microsoft\\Windows\\CurrentVersion\\Run",0,KEY_QUERY_VALUE,&hKEY); ++ if(lRet!=ERROR_SUCCESS) { ++ autorun = false; ++ } else { ++ char owner_Get[80]; ++ DWORD cbData_1=80; ++ DWORD type_1=REG_SZ; ++ lRet=RegQueryValueEx(hKEY,"StarDict",NULL,&type_1,(LPBYTE)owner_Get,&cbData_1); ++ RegCloseKey(hKEY); ++ if((lRet!=ERROR_SUCCESS)||(cbData_1 > 80)) { ++ autorun = false; ++ } else { ++ std::string path = gStarDictDataDir+ G_DIR_SEPARATOR_S "stardict.exe"; ++ if (strcmp(path.c_str(), owner_Get)==0) ++ autorun = true; ++ else ++ autorun = false; ++ } ++ } ++ ++ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_button), autorun); ++ g_signal_connect(G_OBJECT(check_button), "toggled", ++ G_CALLBACK(on_setup_mainwin_autorun_ckbutton_toggled), this); ++#endif ++ ++ check_button = gtk_check_button_new_with_mnemonic(_("Hide main window when _starting StarDict.")); ++ gtk_box_pack_start(GTK_BOX(vbox1), check_button, FALSE, FALSE, 0); ++ bool hide= ++ conf->get_bool_at("main_window/hide_on_startup"); ++ ++ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_button), hide); ++ g_signal_connect(G_OBJECT(check_button), "toggled", ++ G_CALLBACK(on_setup_mainwin_startup_ckbutton_toggled), this); ++ ++#ifdef _WIN32 ++ check_button = gtk_check_button_new_with_mnemonic(_("_Use open main window hotkey: Ctrl+Alt+Z.")); ++ gtk_box_pack_start(GTK_BOX(vbox1),check_button,false,false,0); ++ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_button), conf->get_bool_at("dictionary/use_mainwindow_hotkey")); ++ g_signal_connect(G_OBJECT(check_button), "toggled", ++ G_CALLBACK(on_setup_mainwin_use_mainwindow_hotkey_ckbutton_toggled), this); ++#endif ++ ++ GtkWidget *hbox = gtk_hbox_new(false, 5); ++ gtk_box_pack_start(GTK_BOX(vbox1),hbox,FALSE,FALSE, 0); ++ GtkWidget *label=gtk_label_new(NULL); ++ gtk_label_set_markup_with_mnemonic(GTK_LABEL(label), _("_Transparency:")); ++ gtk_box_pack_start(GTK_BOX(hbox),label,FALSE,FALSE, 0); ++ GtkWidget *hscale; ++ hscale = gtk_hscale_new_with_range(0,80,1); ++ gtk_label_set_mnemonic_widget(GTK_LABEL(label), hscale); ++ int transparent=conf->get_int_at("main_window/transparent"); ++ gtk_range_set_value(GTK_RANGE(hscale), transparent); ++ g_signal_connect(G_OBJECT(hscale), "value-changed", G_CALLBACK(on_setup_mainwin_transparent_scale_changed), this); ++ gtk_box_pack_start(GTK_BOX(hbox),hscale,TRUE,TRUE, 0); ++} ++ ++void PrefsDlg::write_mainwin_searchwebsite_list() ++{ ++ GtkTreeIter iter; ++ gboolean have_iter; ++ gchar *website_name, *website_link, *website_searchlink; ++ std::list<std::string> searchwebsite_list; ++ GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW (searchwebsite_treeview)); ++ ++ have_iter = gtk_tree_model_get_iter_first(model, &iter); ++ while (have_iter) { ++ gtk_tree_model_get (model, &iter, 0, &website_name, 1, &website_link, 2, &website_searchlink, -1); ++ std::string website(std::string(website_name)+'\t'+website_link+'\t'+website_searchlink); ++ g_free(website_name); ++ g_free(website_link); ++ g_free(website_searchlink); ++ searchwebsite_list.push_back(website); ++ have_iter = gtk_tree_model_iter_next(model, &iter); ++ } ++ conf->set_strlist_at("main_window/search_website_list", searchwebsite_list); ++} ++ ++void PrefsDlg::on_setup_mainwin_searchwebsite_moveup_button_clicked(GtkWidget *widget, PrefsDlg *oPrefsDlg) ++{ ++ GtkTreeSelection *selection; ++ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (oPrefsDlg->searchwebsite_treeview)); ++ GtkTreeModel *model; ++ GtkTreeIter iter; ++ if (gtk_tree_selection_get_selected(selection, &model, &iter)) { ++ GtkTreePath* path = gtk_tree_model_get_path(model, &iter); ++ if (gtk_tree_path_prev(path)) { ++ GtkTreeIter prev; ++ gtk_tree_model_get_iter(model, &prev, path); ++ gtk_list_store_swap(GTK_LIST_STORE(model), &iter, &prev); ++ gtk_tree_selection_select_path(selection, path); ++ gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW (oPrefsDlg->searchwebsite_treeview), path, NULL, false, 0, 0); ++ oPrefsDlg->write_mainwin_searchwebsite_list(); ++ } ++ gtk_tree_path_free(path); ++ } ++} ++ ++void PrefsDlg::on_setup_mainwin_searchwebsite_movedown_button_clicked(GtkWidget *widget, PrefsDlg *oPrefsDlg) ++{ ++ GtkTreeSelection *selection; ++ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (oPrefsDlg->searchwebsite_treeview)); ++ GtkTreeModel *model; ++ GtkTreeIter iter; ++ if (gtk_tree_selection_get_selected(selection, &model, &iter)) { ++ GtkTreePath* path = gtk_tree_model_get_path(model, &iter); ++ gtk_tree_path_next(path); ++ GtkTreeIter next; ++ if (gtk_tree_model_get_iter(model, &next, path)) { ++ gtk_list_store_swap(GTK_LIST_STORE(model), &iter, &next); ++ gtk_tree_selection_select_path(selection, path); ++ gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW (oPrefsDlg->searchwebsite_treeview), path, NULL, false, 0, 0); ++ oPrefsDlg->write_mainwin_searchwebsite_list(); ++ } ++ gtk_tree_path_free(path); ++ } ++} ++ ++void PrefsDlg::on_setup_mainwin_searchwebsite_add_button_clicked(GtkWidget *widget, PrefsDlg *oPrefsDlg) ++{ ++ GtkWidget *searchwebsite_add_dialog; ++ GtkWidget *searchwebsite_add_dialog_name_entry; ++ GtkWidget *searchwebsite_add_dialog_link_entry; ++ GtkWidget *searchwebsite_add_dialog_searchlink_entry; ++ ++ searchwebsite_add_dialog = ++ gtk_dialog_new_with_buttons (_("Add"), ++ GTK_WINDOW (oPrefsDlg->window), ++ GTK_DIALOG_DESTROY_WITH_PARENT, ++ GTK_STOCK_CANCEL, ++ GTK_RESPONSE_CANCEL, ++ GTK_STOCK_OK, ++ GTK_RESPONSE_OK, ++ NULL); ++ GtkWidget *table = gtk_table_new(3, 2, FALSE); ++ gtk_container_add(GTK_CONTAINER(GTK_DIALOG(searchwebsite_add_dialog)->vbox), table); ++#ifndef CONFIG_GPE ++ gtk_container_set_border_width(GTK_CONTAINER(table), 6); ++#endif ++ GtkWidget *label = gtk_label_new_with_mnemonic(_("Website Name")); ++ gtk_misc_set_alignment(GTK_MISC(label), 0, .5); ++ searchwebsite_add_dialog_name_entry = gtk_entry_new (); ++#ifdef CONFIG_GPE ++ gtk_widget_set_size_request(searchwebsite_add_dialog_name_entry, 100, -1); ++#endif ++ gtk_entry_set_activates_default(GTK_ENTRY(searchwebsite_add_dialog_name_entry), TRUE); ++ gtk_label_set_mnemonic_widget(GTK_LABEL(label), searchwebsite_add_dialog_name_entry); ++ gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1, GTK_FILL, (GtkAttachOptions)0, 6, 4); ++ gtk_table_attach(GTK_TABLE(table), searchwebsite_add_dialog_name_entry, 1, 2, 0, 1, GTK_EXPAND, (GtkAttachOptions)0, 0, 4); ++ ++ ++ label = gtk_label_new_with_mnemonic(_("Website link")); ++ gtk_misc_set_alignment (GTK_MISC (label), 0, .5); ++ searchwebsite_add_dialog_link_entry = gtk_entry_new (); ++#ifdef CONFIG_GPE ++ gtk_widget_set_size_request(searchwebsite_add_dialog_link_entry, 100, -1); ++#endif ++ gtk_entry_set_activates_default (GTK_ENTRY (searchwebsite_add_dialog_link_entry), TRUE); ++ gtk_label_set_mnemonic_widget (GTK_LABEL (label), searchwebsite_add_dialog_link_entry); ++ gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2, GTK_FILL, (GtkAttachOptions)0, 6, 4); ++ gtk_table_attach(GTK_TABLE(table), searchwebsite_add_dialog_link_entry, 1, 2, 1, 2, GTK_EXPAND, (GtkAttachOptions)0, 0, 4); ++ ++ label = gtk_label_new_with_mnemonic(_("Website search link")); ++ gtk_misc_set_alignment (GTK_MISC (label), 0, .5); ++ searchwebsite_add_dialog_searchlink_entry = gtk_entry_new (); ++#ifdef CONFIG_GPE ++ gtk_widget_set_size_request(searchwebsite_add_dialog_searchlink_entry, 100, -1); ++#endif ++ gtk_entry_set_activates_default (GTK_ENTRY (searchwebsite_add_dialog_searchlink_entry), TRUE); ++ gtk_label_set_mnemonic_widget (GTK_LABEL (label), searchwebsite_add_dialog_searchlink_entry); ++ gtk_table_attach(GTK_TABLE(table), label, 0, 1, 2, 3, GTK_FILL, (GtkAttachOptions)0, 6, 4); ++ gtk_table_attach(GTK_TABLE(table), searchwebsite_add_dialog_searchlink_entry, 1, 2, 2, 3, GTK_EXPAND, (GtkAttachOptions)0, 0, 4); ++ ++ gtk_dialog_set_default_response(GTK_DIALOG(searchwebsite_add_dialog), GTK_RESPONSE_OK); ++ gtk_window_set_resizable(GTK_WINDOW(searchwebsite_add_dialog), FALSE); ++ ++ gtk_widget_show_all(GTK_WIDGET(searchwebsite_add_dialog)); ++ while (gtk_dialog_run(GTK_DIALOG(searchwebsite_add_dialog))==GTK_RESPONSE_OK) { ++ gchar *error_msg = NULL; ++ const gchar *website_name = gtk_entry_get_text(GTK_ENTRY(searchwebsite_add_dialog_name_entry)); ++ const gchar *website_link = gtk_entry_get_text(GTK_ENTRY(searchwebsite_add_dialog_link_entry)); ++ const gchar *website_searchlink = gtk_entry_get_text(GTK_ENTRY(searchwebsite_add_dialog_searchlink_entry)); ++ if (!website_name[0]) ++ error_msg = _("Please input the website name."); ++ else if (!website_link[0]) ++ error_msg = _("Please input the website link."); ++ else if (!website_searchlink[0]) ++ error_msg = _("Please input the website search link."); ++ else if (!strstr(website_searchlink, "%s")) { ++ error_msg = _("The website search link should contain a \"%%s\" string for querying a word."); ++ } ++ ++ if (error_msg) { ++ GtkWidget *message_dlg = ++ gtk_message_dialog_new( ++ GTK_WINDOW(searchwebsite_add_dialog), ++ (GtkDialogFlags) (GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT), ++ GTK_MESSAGE_INFO, GTK_BUTTONS_OK, ++ error_msg); ++ ++ gtk_dialog_set_default_response(GTK_DIALOG(message_dlg), GTK_RESPONSE_OK); ++ gtk_window_set_resizable(GTK_WINDOW(message_dlg), FALSE); ++ ++ gtk_dialog_run(GTK_DIALOG(message_dlg)); ++ gtk_widget_destroy(message_dlg); ++ continue; ++ } ++ GtkListStore *model = ++ GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(oPrefsDlg->searchwebsite_treeview))); ++ GtkTreeIter iter; ++ gtk_list_store_prepend(model, &iter); ++ gtk_list_store_set(model, &iter, ++ 0, website_name, ++ 1, website_link, ++ 2, website_searchlink, ++ 3, TRUE, ++ -1); ++ oPrefsDlg->write_mainwin_searchwebsite_list(); ++ break; ++ } ++ gtk_widget_destroy(searchwebsite_add_dialog); ++} ++ ++void PrefsDlg::on_setup_mainwin_searchwebsite_remove_button_clicked(GtkWidget *widget, PrefsDlg *oPrefsDlg) ++{ ++ GtkTreeSelection *selection; ++ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (oPrefsDlg->searchwebsite_treeview)); ++ GtkTreeModel *model; ++ GtkTreeIter iter; ++ if (gtk_tree_selection_get_selected(selection, &model, &iter)) { ++ if (gtk_list_store_remove(GTK_LIST_STORE(model), &iter)) { ++ GtkTreePath* path = gtk_tree_model_get_path(model, &iter); ++ gtk_tree_selection_select_path(selection, path); ++ gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW (oPrefsDlg->searchwebsite_treeview), path, NULL, false, 0, 0); ++ gtk_tree_path_free(path); ++ } ++ oPrefsDlg->write_mainwin_searchwebsite_list(); ++ } ++} ++ ++void PrefsDlg::on_setup_mainwin_searchwebsite_cell_edited(GtkCellRendererText *cell, const gchar *path_string, const gchar *new_text, PrefsDlg *oPrefsDlg) ++{ ++ GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW (oPrefsDlg->searchwebsite_treeview)); ++ GtkTreePath *path = gtk_tree_path_new_from_string (path_string); ++ GtkTreeIter iter; ++ ++ glong column; ++ column = (glong)(g_object_get_data (G_OBJECT (cell), "column")); ++ gtk_tree_model_get_iter (model, &iter, path); ++ ++ switch (column) { ++ case 0: ++ case 1: ++ if (new_text[0]) { ++ gtk_list_store_set (GTK_LIST_STORE (model), &iter, column, new_text, -1); ++ oPrefsDlg->write_mainwin_searchwebsite_list(); ++ } ++ break; ++ case 2: ++ if (new_text[0]) { ++ if (!strstr(new_text, "%s")) { ++ GtkWidget *message_dlg; ++ ++ message_dlg = gtk_message_dialog_new ( ++ GTK_WINDOW (oPrefsDlg->window), ++ (GtkDialogFlags) (GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT), ++ GTK_MESSAGE_INFO, ++ GTK_BUTTONS_OK, ++ _("The website search link should contain a \"%%s\" string for querying a word.")); ++ ++ gtk_dialog_set_default_response (GTK_DIALOG (message_dlg), GTK_RESPONSE_OK); ++ ++ gtk_window_set_resizable (GTK_WINDOW (message_dlg), FALSE); ++ ++ gtk_dialog_run (GTK_DIALOG (message_dlg)); ++ gtk_widget_destroy (message_dlg); ++ } ++ else { ++ gtk_list_store_set (GTK_LIST_STORE (model), &iter, 2, new_text, -1); ++ oPrefsDlg->write_mainwin_searchwebsite_list(); ++ } ++ } ++ break; ++ ++ } ++ ++ gtk_tree_path_free (path); ++} ++ ++void PrefsDlg::setup_mainwin_searchwebsite_page() ++{ ++ GtkWidget *vbox = prepare_page(GTK_NOTEBOOK(notebook), _("Search website"), GTK_STOCK_JUMP_TO); ++ GtkWidget *vbox2; ++ vbox2 = gtk_vbox_new(false, 6); ++ gtk_box_pack_start(GTK_BOX(vbox), vbox2, true, true,0); ++ ++ GtkListStore *model; ++ model = gtk_list_store_new (4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN); ++ ++ const std::list<std::string> &web_list= ++ conf->get_strlist_at("main_window/search_website_list"); ++ ++ GtkTreeIter iter; ++ for (std::list<std::string>::const_iterator wit=web_list.begin(); ++ wit!=web_list.end(); ++wit) { ++ std::vector<std::string> l=split(*wit, '\t'); ++ if (l.size()==3) { ++ gtk_list_store_append(model, &iter); ++ gtk_list_store_set(model, &iter, ++ 0, l[0].c_str(), ++ 1, l[1].c_str(), ++ 2, l[2].c_str(), ++ 3, TRUE, ++ -1); ++ } ++ } ++ ++ GtkWidget *sw; ++ sw = gtk_scrolled_window_new (NULL, NULL); ++ gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw), GTK_SHADOW_IN); ++ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), ++ GTK_POLICY_AUTOMATIC, ++ GTK_POLICY_AUTOMATIC); ++ ++ gtk_widget_set_size_request (sw, 300, 180); ++ ++ searchwebsite_treeview = gtk_tree_view_new_with_model (GTK_TREE_MODEL(model)); ++ g_object_unref (G_OBJECT (model)); ++ gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (searchwebsite_treeview), TRUE); ++ ++ GtkTreeSelection *selection; ++ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (searchwebsite_treeview)); ++ ++ gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE); ++ ++ GtkCellRenderer *renderer; ++ GtkTreeViewColumn *column; ++ ++ renderer = gtk_cell_renderer_text_new (); ++ g_signal_connect (renderer, "edited", G_CALLBACK (on_setup_mainwin_searchwebsite_cell_edited), this); ++ g_object_set (G_OBJECT (renderer), "xalign", 0.0, NULL); ++ g_object_set_data (G_OBJECT (renderer), "column", GINT_TO_POINTER(0)); ++ column = gtk_tree_view_column_new_with_attributes (_("Website Name"), renderer, "text", 0, "editable", 3, NULL); ++ gtk_tree_view_append_column (GTK_TREE_VIEW(searchwebsite_treeview), column); ++ gtk_tree_view_column_set_clickable (GTK_TREE_VIEW_COLUMN (column), FALSE); ++ ++ renderer = gtk_cell_renderer_text_new (); ++ g_signal_connect (renderer, "edited", G_CALLBACK (on_setup_mainwin_searchwebsite_cell_edited), this); ++ g_object_set (G_OBJECT (renderer), "xalign", 0.0, NULL); ++ g_object_set_data (G_OBJECT (renderer), "column", GINT_TO_POINTER(1)); ++ column = gtk_tree_view_column_new_with_attributes (_("Website link"), renderer, "text", 1, "editable", 3, NULL); ++ gtk_tree_view_append_column (GTK_TREE_VIEW(searchwebsite_treeview), column); ++ gtk_tree_view_column_set_clickable (GTK_TREE_VIEW_COLUMN (column), FALSE); ++ ++ renderer = gtk_cell_renderer_text_new (); ++ g_signal_connect (renderer, "edited", G_CALLBACK (on_setup_mainwin_searchwebsite_cell_edited), this); ++ g_object_set (G_OBJECT (renderer), "xalign", 0.0, NULL); ++ g_object_set_data (G_OBJECT (renderer), "column", GINT_TO_POINTER(2)); ++ column = gtk_tree_view_column_new_with_attributes (_("Website search link"), renderer, "text", 2, "editable", 3, NULL); ++ gtk_tree_view_append_column (GTK_TREE_VIEW(searchwebsite_treeview), column); ++ gtk_tree_view_column_set_clickable (GTK_TREE_VIEW_COLUMN (column), FALSE); ++ ++ gtk_container_add (GTK_CONTAINER (sw), searchwebsite_treeview); ++ gtk_box_pack_start (GTK_BOX (vbox2), sw, TRUE, TRUE, 0); ++ ++ GtkWidget *hbox1; ++ hbox1 = gtk_hbox_new(false,6); ++ GtkWidget *button; ++ button = gtk_button_new(); ++ GtkWidget *image = gtk_image_new_from_stock(GTK_STOCK_GO_UP, GTK_ICON_SIZE_BUTTON); ++ gtk_container_add(GTK_CONTAINER(button), image); ++ GTK_WIDGET_UNSET_FLAGS (button, GTK_CAN_FOCUS); ++ g_signal_connect(G_OBJECT(button),"clicked", G_CALLBACK(on_setup_mainwin_searchwebsite_moveup_button_clicked), this); ++ gtk_box_pack_start (GTK_BOX (hbox1), button, FALSE, FALSE, 0); ++ button = gtk_button_new(); ++ image = gtk_image_new_from_stock(GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_BUTTON); ++ gtk_container_add(GTK_CONTAINER(button), image); ++ GTK_WIDGET_UNSET_FLAGS (button, GTK_CAN_FOCUS); ++ g_signal_connect(G_OBJECT(button),"clicked", G_CALLBACK(on_setup_mainwin_searchwebsite_movedown_button_clicked), this); ++ gtk_box_pack_start (GTK_BOX (hbox1), button, FALSE, FALSE, 0); ++ button = gtk_button_new_from_stock(GTK_STOCK_REMOVE); ++ GTK_WIDGET_UNSET_FLAGS (button, GTK_CAN_FOCUS); ++ g_signal_connect(G_OBJECT(button),"clicked", G_CALLBACK(on_setup_mainwin_searchwebsite_remove_button_clicked), this); ++ gtk_box_pack_end (GTK_BOX (hbox1), button, FALSE, FALSE, 0); ++ ++/* button = gtk_button_new(); ++ GtkWidget *align = gtk_alignment_new (0.5, 0.5, 0.0, 0.0); ++ gtk_container_add (GTK_CONTAINER (button), align); ++ GtkWidget *hbox2 = gtk_hbox_new (FALSE, 2); ++ gtk_container_add (GTK_CONTAINER (align), hbox2); ++ label = gtk_label_new(NULL); ++ gtk_label_set_markup_with_mnemonic(GTK_LABEL(label), _("_Modify")); ++ gtk_label_set_mnemonic_widget(GTK_LABEL(label), button); ++ image = gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_BUTTON); ++ gtk_box_pack_start (GTK_BOX (hbox2), image, FALSE, FALSE, 0); ++ gtk_box_pack_end (GTK_BOX (hbox2), label, FALSE, FALSE, 0); ++ GTK_WIDGET_UNSET_FLAGS (button, GTK_CAN_FOCUS); ++ g_signal_connect(G_OBJECT(button),"clicked", G_CALLBACK(on_setup_mainwin_searchwebsite_edit_button_clicked), this); ++ gtk_box_pack_end (GTK_BOX (hbox1), button, FALSE, FALSE, 0);*/ ++ ++ button = gtk_button_new_from_stock(GTK_STOCK_ADD); ++ GTK_WIDGET_UNSET_FLAGS (button, GTK_CAN_FOCUS); ++ g_signal_connect(G_OBJECT(button),"clicked", G_CALLBACK(on_setup_mainwin_searchwebsite_add_button_clicked), this); ++ gtk_box_pack_end (GTK_BOX (hbox1), button, FALSE, FALSE, 0); ++ ++ gtk_box_pack_start (GTK_BOX (vbox2), hbox1, false, false, 0); ++} ++ ++void PrefsDlg::on_setup_NotificationAreaIcon_QueryInFloatWin_ckbutton_toggled(GtkToggleButton *button, PrefsDlg *oPrefsDlg) ++{ ++ gboolean queryin = gtk_toggle_button_get_active(button); ++ conf->set_bool_at("notification_area_icon/query_in_floatwin", ++ queryin); ++} ++ ++void PrefsDlg::setup_NotificationAreaIcon_options_page() ++{ ++ GtkWidget *vbox = prepare_page(GTK_NOTEBOOK(notebook), _("Options"), GTK_STOCK_DND); ++ GtkWidget *hbox1; ++ hbox1 = gtk_hbox_new(false,0); ++ gtk_box_pack_start(GTK_BOX(vbox),hbox1,false,false,0); ++ ++ GtkWidget *check_button; ++ check_button = gtk_check_button_new_with_mnemonic(_("_Query in the floating window when middle mouse\nbutton is clicked.")); ++ bool query_in_floatwin= ++ conf->get_bool_at("notification_area_icon/query_in_floatwin"); ++ ++ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_button), ++ query_in_floatwin); ++ g_signal_connect(G_OBJECT(check_button), "toggled", ++ G_CALLBACK(on_setup_NotificationAreaIcon_QueryInFloatWin_ckbutton_toggled), this); ++ gtk_box_pack_start(GTK_BOX(hbox1),check_button,false,false,0); ++} ++ ++void PrefsDlg::on_setup_floatwin_pronounce_ckbutton_toggled(GtkToggleButton *button, PrefsDlg *oPrefsDlg) ++{ ++ conf->set_bool_at("floating_window/pronounce_when_popup", ++ gtk_toggle_button_get_active(button)); ++} ++ ++void PrefsDlg::on_setup_show_float_if_not_found(GtkToggleButton *button, PrefsDlg *oPrefsDlg) ++{ ++ conf->set_bool_at("floating_window/show_if_not_found", ++ gtk_toggle_button_get_active(button)); ++} ++ ++void PrefsDlg::setup_floatwin_options_page() ++{ ++ GtkWidget *vbox = prepare_page(GTK_NOTEBOOK(notebook), _("Options"), GTK_STOCK_DND); ++ GtkWidget *vbox1 = gtk_vbox_new(false, 6); ++ gtk_box_pack_start(GTK_BOX(vbox),vbox1,false,false, 0); ++ GtkWidget *check_button = gtk_check_button_new_with_mnemonic(_("_Pronounce the word when it pops up.")); ++ bool pronounce_when_popup= ++ conf->get_bool_at("floating_window/pronounce_when_popup"); ++ ++ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_button), ++ pronounce_when_popup); ++ g_signal_connect(G_OBJECT(check_button), "toggled", G_CALLBACK(on_setup_floatwin_pronounce_ckbutton_toggled), this); ++ gtk_box_pack_start(GTK_BOX(vbox1), check_button, FALSE, FALSE, 0); ++ ++ check_button = gtk_check_button_new_with_mnemonic(_("_Show floating window if word not found.")); ++ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_button), conf->get_bool_at("floating_window/show_if_not_found")); ++ g_signal_connect(G_OBJECT(check_button), "toggled", G_CALLBACK(on_setup_show_float_if_not_found), this); ++ gtk_box_pack_start(GTK_BOX(vbox1), check_button, FALSE, FALSE, 0); ++} ++ ++#ifndef CONFIG_GPE ++void PrefsDlg::on_setup_floatwin_size_max_width_spinbutton_changed(GtkSpinButton *button, PrefsDlg *oPrefsDlg) ++{ ++ gint width = gtk_spin_button_get_value_as_int(button); ++ conf->set_int_at("floating_window/max_window_width", width); ++} ++ ++void PrefsDlg::on_setup_floatwin_size_max_height_spinbutton_changed(GtkSpinButton *button, PrefsDlg *oPrefsDlg) ++{ ++ gint height = gtk_spin_button_get_value_as_int(button); ++ conf->set_int_at("floating_window/max_window_height", height); ++} ++ ++void PrefsDlg::on_setup_floatwin_use_custom_bg_toggled(GtkToggleButton *button, PrefsDlg *oPrefsDlg) ++{ ++ gboolean use = gtk_toggle_button_get_active(button); ++ conf->set_bool_at("floating_window/use_custom_bg", use); ++ if (use) { ++ GdkColor color; ++ color.red = conf->get_int_at("floating_window/bg_red"); ++ color.green = conf->get_int_at("floating_window/bg_green"); ++ color.blue = conf->get_int_at("floating_window/bg_blue"); ++ gpAppFrame->oFloatWin.set_bg(&color); ++ } else { ++ gpAppFrame->oFloatWin.set_bg(NULL); ++ } ++} ++ ++void PrefsDlg::on_setup_floatwin_color_set(GtkColorButton *widget, PrefsDlg *oPrefsDlg) ++{ ++ GdkColor color; ++ gtk_color_button_get_color(widget, &color); ++ conf->set_int_at("floating_window/bg_red", color.red); ++ conf->set_int_at("floating_window/bg_green", color.green); ++ conf->set_int_at("floating_window/bg_blue", color.blue); ++ if (conf->get_bool_at("floating_window/use_custom_bg")) { ++ gpAppFrame->oFloatWin.set_bg(&color); ++ } ++} ++ ++void PrefsDlg::on_setup_floatwin_transparent_scale_changed(GtkRange *range, PrefsDlg *oPrefsDlg) ++{ ++ gint transparent = (gint)gtk_range_get_value(range); ++ conf->set_int_at("floating_window/transparent", transparent); ++ gtk_window_set_opacity(GTK_WINDOW(gpAppFrame->oFloatWin.FloatWindow), (100-transparent)/100.0); ++} ++ ++void PrefsDlg::setup_floatwin_size_page() ++{ ++ GtkWidget *vbox = prepare_page(GTK_NOTEBOOK(notebook), _("Settings"), GTK_STOCK_ZOOM_FIT); ++ GtkWidget *vbox1 = gtk_vbox_new(false, 6); ++ gtk_box_pack_start(GTK_BOX(vbox),vbox1,false,false, 0); ++ GtkWidget *table; ++ table = gtk_table_new(3, 2, FALSE); ++ gtk_table_set_row_spacings(GTK_TABLE(table), 6); ++ gtk_table_set_col_spacings(GTK_TABLE(table), 6); ++ gtk_box_pack_start(GTK_BOX(vbox1),table,false,false,0); ++ ++ int max_width= ++ conf->get_int_at("floating_window/max_window_width"); ++ int max_height= ++ conf->get_int_at("floating_window/max_window_height"); ++ ++ GdkScreen *screen = gtk_window_get_screen(parent_window); ++ gint screen_width = gdk_screen_get_width(screen); ++ gint screen_height = gdk_screen_get_height(screen); ++ ++ GtkWidget *label=gtk_label_new(NULL); ++ gtk_label_set_markup_with_mnemonic(GTK_LABEL(label), _("Max window _width:")); ++ gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1, GTK_FILL, GTK_FILL, 0, 0); ++ GtkWidget *spin_button; ++ spin_button = gtk_spin_button_new_with_range(MIN_MAX_FLOATWIN_WIDTH,screen_width,1); ++ gtk_label_set_mnemonic_widget(GTK_LABEL(label), spin_button); ++ gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin_button), GTK_UPDATE_IF_VALID); ++ gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin_button), max_width); ++ g_signal_connect(G_OBJECT(spin_button), "value-changed", ++ G_CALLBACK(on_setup_floatwin_size_max_width_spinbutton_changed), this); ++ gtk_table_attach(GTK_TABLE(table), spin_button, 1, 2, 0, 1, GTK_FILL, GTK_FILL, 0, 0); ++ label=gtk_label_new(_("(default:320)")); ++ gtk_table_attach(GTK_TABLE(table), label, 2, 3, 0, 1, GTK_FILL, GTK_FILL, 0, 0); ++ ++ label=gtk_label_new(NULL); ++ gtk_label_set_markup_with_mnemonic(GTK_LABEL(label), _("Max window hei_ght:")); ++ gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2, GTK_FILL, GTK_FILL, 0, 0); ++ spin_button = gtk_spin_button_new_with_range(MIN_MAX_FLOATWIN_HEIGHT,screen_height,1); ++ gtk_label_set_mnemonic_widget(GTK_LABEL(label), spin_button); ++ gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin_button), GTK_UPDATE_IF_VALID); ++ gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin_button), max_height); ++ g_signal_connect (G_OBJECT (spin_button), "value-changed", G_CALLBACK (on_setup_floatwin_size_max_height_spinbutton_changed), (gpointer)this); ++ gtk_table_attach(GTK_TABLE(table), spin_button, 1, 2, 1, 2, GTK_FILL, GTK_FILL, 0, 0); ++ label=gtk_label_new(_("(default:240)")); ++ gtk_table_attach(GTK_TABLE(table), label, 2, 3, 1, 2, GTK_FILL, GTK_FILL, 0, 0); ++ ++ GtkWidget*hbox1 = gtk_hbox_new(false, 5); ++ gtk_box_pack_start(GTK_BOX(vbox1),hbox1,false,false,0); ++ GtkWidget *check_button = gtk_check_button_new_with_mnemonic(_("_Use custom background color:")); ++ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_button), conf->get_bool_at("floating_window/use_custom_bg")); ++ g_signal_connect(G_OBJECT(check_button), "toggled", G_CALLBACK(on_setup_floatwin_use_custom_bg_toggled), this); ++ gtk_box_pack_start(GTK_BOX(hbox1),check_button,false,false,0); ++ GdkColor color; ++ color.red = conf->get_int_at("floating_window/bg_red"); ++ color.green = conf->get_int_at("floating_window/bg_green"); ++ color.blue = conf->get_int_at("floating_window/bg_blue"); ++ GtkWidget *colorbutton = gtk_color_button_new_with_color(&color); ++ g_signal_connect(G_OBJECT(colorbutton), "color-set", G_CALLBACK(on_setup_floatwin_color_set), this); ++ gtk_box_pack_start(GTK_BOX(hbox1),colorbutton,false,false,0); ++ ++ GtkWidget *hbox = gtk_hbox_new(false, 5); ++ gtk_box_pack_start(GTK_BOX(vbox1),hbox,FALSE,FALSE, 0); ++ label=gtk_label_new(NULL); ++ gtk_label_set_markup_with_mnemonic(GTK_LABEL(label), _("_Transparency:")); ++ gtk_box_pack_start(GTK_BOX(hbox),label,FALSE,FALSE, 0); ++ GtkWidget *hscale; ++ hscale = gtk_hscale_new_with_range(0,80,1); ++ gtk_label_set_mnemonic_widget(GTK_LABEL(label), hscale); ++ int transparent=conf->get_int_at("floating_window/transparent"); ++ gtk_range_set_value(GTK_RANGE(hscale), transparent); ++ g_signal_connect(G_OBJECT(hscale), "value-changed", G_CALLBACK(on_setup_floatwin_transparent_scale_changed), this); ++ gtk_box_pack_start(GTK_BOX(hbox),hscale,TRUE,TRUE, 0); ++} ++#endif ++ ++GtkWidget* PrefsDlg::create_notebook () ++{ ++ notebook = gtk_notebook_new(); ++ GtkNotebook *nb = GTK_NOTEBOOK(notebook); ++#ifdef CONFIG_GPE ++ gtk_notebook_set_scrollable(nb, true); ++#else ++ gtk_notebook_set_show_tabs(nb,false); ++ gtk_notebook_set_show_border(nb,false); ++ setup_logo_page (); ++#endif ++ setup_dictionary_scan_page (); ++ setup_dictionary_font_page (); ++ setup_dictionary_cache_page (); ++ setup_dictionary_export_page (); ++ setup_dictionary_sound_page (); ++ setup_dict_article_rendering(); ++ setup_network_netdict(); ++ setup_mainwin_input_page (); ++ setup_mainwin_options_page (); ++ setup_mainwin_searchwebsite_page(); ++ setup_NotificationAreaIcon_options_page(); ++ setup_floatwin_options_page (); ++#ifdef CONFIG_GPE ++ gtk_notebook_set_current_page (nb, 0); ++#else ++ setup_floatwin_size_page (); ++ gtk_notebook_set_current_page (nb, LOGO); ++#endif ++ return notebook; ++} ++ ++ ++PrefsDlg::PrefsDlg(GtkWindow *parent, GdkPixbuf *logo, const std::list<std::string>& key_combs_) : ++ key_combs(key_combs_) ++{ ++ parent_window=parent; ++#ifndef CONFIG_GPE ++ stardict_logo=logo; ++#endif ++ ++ window = NULL; ++} ++ ++bool PrefsDlg::ShowModal() ++{ ++ window = gtk_dialog_new(); ++ gtk_window_set_transient_for(GTK_WINDOW(window), parent_window); ++ ++ gtk_dialog_add_button(GTK_DIALOG(window), ++ GTK_STOCK_HELP, ++ GTK_RESPONSE_HELP); ++ ++ gtk_dialog_add_button(GTK_DIALOG(window), ++ GTK_STOCK_CLOSE, ++ GTK_RESPONSE_CLOSE); ++ gtk_dialog_set_default_response(GTK_DIALOG(window), ++ GTK_RESPONSE_CLOSE); ++ g_signal_connect(G_OBJECT(window), "response", ++ G_CALLBACK(response_handler), this); ++#ifndef CONFIG_GPE ++ GtkWidget *hbox; ++ hbox = gtk_hbox_new (FALSE, 18); ++ gtk_container_set_border_width (GTK_CONTAINER (hbox), 10); ++ GtkWidget *r; ++ r = gtk_vbox_new (FALSE, 6); ++ ++ GtkWidget *label; ++ label = gtk_label_new_with_mnemonic (_("Cat_egories:")); ++ gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_LEFT); ++ g_object_set (G_OBJECT (label), "xalign", 0.0, NULL); ++ create_categories_tree(); ++ ++ ++ gtk_box_pack_start(GTK_BOX(r), label, FALSE, FALSE, 0); ++ gtk_box_pack_start(GTK_BOX(r), categories_window, TRUE, TRUE, 0); ++#endif ++ ++ GtkWidget *l = create_notebook (); ++ ++#ifdef CONFIG_GPE ++ gtk_box_pack_start (GTK_BOX (GTK_DIALOG (window)->vbox), l, true, true, 0); ++#else ++ gtk_box_pack_start (GTK_BOX (hbox), r, FALSE, FALSE, 0); ++ gtk_box_pack_start (GTK_BOX (hbox), l, TRUE, TRUE, 0); ++ gtk_box_pack_start (GTK_BOX (GTK_DIALOG (window)->vbox), hbox, true, true, 0); ++ gtk_label_set_mnemonic_widget (GTK_LABEL (label), categories_tree); ++#endif ++ ++ gtk_widget_show_all (GTK_DIALOG (window)->vbox); ++ gtk_window_set_title (GTK_WINDOW (window), _("Preferences")); ++ ++#ifndef CONFIG_GPE ++ resize_categories_tree(); ++#endif ++ gint result; ++ while ((result = gtk_dialog_run(GTK_DIALOG(window)))==GTK_RESPONSE_HELP) ++ ; ++ if (result != GTK_RESPONSE_NONE) { ++ const gchar *ch; ++ ch = gtk_entry_get_text(eExportFile); ++ if (ch[0]) ++ conf->set_string_at("dictionary/export_file", ch); ++#ifndef _WIN32 ++ ch = gtk_entry_get_text(eTTSCommandline); ++ if (ch[0]) { ++ conf->set_string("/apps/stardict/preferences/dictionary/tts_program_cmdline", ch); ++ gpAppFrame->oReadWord.tts_program_cmdline = ch; ++ } ++#endif ++ const gchar *server; ++ ch = gtk_entry_get_text(eStarDictServer); ++ if (ch[0]) ++ server = ch; ++ else ++ server = _("dict.stardict.org"); ++ conf->set_string_at("network/server", server); ++ int port; ++ ch = gtk_entry_get_text(eStarDictServerPort); ++ if (ch[0]) ++ port = atoi(ch); ++ else ++ port = 2628; ++ conf->set_int_at("network/port", port); ++ gpAppFrame->oStarDictClient.set_server(server, port); ++#if defined(CONFIG_GTK) || defined(CONFIG_GPE) ++ ch = gtk_entry_get_text(ePlayCommand); ++ if (ch[0]) ++ conf->set_string_at("dictionary/play_command", ch); ++#endif ++ GtkTextBuffer *text_view_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(tts_textview)); ++ GtkTextIter start_iter; ++ GtkTextIter end_iter; ++ gtk_text_buffer_get_start_iter(text_view_buffer, &start_iter); ++ gtk_text_buffer_get_end_iter(text_view_buffer, &end_iter); ++ gchar *text = gtk_text_buffer_get_text(text_view_buffer, &start_iter, &end_iter, FALSE); ++ conf->set_string_at("dictionary/tts_path", text); ++ gpAppFrame->oReadWord.LoadRealTtsPath(text); ++ g_free(text); ++ gtk_widget_destroy(GTK_WIDGET(window)); ++ window = NULL; ++ return false; ++ } else { ++ return true; ++ } ++} ++ ++void PrefsDlg::Close() ++{ ++ if (window) { ++ gtk_widget_destroy (window); ++ window = NULL; ++ } ++} ++ ++#ifndef CONFIG_GPE ++void PrefsDlg::resize_categories_tree(void) ++{ ++ //this is hack for prevet horizontaly scrolling ++ //if you know how it make better, just write ++ GtkRequisition rtv, rsw; ++ gtk_widget_size_request(categories_tree, &rtv); ++ gtk_widget_size_request(GTK_SCROLLED_WINDOW(categories_window)->vscrollbar, &rsw); ++ gtk_widget_set_size_request(categories_window, rtv.width+rsw.width+25, -1); ++} ++#endif +diff -Nur stardict-3.0.1.orig//src/readword.cpp stardict-3.0.1/src/readword.cpp +--- stardict-3.0.1.orig//src/readword.cpp 2007-07-10 02:16:04.000000000 -0500 ++++ stardict-3.0.1/src/readword.cpp 2010-05-24 00:53:36.380667202 -0500 +@@ -3,6 +3,7 @@ + #endif + + #include <cstring> ++#include <cstdlib> + #include <string> + + #include <glib/gi18n.h> +diff -Nur stardict-3.0.1.orig//src/sigc++/signal.h stardict-3.0.1/src/sigc++/signal.h +--- stardict-3.0.1.orig//src/sigc++/signal.h 2007-07-10 02:16:01.000000000 -0500 ++++ stardict-3.0.1/src/sigc++/signal.h 2010-05-24 00:53:36.381666157 -0500 +@@ -18,7 +18,7 @@ + //Compilers, such as older versions of SUN Forte C++, that do not allow this also often + //do not allow a typedef to have the same name as a class in the typedef's definition. + //For Sun Forte CC 5.7 (SUN Workshop 10), comment this out to fix the build. +- #define SIGC_TYPEDEF_REDEFINE_ALLOWED 1 ++// #define SIGC_TYPEDEF_REDEFINE_ALLOWED 1 + #endif + + namespace sigc { +diff -Nur stardict-3.0.1.orig//src/utils.cpp stardict-3.0.1/src/utils.cpp +--- stardict-3.0.1.orig//src/utils.cpp 2007-10-21 21:25:02.000000000 -0500 ++++ stardict-3.0.1/src/utils.cpp 2010-05-24 00:53:36.381666157 -0500 +@@ -22,6 +22,8 @@ + # include "config.h" + #endif + ++#include <cstring> ++ + #include <glib.h> + #include <glib/gi18n.h> + #include <cstdlib> +diff -Nur stardict-3.0.1.orig//src/utils.cpp~ stardict-3.0.1/src/utils.cpp~ +--- stardict-3.0.1.orig//src/utils.cpp~ 1969-12-31 18:00:00.000000000 -0600 ++++ stardict-3.0.1/src/utils.cpp~ 2007-10-21 21:25:02.000000000 -0500 +@@ -0,0 +1,203 @@ ++/* ++ * This file part of StarDict - A international dictionary for GNOME. ++ * http://stardict.sourceforge.net ++ * Copyright (C) 2005-2006 Evgeniy <dushistov@mail.ru> ++ * ++ * 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 3 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 Library 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. ++ */ ++ ++#ifdef HAVE_CONFIG_H ++# include "config.h" ++#endif ++ ++#include <glib.h> ++#include <glib/gi18n.h> ++#include <cstdlib> ++#include <gtk/gtk.h> ++ ++#ifdef CONFIG_GNOME ++# include <libgnome/libgnome.h> ++# include <libgnomeui/libgnomeui.h> ++#elif defined(_WIN32) ++# include <gdk/gdkwin32.h> ++#endif ++ ++#include "utils.h" ++ ++ ++void ProcessGtkEvent() ++{ ++ while (gtk_events_pending()) ++ gtk_main_iteration(); ++} ++ ++std::string get_user_config_dir() ++{ ++ const gchar *config_path_from_env = g_getenv("STARDICT_CONFIG_PATH"); ++ if (config_path_from_env) ++ return config_path_from_env; ++#ifdef _WIN32 ++ std::string res = g_get_user_config_dir(); ++ res += G_DIR_SEPARATOR_S "StarDict"; ++ return res; ++#else ++ std::string res; ++ gchar *tmp = g_build_filename(g_get_home_dir(), ".stardict", NULL); ++ res=tmp; ++ g_free(tmp); ++ return res; ++#endif ++} ++ ++std::string combnum2str(gint comb_code) ++{ ++ switch (comb_code) { ++#ifdef _WIN32 ++ case 0: ++ return "Shift"; ++ case 1: ++ return "Alt"; ++ case 2: ++ return "Ctrl"; ++ case 3: ++ return "Ctrl+Alt"; ++#else ++ case 0: ++ return "Win"; ++ case 1: ++ return "Shift"; ++ case 2: ++ return "Alt"; ++ case 3: ++ return "Ctrl"; ++ case 4: ++ return "Ctrl+Alt"; ++ case 5: ++ return "Ctrl+e"; ++ case 6: ++ return "F1"; ++ case 7: ++ return "F2"; ++ case 8: ++ return "F3"; ++ case 9: ++ return "F4"; ++#endif ++ default: ++ return ""; ++ } ++} ++ ++std::vector<std::string> split(const std::string& str, char sep) ++{ ++ std::vector<std::string> res; ++ std::string::size_type prev_pos=0, pos = 0; ++ while ((pos=str.find(sep, prev_pos))!=std::string::npos) { ++ res.push_back(std::string(str, prev_pos, pos-prev_pos)); ++ prev_pos=pos+1; ++ } ++ res.push_back(std::string(str, prev_pos, str.length()-prev_pos)); ++ ++ return res; ++} ++ ++GdkPixbuf *load_image_from_file(const std::string& filename) ++{ ++ GError *err=NULL; ++ GdkPixbuf *res=gdk_pixbuf_new_from_file(filename.c_str(), &err); ++ if (!res) { ++ GtkWidget *message_dlg = ++ gtk_message_dialog_new( ++ NULL, ++ (GtkDialogFlags)(GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT), ++ GTK_MESSAGE_ERROR, ++ GTK_BUTTONS_OK, ++ _("Can not load image. %s"), err->message); ++ ++ gtk_dialog_set_default_response(GTK_DIALOG(message_dlg), GTK_RESPONSE_OK); ++ ++ gtk_window_set_resizable(GTK_WINDOW(message_dlg), FALSE); ++ ++ gtk_dialog_run(GTK_DIALOG(message_dlg)); ++ gtk_widget_destroy(message_dlg); ++ g_error_free(err); ++ exit(EXIT_FAILURE); ++ } ++ ++ return res; ++} ++ ++static gchar * byte_to_hex(unsigned char nr) { ++ gchar *result = NULL; ++ ++ result = g_strdup_printf("%%%x%x", nr / 0x10, nr % 0x10); ++ return result; ++} ++ ++char *common_encode_uri_string(const char *string) ++{ ++ gchar *newURIString; ++ gchar *hex, *tmp = NULL; ++ int i, j, len, bytes; ++ ++ /* the UTF-8 string is casted to ASCII to treat ++ the characters bytewise and convert non-ASCII ++ compatible chars to URI hexcodes */ ++ newURIString = g_strdup(""); ++ len = strlen(string); ++ for(i = 0; i < len; i++) { ++ if(g_ascii_isalnum(string[i]) || strchr("-_.!~*'()", (int)string[i])) ++ tmp = g_strdup_printf("%s%c", newURIString, string[i]); ++ else if(string[i] == ' ') ++ tmp = g_strdup_printf("%s%%20", newURIString); ++ else if((unsigned char)string[i] <= 127) { ++ tmp = g_strdup_printf("%s%s", newURIString, hex = byte_to_hex(string[i]));g_free(hex); ++ } else { ++ bytes = 0; ++ if(((unsigned char)string[i] >= 192) && ((unsigned char)string[i] <= 223)) ++ bytes = 2; ++ else if(((unsigned char)string[i] > 223) && ((unsigned char)string[i] <= 239)) ++ bytes = 3; ++ else if(((unsigned char)string[i] > 239) && ((unsigned char)string[i] <= 247)) ++ bytes = 4; ++ else if(((unsigned char)string[i] > 247) && ((unsigned char)string[i] <= 251)) ++ bytes = 5; ++ else if(((unsigned char)string[i] > 247) && ((unsigned char)string[i] <= 251)) ++ bytes = 6; ++ ++ if(0 != bytes) { ++ if((i + (bytes - 1)) > len) { ++ g_warning(("Unexpected end of character sequence or corrupt UTF-8 encoding! Some characters were dropped!")); ++ break; ++ } ++ ++ for(j=0; j < (bytes - 1); j++) { ++ tmp = g_strdup_printf("%s%s", newURIString, hex = byte_to_hex((unsigned char)string[i++])); ++ g_free(hex); ++ g_free(newURIString); ++ newURIString = tmp; ++ } ++ tmp = g_strdup_printf("%s%s", newURIString, hex = byte_to_hex((unsigned char)string[i])); ++ g_free(hex); ++ } else { ++ /* sh..! */ ++ g_error("Internal error while converting UTF-8 chars to HTTP URI!"); ++ } ++ } ++ g_free(newURIString); ++ newURIString = tmp; ++ } ++ return newURIString; ++} +diff -Nur stardict-3.0.1.orig//src/x11_iskeyspressed.hpp stardict-3.0.1/src/x11_iskeyspressed.hpp +--- stardict-3.0.1.orig//src/x11_iskeyspressed.hpp 2007-07-10 02:16:04.000000000 -0500 ++++ stardict-3.0.1/src/x11_iskeyspressed.hpp 2010-05-24 00:53:36.381666157 -0500 +@@ -1,6 +1,8 @@ + #ifndef _X11_ISKEYSPRESSED_HPP_ + #define _X11_ISKEYSPRESSED_HPP_ + ++#include <memory> ++ + #include <gdk/gdkx.h> + #include <X11/keysym.h> + #include <gtk/gtk.h> +diff -Nur stardict-3.0.1.orig//stardict-plugins/stardict-dictdotcn-plugin/stardict_dictdotcn.cpp stardict-3.0.1/stardict-plugins/stardict-dictdotcn-plugin/stardict_dictdotcn.cpp +--- stardict-3.0.1.orig//stardict-plugins/stardict-dictdotcn-plugin/stardict_dictdotcn.cpp 2007-10-10 04:28:29.000000000 -0500 ++++ stardict-3.0.1/stardict-plugins/stardict-dictdotcn-plugin/stardict_dictdotcn.cpp 2010-05-24 00:53:36.381666157 -0500 +@@ -1,6 +1,6 @@ + #include "stardict_dictdotcn.h" + #include <glib/gi18n.h> +-#include <string> ++#include <cstring> + #include <list> + + #ifdef _WIN32 +diff -Nur stardict-3.0.1.orig//stardict-plugins/stardict-espeak-tts-plugin/stardict_espeak.cpp stardict-3.0.1/stardict-plugins/stardict-espeak-tts-plugin/stardict_espeak.cpp +--- stardict-3.0.1.orig//stardict-plugins/stardict-espeak-tts-plugin/stardict_espeak.cpp 2007-09-19 03:27:18.000000000 -0500 ++++ stardict-3.0.1/stardict-plugins/stardict-espeak-tts-plugin/stardict_espeak.cpp 2010-05-24 00:53:36.382665737 -0500 +@@ -1,4 +1,5 @@ + #include "stardict_espeak.h" ++#include <cstring> + #include <espeak/speak_lib.h> + #include <glib/gi18n.h> + +diff -Nur stardict-3.0.1.orig//stardict-plugins/stardict-gucharmap-plugin/stardict_gucharmap.cpp stardict-3.0.1/stardict-plugins/stardict-gucharmap-plugin/stardict_gucharmap.cpp +--- stardict-3.0.1.orig//stardict-plugins/stardict-gucharmap-plugin/stardict_gucharmap.cpp 2007-08-31 02:10:41.000000000 -0500 ++++ stardict-3.0.1/stardict-plugins/stardict-gucharmap-plugin/stardict_gucharmap.cpp 2010-05-24 00:53:36.382665737 -0500 +@@ -1,7 +1,8 @@ + #include "stardict_gucharmap.h" + #include <glib/gi18n.h> + #include <gucharmap/gucharmap.h> +-#include <string> ++#include <cstring> ++#include <cstdlib> + + static char *build_dictdata(char type, const char *definition) + { +diff -Nur stardict-3.0.1.orig//stardict-plugins/stardict-html-parsedata-plugin/stardict_html_parsedata.cpp stardict-3.0.1/stardict-plugins/stardict-html-parsedata-plugin/stardict_html_parsedata.cpp +--- stardict-3.0.1.orig//stardict-plugins/stardict-html-parsedata-plugin/stardict_html_parsedata.cpp 2007-09-13 02:51:55.000000000 -0500 ++++ stardict-3.0.1/stardict-plugins/stardict-html-parsedata-plugin/stardict_html_parsedata.cpp 2010-05-24 00:53:36.382665737 -0500 +@@ -1,4 +1,6 @@ + #include "stardict_html_parsedata.h" ++#include <cstring> ++#include <cstdlib> + #include <glib/gi18n.h> + + #ifdef _WIN32 +diff -Nur stardict-3.0.1.orig//stardict-plugins/stardict-man-plugin/stardict_man.cpp stardict-3.0.1/stardict-plugins/stardict-man-plugin/stardict_man.cpp +--- stardict-3.0.1.orig//stardict-plugins/stardict-man-plugin/stardict_man.cpp 2007-09-19 03:30:54.000000000 -0500 ++++ stardict-3.0.1/stardict-plugins/stardict-man-plugin/stardict_man.cpp 2010-05-24 00:53:36.382665737 -0500 +@@ -1,6 +1,6 @@ + #include "stardict_man.h" + #include <glib/gi18n.h> +-#include <string> ++#include <cstring> + + static const StarDictPluginSystemInfo *plugin_info = NULL; + static bool need_prefix; +diff -Nur stardict-3.0.1.orig//stardict-plugins/stardict-powerword-parsedata-plugin/stardict_powerword_parsedata.cpp stardict-3.0.1/stardict-plugins/stardict-powerword-parsedata-plugin/stardict_powerword_parsedata.cpp +--- stardict-3.0.1.orig//stardict-plugins/stardict-powerword-parsedata-plugin/stardict_powerword_parsedata.cpp 2007-10-25 03:16:37.000000000 -0500 ++++ stardict-3.0.1/stardict-plugins/stardict-powerword-parsedata-plugin/stardict_powerword_parsedata.cpp 2010-05-24 00:53:36.382665737 -0500 +@@ -1,4 +1,5 @@ + #include "stardict_powerword_parsedata.h" ++#include <cstring> + #include <glib/gi18n.h> + + #ifdef _WIN32 +diff -Nur stardict-3.0.1.orig//stardict-plugins/stardict-qqwry-plugin/stardict_qqwry.cpp stardict-3.0.1/stardict-plugins/stardict-qqwry-plugin/stardict_qqwry.cpp +--- stardict-3.0.1.orig//stardict-plugins/stardict-qqwry-plugin/stardict_qqwry.cpp 2007-11-02 03:41:26.000000000 -0500 ++++ stardict-3.0.1/stardict-plugins/stardict-qqwry-plugin/stardict_qqwry.cpp 2010-05-24 00:53:36.382665737 -0500 +@@ -1,7 +1,8 @@ + #include "stardict_qqwry.h" + #include <glib/gi18n.h> + #include <glib/gstdio.h> +-#include <string> ++#include <cstring> ++#include <cstdlib> + + #ifdef _WIN32 + #include <windows.h> +diff -Nur stardict-3.0.1.orig//stardict-plugins/stardict-spell-plugin/stardict_spell.cpp stardict-3.0.1/stardict-plugins/stardict-spell-plugin/stardict_spell.cpp +--- stardict-3.0.1.orig//stardict-plugins/stardict-spell-plugin/stardict_spell.cpp 2007-09-19 03:29:21.000000000 -0500 ++++ stardict-3.0.1/stardict-plugins/stardict-spell-plugin/stardict_spell.cpp 2010-05-24 00:53:36.383666084 -0500 +@@ -1,4 +1,5 @@ + #include "stardict_spell.h" ++#include <cstring> + #include <glib.h> + #include <glib/gi18n.h> + #include <enchant.h> +diff -Nur stardict-3.0.1.orig//stardict-plugins/stardict-wiki-parsedata-plugin/stardict_wiki2xml.cpp stardict-3.0.1/stardict-plugins/stardict-wiki-parsedata-plugin/stardict_wiki2xml.cpp +--- stardict-3.0.1.orig//stardict-plugins/stardict-wiki-parsedata-plugin/stardict_wiki2xml.cpp 2007-07-10 02:16:15.000000000 -0500 ++++ stardict-3.0.1/stardict-plugins/stardict-wiki-parsedata-plugin/stardict_wiki2xml.cpp 2010-05-24 00:53:36.383666084 -0500 +@@ -1,5 +1,6 @@ + #include "stardict_wiki2xml.h" + #include "WIKI2XML.h" ++#include <cstring> + #include <glib.h> + + std::string wiki2xml(std::string &str) +diff -Nur stardict-3.0.1.orig//stardict-plugins/stardict-wiki-parsedata-plugin/stardict_wiki_parsedata.cpp stardict-3.0.1/stardict-plugins/stardict-wiki-parsedata-plugin/stardict_wiki_parsedata.cpp +--- stardict-3.0.1.orig//stardict-plugins/stardict-wiki-parsedata-plugin/stardict_wiki_parsedata.cpp 2007-08-31 01:41:21.000000000 -0500 ++++ stardict-3.0.1/stardict-plugins/stardict-wiki-parsedata-plugin/stardict_wiki_parsedata.cpp 2010-05-24 00:53:36.383666084 -0500 +@@ -1,5 +1,6 @@ + #include "stardict_wiki_parsedata.h" + #include "stardict_wiki2xml.h" ++#include <cstring> + #include <glib/gi18n.h> + + #ifdef _WIN32 +diff -Nur stardict-3.0.1.orig//stardict-plugins/stardict-wordnet-plugin/court_widget.cpp stardict-3.0.1/stardict-plugins/stardict-wordnet-plugin/court_widget.cpp +--- stardict-3.0.1.orig//stardict-plugins/stardict-wordnet-plugin/court_widget.cpp 2007-10-17 20:36:22.000000000 -0500 ++++ stardict-3.0.1/stardict-plugins/stardict-wordnet-plugin/court_widget.cpp 2010-05-24 00:53:36.383666084 -0500 +@@ -1,4 +1,5 @@ + #include "court_widget.h" ++#include <cstring> + #include <math.h> + #include <list> + +diff -Nur stardict-3.0.1.orig//stardict-plugins/stardict-wordnet-plugin/stardict_wordnet.cpp stardict-3.0.1/stardict-plugins/stardict-wordnet-plugin/stardict_wordnet.cpp +--- stardict-3.0.1.orig//stardict-plugins/stardict-wordnet-plugin/stardict_wordnet.cpp 2007-10-14 22:32:04.000000000 -0500 ++++ stardict-3.0.1/stardict-plugins/stardict-wordnet-plugin/stardict_wordnet.cpp 2010-05-24 00:53:36.383666084 -0500 +@@ -1,5 +1,6 @@ + #include "stardict_wordnet.h" + #include "court_widget.h" ++#include <cstring> + #include <glib/gi18n.h> + + #ifdef _WIN32 +diff -Nur stardict-3.0.1.orig//stardict-plugins/stardict-wordnet-plugin/stardict_wordnet_parsedata.cpp stardict-3.0.1/stardict-plugins/stardict-wordnet-plugin/stardict_wordnet_parsedata.cpp +--- stardict-3.0.1.orig//stardict-plugins/stardict-wordnet-plugin/stardict_wordnet_parsedata.cpp 2007-10-10 04:39:10.000000000 -0500 ++++ stardict-3.0.1/stardict-plugins/stardict-wordnet-plugin/stardict_wordnet_parsedata.cpp 2010-05-24 00:53:36.383666084 -0500 +@@ -1,4 +1,5 @@ + #include "stardict_wordnet_parsedata.h" ++#include <cstring> + #include <glib/gi18n.h> + + #ifdef _WIN32 +diff -Nur stardict-3.0.1.orig//stardict-plugins/stardict-xdxf-parsedata-plugin/stardict_xdxf_parsedata.cpp stardict-3.0.1/stardict-plugins/stardict-xdxf-parsedata-plugin/stardict_xdxf_parsedata.cpp +--- stardict-3.0.1.orig//stardict-plugins/stardict-xdxf-parsedata-plugin/stardict_xdxf_parsedata.cpp 2007-08-31 01:41:54.000000000 -0500 ++++ stardict-3.0.1/stardict-plugins/stardict-xdxf-parsedata-plugin/stardict_xdxf_parsedata.cpp 2010-05-24 00:53:36.384665734 -0500 +@@ -1,4 +1,5 @@ + #include "stardict_xdxf_parsedata.h" ++#include <cstring> + #include <glib/gi18n.h> + + #ifdef _WIN32 +diff -Nur stardict-3.0.1.orig//tests/t_config_file.cpp stardict-3.0.1/tests/t_config_file.cpp +--- stardict-3.0.1.orig//tests/t_config_file.cpp 2007-07-10 02:16:04.000000000 -0500 ++++ stardict-3.0.1/tests/t_config_file.cpp 2010-05-24 00:53:36.384665734 -0500 +@@ -8,6 +8,7 @@ + #include <cstring> + #include <iterator> + #include <iostream> ++#include <memory> + #include <gtk/gtk.h> + + #include "config_file.hpp" +diff -Nur stardict-3.0.1.orig//tests/t_xml.cpp stardict-3.0.1/tests/t_xml.cpp +--- stardict-3.0.1.orig//tests/t_xml.cpp 2007-07-10 02:16:04.000000000 -0500 ++++ stardict-3.0.1/tests/t_xml.cpp 2010-05-24 00:53:36.384665734 -0500 +@@ -5,6 +5,7 @@ + #include <glib.h> + #include <cstdlib> + #include <string> ++#include <cstring> + + + static void xml_decode(const char *str, std::string& decoded) |