diff options
Diffstat (limited to 'tools/thesaurus/main.cpp')
-rw-r--r-- | tools/thesaurus/main.cpp | 817 |
1 files changed, 817 insertions, 0 deletions
diff --git a/tools/thesaurus/main.cpp b/tools/thesaurus/main.cpp new file mode 100644 index 00000000..06da2682 --- /dev/null +++ b/tools/thesaurus/main.cpp @@ -0,0 +1,817 @@ +/* + $Id: main.cpp 466447 2005-10-02 17:54:10Z zander $ + This file is part of the KDE project + Copyright (C) 2001,2002,2003 Daniel Naber <daniel.naber@t-online.de> + This is a thesaurus based on a subset of WordNet. It also offers an + almost complete WordNet 1.7 frontend (WordNet is a powerful lexical + database/thesaurus) +*/ +/*************************************************************************** + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ + +/* +TODO: +-Be more verbose if the result is empty +-See the TODO's in the source below + +-If no match was found, use KSpell to offer alternative spellings? +-Don't start WordNet before its tab is activated? +-Maybe remove more uncommon words. However, the "polysemy/familiarity + count" is sometimes very low for quite common word, e.g. "sky". + +-Fix "no mimesource" warning of TQTextBrowser? Seems really harmless. + +NOT TODO: +-Add part of speech information -- I think this would blow up the + filesize too much +*/ + +#include "main.h" + +#include <tqfile.h> +#include <tqtoolbutton.h> +#include <kiconloader.h> +#include <tdefiledialog.h> +#include <tdeversion.h> + +/*************************************************** + * + * Factory + * + ***************************************************/ + +typedef KGenericFactory<Thesaurus, KDataTool> ThesaurusFactory; +K_EXPORT_COMPONENT_FACTORY( libthesaurustool, ThesaurusFactory("thesaurus_tool") ) + +/*************************************************** + * + * Thesaurus * + ***************************************************/ + +Thesaurus::Thesaurus(TQObject* parent, const char* name, const TQStringList &) + : KDataTool(parent, name) +{ + + m_dialog = new KDialogBase(KJanusWidget::Plain, TQString(), + KDialogBase::Help|KDialogBase::Ok|KDialogBase::Cancel, KDialogBase::Ok); + m_dialog->setHelp(TQString(), "thesaurus"); + m_dialog->resize(600, 400); + + m_config = new TDEConfig("kthesaurusrc"); + m_data_file = m_config->readPathEntry("datafile"); + if( ! m_data_file ) { + m_data_file = TDEGlobal::dirs()->findResourceDir("data", "thesaurus/") + + "thesaurus/thesaurus.txt"; + } + setCaption(); + + m_no_match = i18n("(No match)"); + + m_replacement = false; + m_history_pos = 1; + + m_page = m_dialog->plainPage(); + TQVBoxLayout *m_top_layout = new TQVBoxLayout(m_page, KDialog::marginHint(), KDialog::spacingHint()); + + TQHBoxLayout *row1 = new TQHBoxLayout(m_top_layout); + m_edit = new KHistoryCombo(m_page); + m_edit_label = new TQLabel(m_edit, i18n("&Search for:"), m_page); + m_search = new KPushButton(i18n("S&earch"), m_page); + connect(m_search, TQT_SIGNAL(clicked()), + this, TQT_SLOT(slotFindTerm())); + row1->addWidget(m_edit_label, 0); + row1->addWidget(m_edit, 1); + row1->addWidget(m_search, 0); + m_back = new TQToolButton(m_page); + m_back->setIconSet(BarIconSet(TQString::fromLatin1("back"))); + TQToolTip::add(m_back, i18n("Back")); + row1->addWidget(m_back, 0); + m_forward = new TQToolButton(m_page); + m_forward->setIconSet(BarIconSet(TQString::fromLatin1("forward"))); + TQToolTip::add(m_forward, i18n("Forward")); + row1->addWidget(m_forward, 0); + m_lang = new KPushButton(i18n("Change Language..."), m_page); + connect(m_lang, TQT_SIGNAL(clicked()), this, TQT_SLOT(slotChangeLanguage())); + row1->addWidget(m_lang, 0); + + connect(m_back, TQT_SIGNAL(clicked()), this, TQT_SLOT(slotBack())); + connect(m_forward, TQT_SIGNAL(clicked()), this, TQT_SLOT(slotForward())); + + m_tab = new TQTabWidget(m_page); + m_top_layout->addWidget(m_tab); + + // + // Thesaurus Tab + // + + vbox = new TQVBox(m_tab); + m_tab->addTab(vbox, i18n("&Thesaurus")); + vbox->setMargin(KDialog::marginHint()); + vbox->setSpacing(KDialog::spacingHint()); + + TQHBox *hbox = new TQHBox(vbox); + hbox->setSpacing(KDialog::spacingHint()); + + grpbox_syn = new TQGroupBox( 1, Qt::Horizontal, i18n("Synonyms"), hbox); + m_thes_syn = new TQListBox(grpbox_syn); + + grpbox_hyper = new TQGroupBox( 1, Qt::Horizontal, i18n("More General Words"), hbox); + m_thes_hyper = new TQListBox(grpbox_hyper); + + grpbox_hypo = new TQGroupBox( 1, Qt::Horizontal, i18n("More Specific Words"), hbox); + m_thes_hypo = new TQListBox(grpbox_hypo); + + // single click -- keep display unambiguous by removing other selections: + + connect(m_thes_syn, TQT_SIGNAL(clicked(TQListBoxItem *)), m_thes_hyper, TQT_SLOT(clearSelection())); + connect(m_thes_syn, TQT_SIGNAL(clicked(TQListBoxItem *)), m_thes_hypo, TQT_SLOT(clearSelection())); + connect(m_thes_syn, TQT_SIGNAL(selectionChanged(TQListBoxItem *)), + this, TQT_SLOT(slotSetReplaceTerm(TQListBoxItem *))); + + connect(m_thes_hyper, TQT_SIGNAL(clicked(TQListBoxItem *)), m_thes_syn, TQT_SLOT(clearSelection())); + connect(m_thes_hyper, TQT_SIGNAL(clicked(TQListBoxItem *)), m_thes_hypo, TQT_SLOT(clearSelection())); + connect(m_thes_hyper, TQT_SIGNAL(selectionChanged(TQListBoxItem *)), + this, TQT_SLOT(slotSetReplaceTerm(TQListBoxItem *))); + + connect(m_thes_hypo, TQT_SIGNAL(clicked(TQListBoxItem *)), m_thes_syn, TQT_SLOT(clearSelection())); + connect(m_thes_hypo, TQT_SIGNAL(clicked(TQListBoxItem *)), m_thes_hyper, TQT_SLOT(clearSelection())); + connect(m_thes_hypo, TQT_SIGNAL(selectionChanged(TQListBoxItem *)), + this, TQT_SLOT(slotSetReplaceTerm(TQListBoxItem *))); + + // double click: + connect(m_thes_syn, TQT_SIGNAL(selected(const TQString &)), + this, TQT_SLOT(slotFindTerm(const TQString &))); + connect(m_thes_hyper, TQT_SIGNAL(selected(const TQString &)), + this, TQT_SLOT(slotFindTerm(const TQString &))); + connect(m_thes_hypo, TQT_SIGNAL(selected(const TQString &)), + this, TQT_SLOT(slotFindTerm(const TQString &))); + + // + // WordNet Tab + // + + vbox2 = new TQVBox(m_tab); + m_tab->addTab(vbox2, i18n("&WordNet")); + vbox2->setMargin(KDialog::marginHint()); + vbox2->setSpacing(KDialog::spacingHint()); + + m_combobox = new TQComboBox(vbox2); + m_combobox->setEditable(false); + connect(m_combobox, TQT_SIGNAL(activated(int)), this, TQT_SLOT(slotFindTerm())); + + m_resultbox = new TQTextBrowser(vbox2); + m_resultbox->setTextFormat(TQt::RichText); + // TODO?: m_resultbox->setMimeSourceFactory(...); to avoid warning + connect(m_resultbox, TQT_SIGNAL(linkClicked(const TQString &)), + this, TQT_SLOT(slotFindTerm(const TQString &))); + + // Connect for the history box + m_edit->setTrapReturnKey(true); // Do not use Return as default key... + connect(m_edit, TQT_SIGNAL(returnPressed(const TQString&)), this, TQT_SLOT(slotFindTerm(const TQString&))); + connect(m_edit, TQT_SIGNAL(activated(int)), this, TQT_SLOT(slotGotoHistory(int))); + + TQHBoxLayout *row2 = new TQHBoxLayout( m_top_layout ); + m_replace = new KLineEdit(m_page); + m_replace_label = new TQLabel(m_replace, i18n("&Replace with:"), m_page); + row2->addWidget(m_replace_label, 0); + row2->addWidget(m_replace, 1); + + // Set focus + m_edit->setFocus(); + slotUpdateNavButtons(); + + // + // The external command stuff + // + + // calling the 'wn' binary + m_wnproc = new TDEProcess; + connect(m_wnproc, TQT_SIGNAL(processExited(TDEProcess*)), this, TQT_SLOT(wnExited(TDEProcess*))); + connect(m_wnproc, TQT_SIGNAL(receivedStdout(TDEProcess*,char*,int)), + this, TQT_SLOT(receivedWnStdout(TDEProcess*, char*, int))); + connect(m_wnproc, TQT_SIGNAL(receivedStderr(TDEProcess*,char*,int)), + this, TQT_SLOT(receivedWnStderr(TDEProcess*, char*, int))); + + // grep'ing the text file + m_thesproc = new TDEProcess; + connect(m_thesproc, TQT_SIGNAL(processExited(TDEProcess*)), this, TQT_SLOT(thesExited(TDEProcess*))); + connect(m_thesproc, TQT_SIGNAL(receivedStdout(TDEProcess*,char*,int)), + this, TQT_SLOT(receivedThesStdout(TDEProcess*, char*, int))); + connect(m_thesproc, TQT_SIGNAL(receivedStderr(TDEProcess*,char*,int)), + this, TQT_SLOT(receivedThesStderr(TDEProcess*, char*, int))); + +} + + +Thesaurus::~Thesaurus() +{ + m_config->writePathEntry("datafile", m_data_file); + m_config->sync(); + delete m_config; + // FIXME?: this hopefully fixes the problem of a wrong cursor + // and a crash (when closing e.g. konqueror) when the thesaurus dialog + // gets close while it was still working and showing the wait cursor + TQApplication::restoreOverrideCursor(); + delete m_thesproc; + delete m_wnproc; + delete m_dialog; +} + + +bool Thesaurus::run(const TQString& command, void* data, const TQString& datatype, const TQString& mimetype) +{ + + // Check whether we can accept the data + if ( datatype != TQSTRING_OBJECT_NAME_STRING ) { + kdDebug(31000) << "Thesaurus only accepts datatype TQString" << endl; + return FALSE; + } + if ( mimetype != "text/plain" ) { + kdDebug(31000) << "Thesaurus only accepts mimetype text/plain" << endl; + return FALSE; + } + + if ( command == "thesaurus" ) { + // not called from an application like KWord, so make it possible + // to replace text: + m_replacement = true; + m_dialog->setButtonOKText(i18n("&Replace")); + } else if ( command == "thesaurus_standalone" ) { + // not called from any application, but from KThesaurus + m_replacement = false; + m_dialog->showButtonOK(false); + m_dialog->setButtonCancelText(i18n("&Close")); + m_replace->setEnabled(false); + m_replace_label->setEnabled(false); + } else { + kdDebug(31000) << "Thesaurus does only accept the command 'thesaurus' or 'thesaurus_standalone'" << endl; + kdDebug(31000) << "The command " << command << " is not accepted" << endl; + return FALSE; + } + + // Get data and clean it up: + TQString buffer = *((TQString *)data); + buffer = buffer.stripWhiteSpace(); + TQRegExp re("[.,;!?\"'()\\[\\]]"); + buffer.remove(re); + buffer = buffer.left(100); // limit maximum length + + m_wnproc_stdout = ""; + m_wnproc_stderr = ""; + + m_thesproc_stdout = ""; + m_thesproc_stderr = ""; + + if( ! buffer.isEmpty() ) { + slotFindTerm(buffer); + } + + if( m_dialog->exec() == TQDialog::Accepted ) { // "Replace" + *((TQString*)data) = m_replace->text(); + } + + return TRUE; +} + + +void Thesaurus::slotChangeLanguage() +{ + TQString filename = KFileDialog::getOpenFileName( + TDEGlobal::dirs()->findResourceDir("data", "thesaurus/")+"thesaurus/"); + if( !filename.isNull() ) { + m_data_file = filename; + setCaption(); + } +} + +void Thesaurus::setCaption() +{ + KURL url = KURL(); + url.setPath(m_data_file); + m_dialog->setCaption(i18n("Related Words - %1").arg(url.fileName())); +} + +// Enbale or disable back and forward button +void Thesaurus::slotUpdateNavButtons() +{ + if( m_history_pos <= 1 ) { // 1 = first position + m_back->setEnabled(false); + } else { + m_back->setEnabled(true); + } + if( m_history_pos >= m_edit->count() ) { + m_forward->setEnabled(false); + } else { + m_forward->setEnabled(true); + } +} + +// Go to an item from the editbale combo box. +void Thesaurus::slotGotoHistory(int index) +{ + m_history_pos = m_edit->count() - index; + slotFindTerm(m_edit->text(index), false); +} + +// Triggered when the back button is clicked. +void Thesaurus::slotBack() +{ + m_history_pos--; + int pos = m_edit->count() - m_history_pos; + m_edit->setCurrentItem(pos); + slotFindTerm(m_edit->text(pos), false); +} + +// Triggered when the forward button is clicked. +void Thesaurus::slotForward() +{ + m_history_pos++; + int pos = m_edit->count() - m_history_pos; + m_edit->setCurrentItem(pos); + slotFindTerm(m_edit->text(pos), false); +} + +// Triggered when a word is selected in the list box. +void Thesaurus::slotSetReplaceTerm(TQListBoxItem *item) +{ + if( ! item ) + return; + m_replace->setText(item->text()); +} + +void Thesaurus::slotSetReplaceTerm(const TQString &term) +{ + if( m_replacement && term != m_no_match ) { + m_replace->setText(term); + } +} + +// Triggered when Return is pressed. +void Thesaurus::slotFindTerm() +{ + findTerm(m_edit->currentText()); +} + +// Triggered when a word is clicked / a list item is double-clicked. +void Thesaurus::slotFindTerm(const TQString &term, bool add_to_history) +{ + slotSetReplaceTerm(term); + if( term.startsWith("http://") ) { + (void) new KRun(KURL(term)); + } else { + if( add_to_history ) { + m_edit->insertItem(term, 0); + m_history_pos = m_edit->count(); + m_edit->setCurrentItem(0); + } + slotUpdateNavButtons(); + findTerm(term); + } +} + +void Thesaurus::findTerm(const TQString &term) +{ + findTermThesaurus(term); + findTermWordnet(term); +} + + +// +// Thesaurus +// +void Thesaurus::findTermThesaurus(const TQString &term) +{ + + if( !TQFile::exists(m_data_file) ) { + KMessageBox::error(0, i18n("The thesaurus file '%1' was not found. " + "Please use 'Change Language...' to select a thesaurus file."). + arg(m_data_file)); + return; + } + + TQApplication::setOverrideCursor(KCursor::waitCursor()); + + m_thesproc_stdout = ""; + m_thesproc_stderr = ""; + + // Find only whole words. Looks clumsy, but this way we don't have to rely on + // features that might only be in certain versions of grep: + TQString term_tmp = ";" + term.stripWhiteSpace() + ";"; + m_thesproc->clearArguments(); + *m_thesproc << "grep" << "-i" << term_tmp; + *m_thesproc << m_data_file; + + if( !m_thesproc->start(TDEProcess::NotifyOnExit, TDEProcess::AllOutput) ) { + KMessageBox::error(0, i18n("Failed to execute grep.")); + TQApplication::restoreOverrideCursor(); + return; + } +} + +// The external process has ended, so we parse its result and put it in +// the list box. +void Thesaurus::thesExited(TDEProcess *) +{ + + if( !m_thesproc_stderr.isEmpty() ) { + KMessageBox::error(0, i18n("<b>Error:</b> Failed to execute grep. " + "Output:<br>%1").arg(m_thesproc_stderr)); + TQApplication::restoreOverrideCursor(); + return; + } + + TQString search_term = m_edit->currentText().stripWhiteSpace(); + + TQStringList syn; + TQStringList hyper; + TQStringList hypo; + + TQStringList lines = lines.split(TQChar('\n'), m_thesproc_stdout, false); + for ( TQStringList::Iterator it = lines.begin(); it != lines.end(); ++it ) { + TQString line = (*it); + if( line.startsWith(" ") ) { // ignore license (two spaces) + continue; + } + int sep_pos = line.find("#"); + TQString syn_part = line.left(sep_pos); + TQString hyper_part = line.right(line.length()-sep_pos-1); + TQStringList syn_tmp = TQStringList::split(TQChar(';'), syn_part); + TQStringList hyper_tmp = TQStringList::split(TQChar(';'), hyper_part); + if( syn_tmp.grep(search_term, false).size() > 0 ) { + // match on the left side of the '#' -- synonyms + for ( TQStringList::Iterator it2 = syn_tmp.begin(); it2 != syn_tmp.end(); ++it2 ) { + // add if it's not the term itself and if it's not yet in the list + TQString term = (*it2); + if( term.lower() != search_term.lower() && syn.contains(term) == 0 ) { + syn.append(term); + } + } + for ( TQStringList::Iterator it2 = hyper_tmp.begin(); it2 != hyper_tmp.end(); ++it2 ) { + TQString term = (*it2); + if( term.lower() != search_term.lower() && hyper.contains(term) == 0 ) { + hyper.append(term); + } + } + } + if( hyper_tmp.grep(search_term, false).size() > 0 ) { + // match on the right side of the '#' -- hypernyms + for ( TQStringList::Iterator it2 = syn_tmp.begin(); it2 != syn_tmp.end(); ++it2 ) { + TQString term = (*it2); + if( term.lower() != search_term && hypo.contains(term) == 0 ) { + hypo.append(term); + } + } + } + } + + m_thes_syn->clear(); + if( syn.size() > 0 ) { + syn = sortTQStringList(syn); + m_thes_syn->insertStringList(syn); + m_thes_syn->setEnabled(true); + } else { + m_thes_syn->insertItem(m_no_match); + m_thes_syn->setEnabled(false); + } + + m_thes_hyper->clear(); + if( hyper.size() > 0 ) { + hyper = sortTQStringList(hyper); + m_thes_hyper->insertStringList(hyper); + m_thes_hyper->setEnabled(true); + } else { + m_thes_hyper->insertItem(m_no_match); + m_thes_hyper->setEnabled(false); + } + + m_thes_hypo->clear(); + if( hypo.size() > 0 ) { + hypo = sortTQStringList(hypo); + m_thes_hypo->insertStringList(hypo); + m_thes_hypo->setEnabled(true); + } else { + m_thes_hypo->insertItem(m_no_match); + m_thes_hypo->setEnabled(false); + } + + TQApplication::restoreOverrideCursor(); +} + +void Thesaurus::receivedThesStdout(TDEProcess *, char *result, int len) +{ + m_thesproc_stdout += TQString::fromLocal8Bit( TQCString(result, len+1) ); +} + +void Thesaurus::receivedThesStderr(TDEProcess *, char *result, int len) +{ + m_thesproc_stderr += TQString::fromLocal8Bit( TQCString(result, len+1) ); +} + + +// +// WordNet +// +void Thesaurus::findTermWordnet(const TQString &term) +{ + TQApplication::setOverrideCursor(KCursor::waitCursor()); + + m_wnproc_stdout = ""; + m_wnproc_stderr = ""; + + m_wnproc->clearArguments(); + *m_wnproc << "wn"; + *m_wnproc << term; + + // get all results: nouns, verbs, adjectives, adverbs (see below for order): + if( m_combobox->currentItem() == 0 ) { + *m_wnproc << "-synsn" << "-synsv" << "-synsa" << "-synsr"; + m_mode = other; + } else if( m_combobox->currentItem() == 1 ) { + *m_wnproc << "-simsv"; + m_mode = other; + } else if( m_combobox->currentItem() == 2 ) { + *m_wnproc << "-antsn" << "-antsv" << "-antsa" << "-antsr"; + m_mode = other; + } else if( m_combobox->currentItem() == 3 ) { + *m_wnproc << "-hypon" << "-hypov"; + m_mode = other; + } else if( m_combobox->currentItem() == 4 ) { + *m_wnproc << "-meron"; + m_mode = other; + } else if( m_combobox->currentItem() == 5 ) { + *m_wnproc << "-holon"; + m_mode = other; + } else if( m_combobox->currentItem() == 6 ) { + // e.g. "size -> large/small" + *m_wnproc << "-attrn" << "-attra"; + m_mode = other; + } else if( m_combobox->currentItem() == 7 ) { + // e.g. "kill -> die" + *m_wnproc << "-causv"; + m_mode = other; + } else if( m_combobox->currentItem() == 8 ) { + // e.g. "walk -> step" + *m_wnproc << "-entav"; + m_mode = other; + } else if( m_combobox->currentItem() == 9 ) { + *m_wnproc << "-famln" << "-famlv" << "-famla" << "-famlr"; + m_mode = other; + } else if( m_combobox->currentItem() == 10 ) { + *m_wnproc << "-framv"; + m_mode = other; + } else if( m_combobox->currentItem() == 11 ) { + *m_wnproc << "-grepn" << "-grepv" << "-grepa" << "-grepr"; + m_mode = grep; + } else if( m_combobox->currentItem() == 12 ) { + *m_wnproc << "-over"; + m_mode = other; + } + *m_wnproc << "-g"; // "Display gloss" + + int current = m_combobox->currentItem(); // remember current position + m_combobox->clear(); + + // warning: order matters! + // 0: + m_combobox->insertItem(i18n("Synonyms/Hypernyms - Ordered by Frequency")); + m_combobox->insertItem(i18n("Synonyms - Ordered by Similarity of Meaning (verbs only)")); + m_combobox->insertItem(i18n("Antonyms - Words with Opposite Meanings")); + m_combobox->insertItem(i18n("Hyponyms - ... is a (kind of) %1").arg(m_edit->currentText())); + m_combobox->insertItem(i18n("Meronyms - %1 has a ...").arg(m_edit->currentText())); + // 5: + m_combobox->insertItem(i18n("Holonyms - ... has a %1").arg(m_edit->currentText())); + m_combobox->insertItem(i18n("Attributes")); + m_combobox->insertItem(i18n("Cause To (for some verbs only)")); + m_combobox->insertItem(i18n("Verb Entailment (for some verbs only)")); + m_combobox->insertItem(i18n("Familiarity & Polysemy Count")); + // 10: + m_combobox->insertItem(i18n("Verb Frames (examples of use)")); + m_combobox->insertItem(i18n("List of Compound Words")); + m_combobox->insertItem(i18n("Overview of Senses")); + + /** NOT todo: + * -Hypernym tree: layout is difficult, you can get the same information + * by following links + * -Coordinate terms (sisters): just go to synset and then use hyponyms + * -Has Part Meronyms, Has Substance Meronyms, Has Member Meronyms, + * Member of Holonyms, Substance of Holonyms, Part of Holonyms: + * these are just subsets of Meronyms/Holonyms + * -hmern, hholn: these are just compact versions, you can get the + * same information by following some links + */ + + /** TODO?: + * -pert (e.g. nuclear -> nuclues, but "=>" are nested, difficult to display) + * -nomn(n|v), e.g. deny -> denial, but this doesn't seem to work? + */ + + m_combobox->setCurrentItem(current); // reset previous position + + if( m_wnproc->isRunning() ) { + // should never happen + kdDebug(31000) << "Warning: findTerm(): process is already running?!" << endl; + TQApplication::restoreOverrideCursor(); + return; + } + + if( !m_wnproc->start(TDEProcess::NotifyOnExit, TDEProcess::AllOutput) ) { + m_resultbox->setText(i18n("<b>Error:</b> Failed to execute the WordNet program 'wn'. " + "WordNet has to be installed on your computer to use this component of kthesaurus, " + "and 'wn' has to be available in your $PATH environment variable. " + "Look for a WordNet package from your upstream distro package repository. " + "Or you can obtain WordNet at <a href=\"http://wordnet.princeton.edu/wordnet/download/current-version/\">" + "http://wordnet.princeton.edu/wordnet/download/current-version/</a>. " + "Notice that WordNet only supports the English language.")); + m_combobox->setEnabled(false); + TQApplication::restoreOverrideCursor(); + return; + } + +} + +// The process has ended, so parse its result and display it as TQt richtext. +void Thesaurus::wnExited(TDEProcess *) +{ + + if( !m_wnproc_stderr.isEmpty() ) { + m_resultbox->setText(i18n("<b>Error:</b> Failed to execute WordNet program 'wn'. " + "Output:<br>%1").arg(m_wnproc_stderr)); + TQApplication::restoreOverrideCursor(); + return; + } + + if( m_wnproc_stdout.isEmpty() ) { + m_resultbox->setText(i18n("No match for '%1'.").arg(m_edit->currentText())); + } else { + // render in a table, each line one row: + TQStringList lines = lines.split(TQChar('\n'), m_wnproc_stdout, false); + TQString result = "<qt><table>\n"; + // TODO in TQt > 3.01: try without the following line (it's necessary to ensure the + // first column is really always quite small): + result += "<tr><td width=\"10%\"></td><td width=\"90%\"></td></tr>\n"; + uint ct = 0; + for ( TQStringList::Iterator it = lines.begin(); it != lines.end(); ++it ) { + TQString l = (*it); + // Remove some lines: + TQRegExp re("^\\d+( of \\d+)? senses? of \\w+"); + if( re.search(l) != -1 ) { + continue; + } + // Escape XML: + l = l.replace('&', "&"); + l = l.replace('<', "<"); + l = l.replace('>', ">"); + // TODO?: + // move "=>" in own column? + l = formatLine(l); + // Table layout: + result += "<tr>"; + if( l.startsWith(" ") ) { + result += "\t<td width=\"15\"></td>"; + l = l.stripWhiteSpace(); + result += "\t<td>" + l + "</td>"; + } else { + l = l.stripWhiteSpace(); + result += "<td colspan=\"2\">" + l + "</td>"; + } + result += "</tr>\n"; + ct++; + } + result += "\n</table></qt>\n"; + m_resultbox->setText(result); + m_resultbox->setContentsPos(0,0); + //kdDebug() << result << endl; + } + + TQApplication::restoreOverrideCursor(); +} + +void Thesaurus::receivedWnStdout(TDEProcess *, char *result, int len) +{ + m_wnproc_stdout += TQString::fromLocal8Bit( TQCString(result, len+1) ); +} + +void Thesaurus::receivedWnStderr(TDEProcess *, char *result, int len) +{ + m_wnproc_stderr += TQString::fromLocal8Bit( TQCString(result, len+1) ); +} + + +// +// Tools +// + +// Format lines using TQt's simple richtext. +TQString Thesaurus::formatLine(TQString l) +{ + + if( l == "--------------" ) { + return TQString("<hr>"); + } + + TQRegExp re; + + re.setPattern("^(\\d+\\.)(.*)$"); + if( re.search(l) != -1 ) { + l = "<b>" +re.cap(1)+ "</b>" +re.cap(2); + return l; + } + + re.setPattern("^.* of (noun|verb|adj|adv) .*"); + if( re.search(l) != -1 ) { + l = "<font size=\"5\">" +re.cap()+ "</font>\n\n"; + return l; + } + + if( m_mode == grep ) { + l = l.stripWhiteSpace(); + return TQString("<a href=\"" +l+ "\">" +l+ "</a>"); + } + + re.setPattern("^(Sense \\d+)"); + if( re.search(l) != -1 ) { + l = "<b>" +re.cap()+ "</b>\n"; + return l; + } + + re.setPattern("(.*)(Also See->)(.*)"); + // Example: first sense of verb "keep" + if( re.search(l) != -1 ) { + l = re.cap(1); + l += re.cap(2); + TQStringList links = links.split(TQChar(';'), re.cap(3), false); + for ( TQStringList::Iterator it = links.begin(); it != links.end(); ++it ) { + TQString link = (*it); + if( it != links.begin() ) { + l += ", "; + } + link = link.stripWhiteSpace(); + link = link.remove(TQRegExp("#\\d+")); + l += "<a href=\"" +link+ "\">" +link+ "</a>"; + } + l.prepend (' '); // indent in table + } + + re.setPattern("(.*)(=>|HAS \\w+:|PART OF:)(.*) --"); + re.setMinimal(true); // non-greedy + if( re.search(l) != -1 ) { + int dash_pos = l.find("--"); + TQString line_end = l.mid(dash_pos+2, l.length()-dash_pos); + l = re.cap(1); + l += re.cap(2) + " "; + TQStringList links = links.split(TQChar(','), re.cap(3), false); + for ( TQStringList::Iterator it = links.begin(); it != links.end(); ++it ) { + TQString link = (*it); + if( it != links.begin() ) { + l += ", "; + } + link = link.stripWhiteSpace(); + l += "<a href=\"" +link+ "\">" +link+ "</a>"; + } + l += "<font color=\"#777777\">" +line_end+ "</font>"; + l.prepend(' '); // indent in table + return l; + } + re.setMinimal(false); // greedy again + + return l; +} + +/** + * Sort a list case insensitively. + * Be careful: @p list is modified + * TODO: use ksortablevaluelist? + */ +TQStringList Thesaurus::sortTQStringList(TQStringList list) +{ + // Sort list case-insensitive. This looks strange but using a TQMap + // is even suggested by the TQt documentation. + TQMap<TQString,TQString> map_list; + for ( TQStringList::Iterator it = list.begin(); it != list.end(); ++it ) { + TQString str = *it; + map_list[str.lower()] = str; + } + list.clear(); + TQMap<TQString,TQString>::Iterator it; + // TQt doc: "the items are alphabetically sorted [by key] when iterating over the map": + for( it = map_list.begin(); it != map_list.end(); ++it ) { + list.append(it.data()); + } + return list; +} + +#include "main.moc" |