KOffice – TDE office suite
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

818 lines
28 KiB

  1. /*
  2. $Id: main.cc 466447 2005-10-02 17:54:10Z zander $
  3. This file is part of the KDE project
  4. Copyright (C) 2001,2002,2003 Daniel Naber <daniel.naber@t-online.de>
  5. This is a thesaurus based on a subset of WordNet. It also offers an
  6. almost complete WordNet 1.7 frontend (WordNet is a powerful lexical
  7. database/thesaurus)
  8. */
  9. /***************************************************************************
  10. This program is free software; you can redistribute it and/or
  11. modify it under the terms of the GNU General Public License
  12. as published by the Free Software Foundation; either version 2
  13. of the License, or (at your option) any later version.
  14. This program is distributed in the hope that it will be useful,
  15. but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. GNU General Public License for more details.
  18. You should have received a copy of the GNU General Public License
  19. along with this program; if not, write to the Free Software
  20. Foundation, Inc., 51 Franklin Street, Fifth Floor,
  21. * Boston, MA 02110-1301, USA.
  22. ***************************************************************************/
  23. /*
  24. TODO:
  25. -Be more verbose if the result is empty
  26. -See the TODO's in the source below
  27. -If no match was found, use KSpell to offer alternative spellings?
  28. -Don't start WordNet before its tab is activated?
  29. -Maybe remove more uncommon words. However, the "polysemy/familiarity
  30. count" is sometimes very low for quite common word, e.g. "sky".
  31. -Fix "no mimesource" warning of TQTextBrowser? Seems really harmless.
  32. NOT TODO:
  33. -Add part of speech information -- I think this would blow up the
  34. filesize too much
  35. */
  36. #include "main.h"
  37. #include <tqfile.h>
  38. #include <tqtoolbutton.h>
  39. #include <kiconloader.h>
  40. #include <tdefiledialog.h>
  41. #include <tdeversion.h>
  42. /***************************************************
  43. *
  44. * Factory
  45. *
  46. ***************************************************/
  47. typedef KGenericFactory<Thesaurus, KDataTool> ThesaurusFactory;
  48. K_EXPORT_COMPONENT_FACTORY( libthesaurustool, ThesaurusFactory("thesaurus_tool") )
  49. /***************************************************
  50. *
  51. * Thesaurus *
  52. ***************************************************/
  53. Thesaurus::Thesaurus(TQObject* parent, const char* name, const TQStringList &)
  54. : KDataTool(parent, name)
  55. {
  56. m_dialog = new KDialogBase(KJanusWidget::Plain, TQString(),
  57. KDialogBase::Help|KDialogBase::Ok|KDialogBase::Cancel, KDialogBase::Ok);
  58. m_dialog->setHelp(TQString(), "thesaurus");
  59. m_dialog->resize(600, 400);
  60. m_config = new TDEConfig("kthesaurusrc");
  61. m_data_file = m_config->readPathEntry("datafile");
  62. if( ! m_data_file ) {
  63. m_data_file = TDEGlobal::dirs()->findResourceDir("data", "thesaurus/")
  64. + "thesaurus/thesaurus.txt";
  65. }
  66. setCaption();
  67. m_no_match = i18n("(No match)");
  68. m_replacement = false;
  69. m_history_pos = 1;
  70. m_page = m_dialog->plainPage();
  71. TQVBoxLayout *m_top_layout = new TQVBoxLayout(m_page, KDialog::marginHint(), KDialog::spacingHint());
  72. TQHBoxLayout *row1 = new TQHBoxLayout(m_top_layout);
  73. m_edit = new KHistoryCombo(m_page);
  74. m_edit_label = new TQLabel(m_edit, i18n("&Search for:"), m_page);
  75. m_search = new KPushButton(i18n("S&earch"), m_page);
  76. connect(m_search, TQT_SIGNAL(clicked()),
  77. this, TQT_SLOT(slotFindTerm()));
  78. row1->addWidget(m_edit_label, 0);
  79. row1->addWidget(m_edit, 1);
  80. row1->addWidget(m_search, 0);
  81. m_back = new TQToolButton(m_page);
  82. m_back->setIconSet(BarIconSet(TQString::fromLatin1("back")));
  83. TQToolTip::add(m_back, i18n("Back"));
  84. row1->addWidget(m_back, 0);
  85. m_forward = new TQToolButton(m_page);
  86. m_forward->setIconSet(BarIconSet(TQString::fromLatin1("forward")));
  87. TQToolTip::add(m_forward, i18n("Forward"));
  88. row1->addWidget(m_forward, 0);
  89. m_lang = new KPushButton(i18n("Change Language..."), m_page);
  90. connect(m_lang, TQT_SIGNAL(clicked()), this, TQT_SLOT(slotChangeLanguage()));
  91. row1->addWidget(m_lang, 0);
  92. connect(m_back, TQT_SIGNAL(clicked()), this, TQT_SLOT(slotBack()));
  93. connect(m_forward, TQT_SIGNAL(clicked()), this, TQT_SLOT(slotForward()));
  94. m_tab = new TQTabWidget(m_page);
  95. m_top_layout->addWidget(m_tab);
  96. //
  97. // Thesaurus Tab
  98. //
  99. vbox = new TQVBox(m_tab);
  100. m_tab->addTab(vbox, i18n("&Thesaurus"));
  101. vbox->setMargin(KDialog::marginHint());
  102. vbox->setSpacing(KDialog::spacingHint());
  103. TQHBox *hbox = new TQHBox(vbox);
  104. hbox->setSpacing(KDialog::spacingHint());
  105. grpbox_syn = new TQGroupBox( 1, Qt::Horizontal, i18n("Synonyms"), hbox);
  106. m_thes_syn = new TQListBox(grpbox_syn);
  107. grpbox_hyper = new TQGroupBox( 1, Qt::Horizontal, i18n("More General Words"), hbox);
  108. m_thes_hyper = new TQListBox(grpbox_hyper);
  109. grpbox_hypo = new TQGroupBox( 1, Qt::Horizontal, i18n("More Specific Words"), hbox);
  110. m_thes_hypo = new TQListBox(grpbox_hypo);
  111. // single click -- keep display unambiguous by removing other selections:
  112. connect(m_thes_syn, TQT_SIGNAL(clicked(TQListBoxItem *)), m_thes_hyper, TQT_SLOT(clearSelection()));
  113. connect(m_thes_syn, TQT_SIGNAL(clicked(TQListBoxItem *)), m_thes_hypo, TQT_SLOT(clearSelection()));
  114. connect(m_thes_syn, TQT_SIGNAL(selectionChanged(TQListBoxItem *)),
  115. this, TQT_SLOT(slotSetReplaceTerm(TQListBoxItem *)));
  116. connect(m_thes_hyper, TQT_SIGNAL(clicked(TQListBoxItem *)), m_thes_syn, TQT_SLOT(clearSelection()));
  117. connect(m_thes_hyper, TQT_SIGNAL(clicked(TQListBoxItem *)), m_thes_hypo, TQT_SLOT(clearSelection()));
  118. connect(m_thes_hyper, TQT_SIGNAL(selectionChanged(TQListBoxItem *)),
  119. this, TQT_SLOT(slotSetReplaceTerm(TQListBoxItem *)));
  120. connect(m_thes_hypo, TQT_SIGNAL(clicked(TQListBoxItem *)), m_thes_syn, TQT_SLOT(clearSelection()));
  121. connect(m_thes_hypo, TQT_SIGNAL(clicked(TQListBoxItem *)), m_thes_hyper, TQT_SLOT(clearSelection()));
  122. connect(m_thes_hypo, TQT_SIGNAL(selectionChanged(TQListBoxItem *)),
  123. this, TQT_SLOT(slotSetReplaceTerm(TQListBoxItem *)));
  124. // double click:
  125. connect(m_thes_syn, TQT_SIGNAL(selected(const TQString &)),
  126. this, TQT_SLOT(slotFindTerm(const TQString &)));
  127. connect(m_thes_hyper, TQT_SIGNAL(selected(const TQString &)),
  128. this, TQT_SLOT(slotFindTerm(const TQString &)));
  129. connect(m_thes_hypo, TQT_SIGNAL(selected(const TQString &)),
  130. this, TQT_SLOT(slotFindTerm(const TQString &)));
  131. //
  132. // WordNet Tab
  133. //
  134. vbox2 = new TQVBox(m_tab);
  135. m_tab->addTab(vbox2, i18n("&WordNet"));
  136. vbox2->setMargin(KDialog::marginHint());
  137. vbox2->setSpacing(KDialog::spacingHint());
  138. m_combobox = new TQComboBox(vbox2);
  139. m_combobox->setEditable(false);
  140. connect(m_combobox, TQT_SIGNAL(activated(int)), this, TQT_SLOT(slotFindTerm()));
  141. m_resultbox = new TQTextBrowser(vbox2);
  142. m_resultbox->setTextFormat(TQt::RichText);
  143. // TODO?: m_resultbox->setMimeSourceFactory(...); to avoid warning
  144. connect(m_resultbox, TQT_SIGNAL(linkClicked(const TQString &)),
  145. this, TQT_SLOT(slotFindTerm(const TQString &)));
  146. // Connect for the history box
  147. m_edit->setTrapReturnKey(true); // Do not use Return as default key...
  148. connect(m_edit, TQT_SIGNAL(returnPressed(const TQString&)), this, TQT_SLOT(slotFindTerm(const TQString&)));
  149. connect(m_edit, TQT_SIGNAL(activated(int)), this, TQT_SLOT(slotGotoHistory(int)));
  150. TQHBoxLayout *row2 = new TQHBoxLayout( m_top_layout );
  151. m_replace = new KLineEdit(m_page);
  152. m_replace_label = new TQLabel(m_replace, i18n("&Replace with:"), m_page);
  153. row2->addWidget(m_replace_label, 0);
  154. row2->addWidget(m_replace, 1);
  155. // Set focus
  156. m_edit->setFocus();
  157. slotUpdateNavButtons();
  158. //
  159. // The external command stuff
  160. //
  161. // calling the 'wn' binary
  162. m_wnproc = new TDEProcess;
  163. connect(m_wnproc, TQT_SIGNAL(processExited(TDEProcess*)), this, TQT_SLOT(wnExited(TDEProcess*)));
  164. connect(m_wnproc, TQT_SIGNAL(receivedStdout(TDEProcess*,char*,int)),
  165. this, TQT_SLOT(receivedWnStdout(TDEProcess*, char*, int)));
  166. connect(m_wnproc, TQT_SIGNAL(receivedStderr(TDEProcess*,char*,int)),
  167. this, TQT_SLOT(receivedWnStderr(TDEProcess*, char*, int)));
  168. // grep'ing the text file
  169. m_thesproc = new TDEProcess;
  170. connect(m_thesproc, TQT_SIGNAL(processExited(TDEProcess*)), this, TQT_SLOT(thesExited(TDEProcess*)));
  171. connect(m_thesproc, TQT_SIGNAL(receivedStdout(TDEProcess*,char*,int)),
  172. this, TQT_SLOT(receivedThesStdout(TDEProcess*, char*, int)));
  173. connect(m_thesproc, TQT_SIGNAL(receivedStderr(TDEProcess*,char*,int)),
  174. this, TQT_SLOT(receivedThesStderr(TDEProcess*, char*, int)));
  175. }
  176. Thesaurus::~Thesaurus()
  177. {
  178. m_config->writePathEntry("datafile", m_data_file);
  179. m_config->sync();
  180. delete m_config;
  181. // FIXME?: this hopefully fixes the problem of a wrong cursor
  182. // and a crash (when closing e.g. konqueror) when the thesaurus dialog
  183. // gets close while it was still working and showing the wait cursor
  184. TQApplication::restoreOverrideCursor();
  185. delete m_thesproc;
  186. delete m_wnproc;
  187. delete m_dialog;
  188. }
  189. bool Thesaurus::run(const TQString& command, void* data, const TQString& datatype, const TQString& mimetype)
  190. {
  191. // Check whether we can accept the data
  192. if ( datatype != TQSTRING_OBJECT_NAME_STRING ) {
  193. kdDebug(31000) << "Thesaurus only accepts datatype TQString" << endl;
  194. return FALSE;
  195. }
  196. if ( mimetype != "text/plain" ) {
  197. kdDebug(31000) << "Thesaurus only accepts mimetype text/plain" << endl;
  198. return FALSE;
  199. }
  200. if ( command == "thesaurus" ) {
  201. // not called from an application like KWord, so make it possible
  202. // to replace text:
  203. m_replacement = true;
  204. m_dialog->setButtonOKText(i18n("&Replace"));
  205. } else if ( command == "thesaurus_standalone" ) {
  206. // not called from any application, but from KThesaurus
  207. m_replacement = false;
  208. m_dialog->showButtonOK(false);
  209. m_dialog->setButtonCancelText(i18n("&Close"));
  210. m_replace->setEnabled(false);
  211. m_replace_label->setEnabled(false);
  212. } else {
  213. kdDebug(31000) << "Thesaurus does only accept the command 'thesaurus' or 'thesaurus_standalone'" << endl;
  214. kdDebug(31000) << "The command " << command << " is not accepted" << endl;
  215. return FALSE;
  216. }
  217. // Get data and clean it up:
  218. TQString buffer = *((TQString *)data);
  219. buffer = buffer.stripWhiteSpace();
  220. TQRegExp re("[.,;!?\"'()\\[\\]]");
  221. buffer.remove(re);
  222. buffer = buffer.left(100); // limit maximum length
  223. m_wnproc_stdout = "";
  224. m_wnproc_stderr = "";
  225. m_thesproc_stdout = "";
  226. m_thesproc_stderr = "";
  227. if( ! buffer.isEmpty() ) {
  228. slotFindTerm(buffer);
  229. }
  230. if( m_dialog->exec() == TQDialog::Accepted ) { // "Replace"
  231. *((TQString*)data) = m_replace->text();
  232. }
  233. return TRUE;
  234. }
  235. void Thesaurus::slotChangeLanguage()
  236. {
  237. TQString filename = KFileDialog::getOpenFileName(
  238. TDEGlobal::dirs()->findResourceDir("data", "thesaurus/")+"thesaurus/");
  239. if( !filename.isNull() ) {
  240. m_data_file = filename;
  241. setCaption();
  242. }
  243. }
  244. void Thesaurus::setCaption()
  245. {
  246. KURL url = KURL();
  247. url.setPath(m_data_file);
  248. m_dialog->setCaption(i18n("Related Words - %1").arg(url.fileName()));
  249. }
  250. // Enbale or disable back and forward button
  251. void Thesaurus::slotUpdateNavButtons()
  252. {
  253. if( m_history_pos <= 1 ) { // 1 = first position
  254. m_back->setEnabled(false);
  255. } else {
  256. m_back->setEnabled(true);
  257. }
  258. if( m_history_pos >= m_edit->count() ) {
  259. m_forward->setEnabled(false);
  260. } else {
  261. m_forward->setEnabled(true);
  262. }
  263. }
  264. // Go to an item from the editbale combo box.
  265. void Thesaurus::slotGotoHistory(int index)
  266. {
  267. m_history_pos = m_edit->count() - index;
  268. slotFindTerm(m_edit->text(index), false);
  269. }
  270. // Triggered when the back button is clicked.
  271. void Thesaurus::slotBack()
  272. {
  273. m_history_pos--;
  274. int pos = m_edit->count() - m_history_pos;
  275. m_edit->setCurrentItem(pos);
  276. slotFindTerm(m_edit->text(pos), false);
  277. }
  278. // Triggered when the forward button is clicked.
  279. void Thesaurus::slotForward()
  280. {
  281. m_history_pos++;
  282. int pos = m_edit->count() - m_history_pos;
  283. m_edit->setCurrentItem(pos);
  284. slotFindTerm(m_edit->text(pos), false);
  285. }
  286. // Triggered when a word is selected in the list box.
  287. void Thesaurus::slotSetReplaceTerm(TQListBoxItem *item)
  288. {
  289. if( ! item )
  290. return;
  291. m_replace->setText(item->text());
  292. }
  293. void Thesaurus::slotSetReplaceTerm(const TQString &term)
  294. {
  295. if( m_replacement && term != m_no_match ) {
  296. m_replace->setText(term);
  297. }
  298. }
  299. // Triggered when Return is pressed.
  300. void Thesaurus::slotFindTerm()
  301. {
  302. findTerm(m_edit->currentText());
  303. }
  304. // Triggered when a word is clicked / a list item is double-clicked.
  305. void Thesaurus::slotFindTerm(const TQString &term, bool add_to_history)
  306. {
  307. slotSetReplaceTerm(term);
  308. if( term.startsWith("http://") ) {
  309. (void) new KRun(KURL(term));
  310. } else {
  311. if( add_to_history ) {
  312. m_edit->insertItem(term, 0);
  313. m_history_pos = m_edit->count();
  314. m_edit->setCurrentItem(0);
  315. }
  316. slotUpdateNavButtons();
  317. findTerm(term);
  318. }
  319. }
  320. void Thesaurus::findTerm(const TQString &term)
  321. {
  322. findTermThesaurus(term);
  323. findTermWordnet(term);
  324. }
  325. //
  326. // Thesaurus
  327. //
  328. void Thesaurus::findTermThesaurus(const TQString &term)
  329. {
  330. if( !TQFile::exists(m_data_file) ) {
  331. KMessageBox::error(0, i18n("The thesaurus file '%1' was not found. "
  332. "Please use 'Change Language...' to select a thesaurus file.").
  333. arg(m_data_file));
  334. return;
  335. }
  336. TQApplication::setOverrideCursor(KCursor::waitCursor());
  337. m_thesproc_stdout = "";
  338. m_thesproc_stderr = "";
  339. // Find only whole words. Looks clumsy, but this way we don't have to rely on
  340. // features that might only be in certain versions of grep:
  341. TQString term_tmp = ";" + term.stripWhiteSpace() + ";";
  342. m_thesproc->clearArguments();
  343. *m_thesproc << "grep" << "-i" << term_tmp;
  344. *m_thesproc << m_data_file;
  345. if( !m_thesproc->start(TDEProcess::NotifyOnExit, TDEProcess::AllOutput) ) {
  346. KMessageBox::error(0, i18n("Failed to execute grep."));
  347. TQApplication::restoreOverrideCursor();
  348. return;
  349. }
  350. }
  351. // The external process has ended, so we parse its result and put it in
  352. // the list box.
  353. void Thesaurus::thesExited(TDEProcess *)
  354. {
  355. if( !m_thesproc_stderr.isEmpty() ) {
  356. KMessageBox::error(0, i18n("<b>Error:</b> Failed to execute grep. "
  357. "Output:<br>%1").arg(m_thesproc_stderr));
  358. TQApplication::restoreOverrideCursor();
  359. return;
  360. }
  361. TQString search_term = m_edit->currentText().stripWhiteSpace();
  362. TQStringList syn;
  363. TQStringList hyper;
  364. TQStringList hypo;
  365. TQStringList lines = lines.split(TQChar('\n'), m_thesproc_stdout, false);
  366. for ( TQStringList::Iterator it = lines.begin(); it != lines.end(); ++it ) {
  367. TQString line = (*it);
  368. if( line.startsWith(" ") ) { // ignore license (two spaces)
  369. continue;
  370. }
  371. int sep_pos = line.find("#");
  372. TQString syn_part = line.left(sep_pos);
  373. TQString hyper_part = line.right(line.length()-sep_pos-1);
  374. TQStringList syn_tmp = TQStringList::split(TQChar(';'), syn_part);
  375. TQStringList hyper_tmp = TQStringList::split(TQChar(';'), hyper_part);
  376. if( syn_tmp.grep(search_term, false).size() > 0 ) {
  377. // match on the left side of the '#' -- synonyms
  378. for ( TQStringList::Iterator it2 = syn_tmp.begin(); it2 != syn_tmp.end(); ++it2 ) {
  379. // add if it's not the term itself and if it's not yet in the list
  380. TQString term = (*it2);
  381. if( term.lower() != search_term.lower() && syn.contains(term) == 0 ) {
  382. syn.append(term);
  383. }
  384. }
  385. for ( TQStringList::Iterator it2 = hyper_tmp.begin(); it2 != hyper_tmp.end(); ++it2 ) {
  386. TQString term = (*it2);
  387. if( term.lower() != search_term.lower() && hyper.contains(term) == 0 ) {
  388. hyper.append(term);
  389. }
  390. }
  391. }
  392. if( hyper_tmp.grep(search_term, false).size() > 0 ) {
  393. // match on the right side of the '#' -- hypernyms
  394. for ( TQStringList::Iterator it2 = syn_tmp.begin(); it2 != syn_tmp.end(); ++it2 ) {
  395. TQString term = (*it2);
  396. if( term.lower() != search_term && hypo.contains(term) == 0 ) {
  397. hypo.append(term);
  398. }
  399. }
  400. }
  401. }
  402. m_thes_syn->clear();
  403. if( syn.size() > 0 ) {
  404. syn = sortTQStringList(syn);
  405. m_thes_syn->insertStringList(syn);
  406. m_thes_syn->setEnabled(true);
  407. } else {
  408. m_thes_syn->insertItem(m_no_match);
  409. m_thes_syn->setEnabled(false);
  410. }
  411. m_thes_hyper->clear();
  412. if( hyper.size() > 0 ) {
  413. hyper = sortTQStringList(hyper);
  414. m_thes_hyper->insertStringList(hyper);
  415. m_thes_hyper->setEnabled(true);
  416. } else {
  417. m_thes_hyper->insertItem(m_no_match);
  418. m_thes_hyper->setEnabled(false);
  419. }
  420. m_thes_hypo->clear();
  421. if( hypo.size() > 0 ) {
  422. hypo = sortTQStringList(hypo);
  423. m_thes_hypo->insertStringList(hypo);
  424. m_thes_hypo->setEnabled(true);
  425. } else {
  426. m_thes_hypo->insertItem(m_no_match);
  427. m_thes_hypo->setEnabled(false);
  428. }
  429. TQApplication::restoreOverrideCursor();
  430. }
  431. void Thesaurus::receivedThesStdout(TDEProcess *, char *result, int len)
  432. {
  433. m_thesproc_stdout += TQString::fromLocal8Bit( TQCString(result, len+1) );
  434. }
  435. void Thesaurus::receivedThesStderr(TDEProcess *, char *result, int len)
  436. {
  437. m_thesproc_stderr += TQString::fromLocal8Bit( TQCString(result, len+1) );
  438. }
  439. //
  440. // WordNet
  441. //
  442. void Thesaurus::findTermWordnet(const TQString &term)
  443. {
  444. TQApplication::setOverrideCursor(KCursor::waitCursor());
  445. m_wnproc_stdout = "";
  446. m_wnproc_stderr = "";
  447. m_wnproc->clearArguments();
  448. *m_wnproc << "wn";
  449. *m_wnproc << term;
  450. // get all results: nouns, verbs, adjectives, adverbs (see below for order):
  451. if( m_combobox->currentItem() == 0 ) {
  452. *m_wnproc << "-synsn" << "-synsv" << "-synsa" << "-synsr";
  453. m_mode = other;
  454. } else if( m_combobox->currentItem() == 1 ) {
  455. *m_wnproc << "-simsv";
  456. m_mode = other;
  457. } else if( m_combobox->currentItem() == 2 ) {
  458. *m_wnproc << "-antsn" << "-antsv" << "-antsa" << "-antsr";
  459. m_mode = other;
  460. } else if( m_combobox->currentItem() == 3 ) {
  461. *m_wnproc << "-hypon" << "-hypov";
  462. m_mode = other;
  463. } else if( m_combobox->currentItem() == 4 ) {
  464. *m_wnproc << "-meron";
  465. m_mode = other;
  466. } else if( m_combobox->currentItem() == 5 ) {
  467. *m_wnproc << "-holon";
  468. m_mode = other;
  469. } else if( m_combobox->currentItem() == 6 ) {
  470. // e.g. "size -> large/small"
  471. *m_wnproc << "-attrn" << "-attra";
  472. m_mode = other;
  473. } else if( m_combobox->currentItem() == 7 ) {
  474. // e.g. "kill -> die"
  475. *m_wnproc << "-causv";
  476. m_mode = other;
  477. } else if( m_combobox->currentItem() == 8 ) {
  478. // e.g. "walk -> step"
  479. *m_wnproc << "-entav";
  480. m_mode = other;
  481. } else if( m_combobox->currentItem() == 9 ) {
  482. *m_wnproc << "-famln" << "-famlv" << "-famla" << "-famlr";
  483. m_mode = other;
  484. } else if( m_combobox->currentItem() == 10 ) {
  485. *m_wnproc << "-framv";
  486. m_mode = other;
  487. } else if( m_combobox->currentItem() == 11 ) {
  488. *m_wnproc << "-grepn" << "-grepv" << "-grepa" << "-grepr";
  489. m_mode = grep;
  490. } else if( m_combobox->currentItem() == 12 ) {
  491. *m_wnproc << "-over";
  492. m_mode = other;
  493. }
  494. *m_wnproc << "-g"; // "Display gloss"
  495. int current = m_combobox->currentItem(); // remember current position
  496. m_combobox->clear();
  497. // warning: order matters!
  498. // 0:
  499. m_combobox->insertItem(i18n("Synonyms/Hypernyms - Ordered by Frequency"));
  500. m_combobox->insertItem(i18n("Synonyms - Ordered by Similarity of Meaning (verbs only)"));
  501. m_combobox->insertItem(i18n("Antonyms - Words with Opposite Meanings"));
  502. m_combobox->insertItem(i18n("Hyponyms - ... is a (kind of) %1").arg(m_edit->currentText()));
  503. m_combobox->insertItem(i18n("Meronyms - %1 has a ...").arg(m_edit->currentText()));
  504. // 5:
  505. m_combobox->insertItem(i18n("Holonyms - ... has a %1").arg(m_edit->currentText()));
  506. m_combobox->insertItem(i18n("Attributes"));
  507. m_combobox->insertItem(i18n("Cause To (for some verbs only)"));
  508. m_combobox->insertItem(i18n("Verb Entailment (for some verbs only)"));
  509. m_combobox->insertItem(i18n("Familiarity & Polysemy Count"));
  510. // 10:
  511. m_combobox->insertItem(i18n("Verb Frames (examples of use)"));
  512. m_combobox->insertItem(i18n("List of Compound Words"));
  513. m_combobox->insertItem(i18n("Overview of Senses"));
  514. /** NOT todo:
  515. * -Hypernym tree: layout is difficult, you can get the same information
  516. * by following links
  517. * -Coordinate terms (sisters): just go to synset and then use hyponyms
  518. * -Has Part Meronyms, Has Substance Meronyms, Has Member Meronyms,
  519. * Member of Holonyms, Substance of Holonyms, Part of Holonyms:
  520. * these are just subsets of Meronyms/Holonyms
  521. * -hmern, hholn: these are just compact versions, you can get the
  522. * same information by following some links
  523. */
  524. /** TODO?:
  525. * -pert (e.g. nuclear -> nuclues, but "=>" are nested, difficult to display)
  526. * -nomn(n|v), e.g. deny -> denial, but this doesn't seem to work?
  527. */
  528. m_combobox->setCurrentItem(current); // reset previous position
  529. if( m_wnproc->isRunning() ) {
  530. // should never happen
  531. kdDebug(31000) << "Warning: findTerm(): process is already running?!" << endl;
  532. TQApplication::restoreOverrideCursor();
  533. return;
  534. }
  535. if( !m_wnproc->start(TDEProcess::NotifyOnExit, TDEProcess::AllOutput) ) {
  536. m_resultbox->setText(i18n("<b>Error:</b> Failed to execute the WordNet program 'wn'. "
  537. "WordNet has to be installed on your computer to use this component of kthesaurus, "
  538. "and 'wn' has to be available in your $PATH environment variable. "
  539. "Look for a WordNet package from your upstream distro package repository. "
  540. "Or you can obtain WordNet at <a href=\"http://wordnet.princeton.edu/wordnet/download/current-version/\">"
  541. "http://wordnet.princeton.edu/wordnet/download/current-version/</a>. "
  542. "Notice that WordNet only supports the English language."));
  543. m_combobox->setEnabled(false);
  544. TQApplication::restoreOverrideCursor();
  545. return;
  546. }
  547. }
  548. // The process has ended, so parse its result and display it as TQt richtext.
  549. void Thesaurus::wnExited(TDEProcess *)
  550. {
  551. if( !m_wnproc_stderr.isEmpty() ) {
  552. m_resultbox->setText(i18n("<b>Error:</b> Failed to execute WordNet program 'wn'. "
  553. "Output:<br>%1").arg(m_wnproc_stderr));
  554. TQApplication::restoreOverrideCursor();
  555. return;
  556. }
  557. if( m_wnproc_stdout.isEmpty() ) {
  558. m_resultbox->setText(i18n("No match for '%1'.").arg(m_edit->currentText()));
  559. } else {
  560. // render in a table, each line one row:
  561. TQStringList lines = lines.split(TQChar('\n'), m_wnproc_stdout, false);
  562. TQString result = "<qt><table>\n";
  563. // TODO in TQt > 3.01: try without the following line (it's necessary to ensure the
  564. // first column is really always quite small):
  565. result += "<tr><td width=\"10%\"></td><td width=\"90%\"></td></tr>\n";
  566. uint ct = 0;
  567. for ( TQStringList::Iterator it = lines.begin(); it != lines.end(); ++it ) {
  568. TQString l = (*it);
  569. // Remove some lines:
  570. TQRegExp re("^\\d+( of \\d+)? senses? of \\w+");
  571. if( re.search(l) != -1 ) {
  572. continue;
  573. }
  574. // Escape XML:
  575. l = l.replace('&', "&amp;");
  576. l = l.replace('<', "&lt;");
  577. l = l.replace('>', "&gt;");
  578. // TODO?:
  579. // move "=>" in own column?
  580. l = formatLine(l);
  581. // Table layout:
  582. result += "<tr>";
  583. if( l.startsWith(" ") ) {
  584. result += "\t<td width=\"15\"></td>";
  585. l = l.stripWhiteSpace();
  586. result += "\t<td>" + l + "</td>";
  587. } else {
  588. l = l.stripWhiteSpace();
  589. result += "<td colspan=\"2\">" + l + "</td>";
  590. }
  591. result += "</tr>\n";
  592. ct++;
  593. }
  594. result += "\n</table></qt>\n";
  595. m_resultbox->setText(result);
  596. m_resultbox->setContentsPos(0,0);
  597. //kdDebug() << result << endl;
  598. }
  599. TQApplication::restoreOverrideCursor();
  600. }
  601. void Thesaurus::receivedWnStdout(TDEProcess *, char *result, int len)
  602. {
  603. m_wnproc_stdout += TQString::fromLocal8Bit( TQCString(result, len+1) );
  604. }
  605. void Thesaurus::receivedWnStderr(TDEProcess *, char *result, int len)
  606. {
  607. m_wnproc_stderr += TQString::fromLocal8Bit( TQCString(result, len+1) );
  608. }
  609. //
  610. // Tools
  611. //
  612. // Format lines using TQt's simple richtext.
  613. TQString Thesaurus::formatLine(TQString l)
  614. {
  615. if( l == "--------------" ) {
  616. return TQString("<hr>");
  617. }
  618. TQRegExp re;
  619. re.setPattern("^(\\d+\\.)(.*)$");
  620. if( re.search(l) != -1 ) {
  621. l = "<b>" +re.cap(1)+ "</b>" +re.cap(2);
  622. return l;
  623. }
  624. re.setPattern("^.* of (noun|verb|adj|adv) .*");
  625. if( re.search(l) != -1 ) {
  626. l = "<font size=\"5\">" +re.cap()+ "</font>\n\n";
  627. return l;
  628. }
  629. if( m_mode == grep ) {
  630. l = l.stripWhiteSpace();
  631. return TQString("<a href=\"" +l+ "\">" +l+ "</a>");
  632. }
  633. re.setPattern("^(Sense \\d+)");
  634. if( re.search(l) != -1 ) {
  635. l = "<b>" +re.cap()+ "</b>\n";
  636. return l;
  637. }
  638. re.setPattern("(.*)(Also See-&gt;)(.*)");
  639. // Example: first sense of verb "keep"
  640. if( re.search(l) != -1 ) {
  641. l = re.cap(1);
  642. l += re.cap(2);
  643. TQStringList links = links.split(TQChar(';'), re.cap(3), false);
  644. for ( TQStringList::Iterator it = links.begin(); it != links.end(); ++it ) {
  645. TQString link = (*it);
  646. if( it != links.begin() ) {
  647. l += ", ";
  648. }
  649. link = link.stripWhiteSpace();
  650. link = link.remove(TQRegExp("#\\d+"));
  651. l += "<a href=\"" +link+ "\">" +link+ "</a>";
  652. }
  653. l.prepend (' '); // indent in table
  654. }
  655. re.setPattern("(.*)(=&gt;|HAS \\w+:|PART OF:)(.*) --");
  656. re.setMinimal(true); // non-greedy
  657. if( re.search(l) != -1 ) {
  658. int dash_pos = l.find("--");
  659. TQString line_end = l.mid(dash_pos+2, l.length()-dash_pos);
  660. l = re.cap(1);
  661. l += re.cap(2) + " ";
  662. TQStringList links = links.split(TQChar(','), re.cap(3), false);
  663. for ( TQStringList::Iterator it = links.begin(); it != links.end(); ++it ) {
  664. TQString link = (*it);
  665. if( it != links.begin() ) {
  666. l += ", ";
  667. }
  668. link = link.stripWhiteSpace();
  669. l += "<a href=\"" +link+ "\">" +link+ "</a>";
  670. }
  671. l += "<font color=\"#777777\">" +line_end+ "</font>";
  672. l.prepend(' '); // indent in table
  673. return l;
  674. }
  675. re.setMinimal(false); // greedy again
  676. return l;
  677. }
  678. /**
  679. * Sort a list case insensitively.
  680. * Be careful: @p list is modified
  681. * TODO: use ksortablevaluelist?
  682. */
  683. TQStringList Thesaurus::sortTQStringList(TQStringList list)
  684. {
  685. // Sort list case-insensitive. This looks strange but using a TQMap
  686. // is even suggested by the TQt documentation.
  687. TQMap<TQString,TQString> map_list;
  688. for ( TQStringList::Iterator it = list.begin(); it != list.end(); ++it ) {
  689. TQString str = *it;
  690. map_list[str.lower()] = str;
  691. }
  692. list.clear();
  693. TQMap<TQString,TQString>::Iterator it;
  694. // TQt doc: "the items are alphabetically sorted [by key] when iterating over the map":
  695. for( it = map_list.begin(); it != map_list.end(); ++it ) {
  696. list.append(it.data());
  697. }
  698. return list;
  699. }
  700. #include "main.moc"