stardict.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437
  1. /*****************************************************************************
  2. * stardict.cpp - QStarDict, a StarDict clone written using Qt *
  3. * Copyright (C) 2008 Alexander Rodin *
  4. * *
  5. * This program is free software; you can redistribute it and/or modify *
  6. * it under the terms of the GNU General Public License as published by *
  7. * the Free Software Foundation; either version 2 of the License, or *
  8. * (at your option) any later version. *
  9. * *
  10. * This program is distributed in the hope that it will be useful, *
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of *
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
  13. * GNU General Public License for more details. *
  14. * *
  15. * You should have received a copy of the GNU General Public License along *
  16. * with this program; if not, write to the Free Software Foundation, Inc., *
  17. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *
  18. *****************************************************************************/
  19. #include "stardict.h"
  20. #include <list>
  21. #include <map>
  22. #include <string>
  23. #include <utility>
  24. #include <QCoreApplication>
  25. #include <QDir>
  26. #include <QFile>
  27. #include <QSettings>
  28. #include <QStack>
  29. #include <glib.h>
  30. #include "lib.h"
  31. #include "file.hpp"
  32. #include "settingsdialog.h"
  33. #include <QDebug>
  34. namespace
  35. {
  36. void xdxf2html(QString &str);
  37. QString whereDict(const QString &name, const QStringList &dictDirs);
  38. const int MaxFuzzy = 24;
  39. class StdList: public std::list<std::string>
  40. {
  41. public:
  42. StdList()
  43. : std::list<std::string>()
  44. { }
  45. StdList(const QList<QString> &list)
  46. : std::list<std::string>()
  47. {
  48. for (QList<QString>::const_iterator i = list.begin(); i != list.end(); ++i)
  49. push_back(i->toUtf8().data());
  50. }
  51. StdList(const std::list<std::string> &list)
  52. : std::list<std::string>(list)
  53. { }
  54. QStringList toStringList() const
  55. {
  56. QStringList list;
  57. for (const_iterator i = begin(); i != end(); ++i)
  58. list << QString::fromUtf8(i->c_str());
  59. return list;
  60. }
  61. };
  62. class IfoListSetter
  63. {
  64. public:
  65. IfoListSetter(QStringList *list)
  66. : m_list(list)
  67. { }
  68. void operator ()(const std::string &filename, bool)
  69. {
  70. DictInfo info;
  71. if (info.load_from_ifo_file(filename, false))
  72. m_list->push_back(QString::fromUtf8(info.bookname.c_str()));
  73. }
  74. private:
  75. QStringList *m_list;
  76. };
  77. class IfoFileFinder
  78. {
  79. public:
  80. IfoFileFinder(const QString &name, QString *filename)
  81. : m_name(name.toUtf8().data()),
  82. m_filename(filename)
  83. { }
  84. void operator()(const std::string &filename, bool)
  85. {
  86. DictInfo info;
  87. if (info.load_from_ifo_file(filename, false) && info.bookname == m_name) {
  88. *m_filename = QString::fromUtf8(filename.c_str());
  89. }
  90. }
  91. private:
  92. std::string m_name;
  93. QString *m_filename;
  94. };
  95. }
  96. StarDict::StarDict(QObject *parent)
  97. : QObject(parent)
  98. {
  99. m_sdLibs = new Libs;
  100. QSettings settings("qstardict","qstardict");
  101. m_dictDirs = settings.value("StarDict/dictDirs", m_dictDirs).toStringList();
  102. m_reformatLists = settings.value("StarDict/reformatLists", true).toBool();
  103. m_expandAbbreviations = settings.value("StarDict/expandAbbreviations", true).toBool();
  104. if (m_dictDirs.isEmpty())
  105. {
  106. #ifdef Q_OS_UNIX
  107. m_dictDirs << "/usr/share/stardict/dic";
  108. #else
  109. m_dictDirs << QCoreApplication::applicationDirPath() + "/dic";
  110. #endif // Q_OS_UNIX
  111. m_dictDirs << QDir::homePath() + "/.stardict/dic";
  112. }
  113. }
  114. StarDict::~StarDict()
  115. {
  116. QSettings settings("qstardict","qstardict");
  117. settings.setValue("StarDict/dictDirs", m_dictDirs);
  118. settings.setValue("StarDict/reformatLists", m_reformatLists);
  119. settings.setValue("StarDict/expandAbbreviations", m_expandAbbreviations);
  120. delete m_sdLibs;
  121. }
  122. QStringList StarDict::availableDicts() const
  123. {
  124. QStringList result;
  125. IfoListSetter setter(&result);
  126. for_each_file(StdList(m_dictDirs), ".ifo", StdList(), StdList(), setter);
  127. return result;
  128. }
  129. void StarDict::setLoadedDicts(const QStringList &loadedDicts)
  130. {
  131. QStringList available = availableDicts();
  132. StdList disabled;
  133. for (QStringList::const_iterator i = available.begin(); i != available.end(); ++i)
  134. {
  135. if (! loadedDicts.contains(*i))
  136. disabled.push_back(i->toUtf8().data());
  137. }
  138. m_sdLibs->reload(StdList(m_dictDirs), StdList(loadedDicts), disabled);
  139. m_loadedDicts.clear();
  140. for (int i = 0; i < m_sdLibs->ndicts(); ++i)
  141. m_loadedDicts[QString::fromUtf8(m_sdLibs->dict_name(i).c_str())] = i;
  142. }
  143. StarDict::DictInfo StarDict::dictInfo(const QString &dict)
  144. {
  145. ::DictInfo nativeInfo;
  146. nativeInfo.wordcount = 0;
  147. if (! nativeInfo.load_from_ifo_file(whereDict(dict, m_dictDirs).toUtf8().data(), false)) {
  148. return DictInfo();
  149. }
  150. DictInfo result(name(), dict);
  151. result.setAuthor(QString::fromUtf8(nativeInfo.author.c_str()));
  152. result.setDescription(QString::fromUtf8(nativeInfo.description.c_str()));
  153. result.setWordsCount(nativeInfo.wordcount ? static_cast<long>(nativeInfo.wordcount) : -1);
  154. return result;
  155. }
  156. bool StarDict::isTranslatable(const QString &dict, const QString &word)
  157. {
  158. if (! m_loadedDicts.contains(dict))
  159. return false;
  160. long ind;
  161. return m_sdLibs->SimpleLookupWord(word.toUtf8().data(), ind, m_loadedDicts[dict]);
  162. }
  163. StarDict::Translation StarDict::translate(const QString &dict, const QString &word)
  164. {
  165. if (! m_loadedDicts.contains(dict))
  166. return Translation();
  167. if (word.isEmpty())
  168. return Translation();
  169. int dictIndex = m_loadedDicts[dict];
  170. long ind;
  171. if (! m_sdLibs->SimpleLookupWord(word.toUtf8().data(), ind, m_loadedDicts[dict]))
  172. return Translation();
  173. return Translation(QString::fromUtf8(m_sdLibs->poGetWord(ind, dictIndex)),
  174. QString::fromUtf8(m_sdLibs->dict_name(dictIndex).c_str()),
  175. parseData(m_sdLibs->poGetWordData(ind, dictIndex), dictIndex, true,
  176. m_reformatLists, m_expandAbbreviations));
  177. }
  178. QStringList StarDict::findSimilarWords(const QString &dict, const QString &word)
  179. {
  180. if (! m_loadedDicts.contains(dict))
  181. return QStringList();
  182. gchar *fuzzy_res[MaxFuzzy];
  183. if (! m_sdLibs->LookupWithFuzzy(word.toUtf8().data(), fuzzy_res, MaxFuzzy, m_loadedDicts[dict]))
  184. return QStringList();
  185. QStringList result;
  186. for (gchar **p = fuzzy_res, **end = fuzzy_res + MaxFuzzy; p != end && *p; ++p)
  187. {
  188. result << QString::fromUtf8(*p);
  189. g_free(*p);
  190. }
  191. return result;
  192. }
  193. int StarDict::execSettingsDialog(QWidget *parent)
  194. {
  195. ::SettingsDialog dialog(this, parent);
  196. return dialog.exec();
  197. }
  198. QString StarDict::parseData(const char *data, int dictIndex, bool htmlSpaces, bool reformatLists, bool expandAbbreviations)
  199. {
  200. QString result;
  201. quint32 dataSize = *reinterpret_cast<const quint32*>(data);
  202. const char *dataEnd = data + dataSize;
  203. const char *ptr = data + sizeof(quint32);
  204. while (ptr < dataEnd)
  205. {
  206. switch (*ptr++)
  207. {
  208. case 'm':
  209. case 'l':
  210. case 'g':
  211. {
  212. QString str = QString::fromUtf8(ptr);
  213. ptr += str.toUtf8().length() + 1;
  214. result += str;
  215. break;
  216. }
  217. case 'x':
  218. {
  219. QString str = QString::fromUtf8(ptr);
  220. ptr += str.toUtf8().length() + 1;
  221. xdxf2html(str);
  222. result += str;
  223. break;
  224. }
  225. case 't':
  226. {
  227. QString str = QString::fromUtf8(ptr);
  228. ptr += str.toUtf8().length() + 1;
  229. result += "<font class=\"example\">";
  230. result += str;
  231. result += "</font>";
  232. break;
  233. }
  234. case 'y':
  235. {
  236. ptr += strlen(ptr) + 1;
  237. break;
  238. }
  239. case 'W':
  240. case 'P':
  241. {
  242. ptr += *reinterpret_cast<const quint32*>(ptr) + sizeof(quint32);
  243. break;
  244. }
  245. default:
  246. ; // nothing
  247. }
  248. }
  249. if (expandAbbreviations)
  250. {
  251. QRegExp regExp("_\\S+[\\.:]");
  252. int pos = 0;
  253. while ((pos = regExp.indexIn(result, pos)) != -1)
  254. {
  255. long ind;
  256. if (m_sdLibs->SimpleLookupWord(result.mid(pos, regExp.matchedLength()).toUtf8().data(), ind, dictIndex))
  257. {
  258. QString expanded = "<font class=\"explanation\">";
  259. expanded += parseData(m_sdLibs->poGetWordData(ind, dictIndex));
  260. if (result[pos + regExp.matchedLength() - 1] == ':')
  261. expanded += ':';
  262. expanded += "</font>";
  263. result.replace(pos, regExp.matchedLength(), expanded);
  264. pos += expanded.length();
  265. }
  266. else
  267. pos += regExp.matchedLength();
  268. }
  269. }
  270. if (reformatLists)
  271. {
  272. int pos = 0;
  273. QStack<QChar> openedLists;
  274. while (pos < result.length())
  275. {
  276. if (result[pos].isDigit())
  277. {
  278. int n = 0;
  279. while (result[pos + n].isDigit())
  280. ++n;
  281. pos += n;
  282. if (result[pos] == '&' && result.mid(pos + 1, 3) == "gt;")
  283. result.replace(pos, 4, ">");
  284. QChar marker = result[pos];
  285. QString replacement;
  286. if (marker == '>' || marker == '.' || marker == ')')
  287. {
  288. if (n == 1 && result[pos - 1] == '1') // open new list
  289. {
  290. if (openedLists.contains(marker))
  291. {
  292. replacement = "</li></ol>";
  293. while (openedLists.size() && openedLists.top() != marker)
  294. {
  295. replacement += "</li></ol>";
  296. openedLists.pop();
  297. }
  298. }
  299. openedLists.push(marker);
  300. replacement += "<ol>";
  301. }
  302. else
  303. {
  304. while (openedLists.size() && openedLists.top() != marker)
  305. {
  306. replacement += "</li></ol>";
  307. openedLists.pop();
  308. }
  309. replacement += "</li>";
  310. }
  311. replacement += "<li>";
  312. pos -= n;
  313. n += pos;
  314. while (result[pos - 1].isSpace())
  315. --pos;
  316. while (result[n + 1].isSpace())
  317. ++n;
  318. result.replace(pos, n - pos + 1, replacement);
  319. pos += replacement.length();
  320. }
  321. else
  322. ++pos;
  323. }
  324. else
  325. ++pos;
  326. }
  327. while (openedLists.size())
  328. {
  329. result += "</li></ol>";
  330. openedLists.pop();
  331. }
  332. }
  333. if (htmlSpaces)
  334. {
  335. int n = 0;
  336. while (result[n].isSpace())
  337. ++n;
  338. result.remove(0, n);
  339. n = 0;
  340. while (result[result.length() - 1 - n].isSpace())
  341. ++n;
  342. result.remove(result.length() - n, n);
  343. for (int pos = 0; pos < result.length();)
  344. {
  345. switch (result[pos].toLatin1())
  346. {
  347. case '[':
  348. result.insert(pos, "<font class=\"transcription\">");
  349. pos += 28 + 1; // sizeof "<font class=\"transcription\">" + 1
  350. break;
  351. case ']':
  352. result.insert(pos + 1, "</font>");
  353. pos += 7 + 1; // sizeof "</font>" + 1
  354. break;
  355. case '\t':
  356. result.insert(pos, "&nbsp;&nbsp;&nbsp;&nbsp;");
  357. pos += 24 + 1; // sizeof "&nbsp;&nbsp;&nbsp;&nbsp;" + 1
  358. break;
  359. case '\n':
  360. {
  361. int count = 1;
  362. n = 1;
  363. while (result[pos + n].isSpace())
  364. {
  365. if (result[pos + n] == '\n')
  366. ++count;
  367. ++n;
  368. }
  369. if (count > 1)
  370. result.replace(pos, n, "</p><p>");
  371. else
  372. result.replace(pos, n, "<br>");
  373. break;
  374. }
  375. default:
  376. ++pos;
  377. }
  378. }
  379. }
  380. return result;
  381. }
  382. namespace
  383. {
  384. QString whereDict(const QString &name, const QStringList &dictDirs)
  385. {
  386. QString filename;
  387. IfoFileFinder finder(name, &filename);
  388. for_each_file(StdList(dictDirs), ".ifo", StdList(), StdList(), finder);
  389. return filename;
  390. }
  391. void xdxf2html(QString &str)
  392. {
  393. str.replace("<abr>", "<font class=\"abbreviature\">");
  394. str.replace("<tr>", "<font class=\"transcription\">[");
  395. str.replace("</tr>", "]</font>");
  396. str.replace("<ex>", "<font class=\"example\">");
  397. str.replace(QRegExp("<k>.*<\\/k>"), "");
  398. str.replace(QRegExp("(<\\/abr>)|(<\\ex>)"), "</font");
  399. }
  400. }
  401. #if QT_VERSION < 0x050000
  402. Q_EXPORT_PLUGIN2(stardict, StarDict)
  403. #endif
  404. // vim: tabstop=4 softtabstop=4 shiftwidth=4 expandtab cindent textwidth=120 formatoptions=tc