diff options
Diffstat (limited to 'scribus/plugins/tools/spellcheck/aspellpluginimpl.cpp')
| -rw-r--r-- | scribus/plugins/tools/spellcheck/aspellpluginimpl.cpp | 516 |
1 files changed, 516 insertions, 0 deletions
diff --git a/scribus/plugins/tools/spellcheck/aspellpluginimpl.cpp b/scribus/plugins/tools/spellcheck/aspellpluginimpl.cpp new file mode 100644 index 0000000..d2e7a74 --- /dev/null +++ b/scribus/plugins/tools/spellcheck/aspellpluginimpl.cpp @@ -0,0 +1,516 @@ +/* +For general Scribus (>=1.3.2) copyright and licensing information please refer +to the COPYING file provided with the program. Following this notice may exist +a copyright and/or license notice that predates the release of Scribus 1.3.2 +for which a new license (GPL+exception) is in place. +*/ +#include "aspellpluginimpl.h" +#include "pageitem_textframe.h" +#include "text/specialchars.h" +#include "util.h" +#include <QDebug> +#include <QMessageBox> + +const char* AspellPluginImpl::kDEF_CONTEXT = "AspellPlugin"; +const QString AspellPluginImpl::kDEF_ASPELL_ENTRY = + QString( "en" ) + Speller::Aspell::Suggest::kDICT_DELIM + + QString( "en" ) + Speller::Aspell::Suggest::kDICT_DELIM + + QString( "*" ) + Speller::Aspell::Suggest::kDICT_DELIM + + QString( "60" ); // I.e., en/en/*/60 corresponding to default English dictionary + +// Initialize members here, if any +AspellPluginImpl::AspellPluginImpl(ScribusDoc* doc, QWidget* parent) : + QDialog( parent ), + fsuggest(0), + fdoc( doc ), + m_docIsChanged(false), + fpos(0), + fidx(0), + m_errorMessage("") +{ + // Constructor. + setupUi( this ); + setModal( true ); + + rememberedWords.clear(); + // Get stored language, jargon, encoding settings + fprefs = PrefsManager::instance()->prefsFile->getPluginContext( kDEF_CONTEXT ); + getPreferences(); + QString text = tr( "Loaded " ) + (fentry == kDEF_ASPELL_ENTRY ? tr( "default " ) : "") + fentry + tr( " aspell dictionary." ); + doc->scMW()->setStatusBarInfoText( text ); + bool dictCount=0; + try + { + // Deactivate GUI elements in spell-checking tab until an + // aspell dictionary is chosen. + activateSpellGUI(false); + + // Create speller, and get list of available aspell + // dictionaries. + fsuggest = new Speller::Aspell::Suggest( flang.toUtf8().data(), fjargon.toUtf8().data(), fencoding.toUtf8().data() ); + Q_CHECK_PTR( fsuggest ); + + // Get list of available aspell dictionaries + std::vector<std::string> entries; + fsuggest->listDicts( entries ); + dictCount=entries.size(); + if (!entries.empty()) + { + //qDebug()<<"Listing dictionaries"; + for( std::vector<std::string>::const_iterator i = entries.begin(); i != entries.end(); ++i ) + { + // FIXME: Handle encodings other than UTF-8. + flistDicts->addItem(i->c_str()); + //qDebug() << i->c_str(); + } + } + // check the availability of any dict. If == 0 then the plugin + // is disabled in all GUI. + if (entries.empty() || flistDicts->count() == 0) + { + //qDebug() << "Dictionaries found:"<<flistDicts->count(); + m_errorMessage = tr("No available Aspell dictionaries found. Install some, please."); + qWarning()<<m_errorMessage.toUtf8().data(); + } + else + { + // use dict for system local if there are no preferences set before + QString locale(QLocale::system().name().left(2)); + if (fentry.isEmpty()) + { + int ix = flistDicts->findText(locale, Qt::MatchStartsWith); + if (ix != -1) + flistDicts->setCurrentIndex(ix); + else + { + fentry = kDEF_ASPELL_ENTRY; + setCurrentComboItem(flistDicts, fentry); + } + } + else + setCurrentComboItem(flistDicts, fentry); + handleSpellConfig(flistDicts->currentText()); + } + } + catch( const std::invalid_argument& err ) + { + QString warn = tr( "Spell Checker Plugin Failed to Initialise.\nConfiguration invalid" ); + qWarning()<<warn.toUtf8().data(); + if (m_errorMessage.isEmpty()) + m_errorMessage=warn; + } + catch( const std::runtime_error& err ) + { + QString warn = tr( "Spell Checker Plugin Failed to Initialise."); + if (dictCount==0) + warn+="\n"+tr( "No Aspell dictionaries could be found." ); + qWarning()<<warn.toUtf8().data(); + if (m_errorMessage.isEmpty()) + m_errorMessage=warn; + } + if (m_errorMessage.isEmpty()) + { + activateSpellGUI(true); + parseSelection(); + } +} +//__________________________________________________________________________ +AspellPluginImpl::~AspellPluginImpl() +{ + if (m_errorMessage.isEmpty()) + { + // Destructor + try + { + fsuggest->saveLists(); + } + catch( const std::runtime_error& err ) + { + qWarning( "aspellplugin (AspellPluginImpl::~AspellPlugin" + "Impl): Error in saving aspell word lists." ); + } + } + if (fsuggest) + delete fsuggest; +} +//__________________________________________________________________________ +void AspellPluginImpl::activateSpellGUI(bool active) +{ + // Activates spell-checking GUI elements in spell-checking + // tab, i.e., everything except combo box at top + fcurrWord->setEnabled( active ); + flistReplacements->setEnabled( active ); + fchangeBtn->setEnabled( active ); + fchangeAllBtn->setEnabled( active ); + fskipAllBtn->setEnabled( active ); + fskipBtn->setEnabled( active ); + faddWordBtn->setEnabled( active ); + ftextLabel1->setEnabled( active ); + ftextLabel2->setEnabled( active ); + ftextLabel4->setEnabled( active ); + fmisSpelling->setEnabled( active ); + flistDicts->setEnabled( active ); +} +//__________________________________________________________________________ +void AspellPluginImpl::nextWord() +{ + QChar ch; + QString wordBoundaries = QString(" .,:;\"'!?\n"); + uint len = fFrame->itemText.length(); + while (fpos < len) + { + ch = fFrame->itemText.text(fpos); + if ((wordBoundaries.indexOf(ch) >= 0) || ch.isSpace() || SpecialChars::isBreak(ch)) + { + ++fpos; + } + break; + } + int pa = fpos; + int pe = 0; + while (fpos < len) + { + ch = fFrame->itemText.text(fpos); + if ((wordBoundaries.indexOf(ch) >= 0) || ch.isSpace() || SpecialChars::isBreak(ch)) + { + break; + } + ++fpos; + } + pe = fpos; + if (pa == pe) + { + spellCheckDone(); // No more new Text, bail out from Spellchecking + return; + } + fcontent = fFrame->itemText.text(pa, pe - pa); + fpos = pa; +} +//__________________________________________________________________________ +void AspellPluginImpl::checkText() +{ + // Called from parseXXX(), after filling in the currently + // relevant text into 'fcontent'. Handles spell-checking of + // the text. + while (fpos < static_cast<uint>(fFrame->itemText.length())) + { + std::vector<std::string> replacement; + bool status = fsuggest->checkWord(fcontent.toUtf8().data(), replacement); + if (status) + break; + + fmisSpelling->setText( fcontent ); + fcurrWord->setText( "" ); + flistReplacements->clear(); + unsigned int idx = 0; + for( std::vector<std::string>::const_iterator i = replacement.begin(); i != replacement.end(); ++i ) + { + // FIXME: Handle encodings other than UTF-8. + QString dict = QString::fromUtf8(i->c_str()); + flistReplacements->insertItem( idx, dict ); + idx++; + } + if( flistReplacements->count() > 0 ) + { + // FIXME: Is this the correct substitute for + // setSelected()? + flistReplacements->setCurrentRow( 0 ); + fcurrWord->setText( flistReplacements->currentItem()->text() ); + } + if (rememberedWords.contains(fcontent)) + { + QString repl = rememberedWords.value(fcontent); + int cs, cx; + if (fcontent.length() == repl.length()) + { + for (cs = 0; cs < fcontent.length(); ++cs) + fFrame->itemText.replaceChar(fpos+cs, repl[cs]); + } + else + { + if (fcontent.length() < repl.length()) + { + for (cs = 0; cs < fcontent.length(); ++cs) + fFrame->itemText.replaceChar(fpos+cs, repl[cs]); + for (cx = cs; cx < repl.length(); ++cx) + fFrame->itemText.insertChars(fpos+cx, repl.mid(cx,1), true); + } + else + { + for (cs = 0; cs < repl.length(); ++cs) + fFrame->itemText.replaceChar(fpos+cs, repl[cs]); + fFrame->itemText.removeChars(fpos+cs, fcontent.length() - cs); + } + } + } + else + break; + + fpos += fcontent.length(); + nextWord(); + } +} +//__________________________________________________________________________ +void AspellPluginImpl::spellCheckDone() +{ + // Called once all words in the current text, i.e., in 'fcontent' + // have been spell-checked. Pops up an information dialog. + QString completeMsg(tr("Spelling check complete")); + QMessageBox::information(fdoc->scMW(), tr("Spell Checker"), completeMsg); + if( fFrame && fFrame->asTextFrame() ) + fFrame->asTextFrame()->invalidateLayout(); +// if( fnchanges.fntot > 0 ) + if (m_docIsChanged) + { + fdoc->changed(); + } + // Redraw document + fdoc->view()->DrawNew(); + QApplication::changeOverrideCursor(Qt::ArrowCursor); +// QApplication::restoreOverrideCursor(); + fdoc->scMW()->setStatusBarInfoText(completeMsg); + fdoc->scMW()->mainWindowProgressBar->reset(); + close(); +} +//__________________________________________________________________________ +void AspellPluginImpl::on_fcloseBtn_clicked() +{ + // Called when the "Close" button is clicked. Makes any pending + // replacements and closes the spell-checking window. + if (m_errorMessage.isEmpty()) + spellCheckDone(); // Also closes spell-checking window. +} +//__________________________________________________________________________ +void AspellPluginImpl::on_fchangeBtn_clicked() +{ + // Called when the "Change" button is clicked. Replaces the word + // being spell-checked with the current word in text edit box. + // FIXME: Handle encodings other than UTF-8. + QString repl = fcurrWord->text(); + int cs, cx; + m_docIsChanged = true; + + if (fcontent.length() == repl.length()) + { + for (cs = 0; cs < fcontent.length(); ++cs) + fFrame->itemText.replaceChar(fpos+cs, repl[cs]); + } + else + { + if (fcontent.length() < repl.length()) + { + for (cs = 0; cs < fcontent.length(); ++cs) + fFrame->itemText.replaceChar(fpos+cs, repl[cs]); + for (cx = cs; cx < repl.length(); ++cx) + fFrame->itemText.insertChars(fpos+cx, repl.mid(cx,1), true); + } + else + { + for (cs = 0; cs < repl.length(); ++cs) + fFrame->itemText.replaceChar(fpos+cs, repl[cs]); + fFrame->itemText.removeChars(fpos+cs, fcontent.length() - cs); + } + } + fpos += fcontent.length(); + nextWord(); + checkText(); +} +//__________________________________________________________________________ +void AspellPluginImpl::on_fchangeAllBtn_clicked() +{ + // Called when the "Change All" button is clicked. Replaces all + // instances of the word being spell-checked with the current word in + // text edit box. + QString repl = fcurrWord->text(); + int cs, cx; + m_docIsChanged = true; + + if (fcontent.length() == repl.length()) + { + for (cs = 0; cs < fcontent.length(); ++cs) + fFrame->itemText.replaceChar(fpos+cs, repl[cs]); + } + else + { + if (fcontent.length() < repl.length()) + { + for (cs = 0; cs < fcontent.length(); ++cs) + fFrame->itemText.replaceChar(fpos+cs, repl[cs]); + for (cx = cs; cx < repl.length(); ++cx) + fFrame->itemText.insertChars(fpos+cx, repl.mid(cx,1), true); + } + else + { + for (cs = 0; cs < repl.length(); ++cs) + fFrame->itemText.replaceChar(fpos+cs, repl[cs]); + fFrame->itemText.removeChars(fpos+cs, fcontent.length() - cs); + } + } + if (!rememberedWords.contains(fcontent)) + rememberedWords.insert(fcontent, repl); + fpos += fcontent.length(); + nextWord(); // Go to next word + checkText(); +} +//__________________________________________________________________________ +void AspellPluginImpl::on_fskipBtn_clicked() +{ + // Called when the "Skip" button is clicked. Skips the word currently + // being spell-checked. + fpos += fcontent.length(); + nextWord(); // Go to next word + checkText(); +} +//__________________________________________________________________________ +void AspellPluginImpl::on_fskipAllBtn_clicked() +{ + // Called when the "Skip All" button is clicked. Puts the word + // currently being spell-checked into the list of words to be ignored + // for this session. + try + { +#if 0 + std::cerr << "on_fskipAllBtn_clicked: Ignoring \"" << + fwordlist[fidx] << "\"\n"; +#endif + fsuggest->ignoreWord( fcontent.toUtf8().data() ); + } + catch( const std::runtime_error& err ) + { + + QString warn = + tr( "AspellPluginImpl::on_fskipAllBtn_clicked(): Unable " + "to skip all instances of \"" ) + fcontent + + tr(" by adding it to the session list."); + qWarning( "%s", warn.toUtf8().data() ); + } + fpos += fcontent.length(); + nextWord(); // Go to next word + checkText(); +} +//__________________________________________________________________________ +void AspellPluginImpl::on_faddWordBtn_clicked() +{ + // Called when the "Add word" button is clicked. Adds word to personal + // word list. + try + { + // FIXME: Handle encodings other than UTF-8. + // FIXME: Is this the right replacement for fcurrWord->text()? + fsuggest->addPersonalList( fcurrWord->text().toUtf8().data() ); + } + catch( const std::runtime_error& err ) + { + QString warn = + tr( "AspellPluginImpl::on_faddWordBtn_clicked(): " + "Unable to add word to personal list." ); + qWarning( "%s", warn.toUtf8().data() ); + } +} +//__________________________________________________________________________ +void AspellPluginImpl::on_flistReplacements_itemActivated() +{ + // Called when an item in the list of replacements is + // selected. Replaces current word in text edit box with the text + // of the selected item. + fcurrWord->setText( flistReplacements->currentItem()->text() ); +} + +bool AspellPluginImpl::handleSpellConfig(const QString & dictFullName) +{ + QString entry(dictFullName); + QStringList fields = entry.split( Speller::Aspell::Suggest::kDICT_DELIM ); + // Ensure that we have at least the right no.of fields. + if( fields.size() == 4 ) + { + QString value = + fields[0] + Speller::Aspell::Suggest::kDICT_DELIM + + fields[1] + Speller::Aspell::Suggest::kDICT_DELIM + + fields[2] + Speller::Aspell::Suggest::kDICT_DELIM + + fields[3]; + fsuggest->resetConfig( fields[1].toAscii().data(), fields[2].toAscii().data() ); + // FIXME: Handle encodings other than UTF-8. + setPreferences( fields[1], fields[2], Speller::Aspell::Suggest::kDEF_ENCODING, value ); + return true; + } + return false; +} +//__________________________________________________________________________ +void AspellPluginImpl::on_flistDicts_activated() +{ + // Called when an item in the list of available aspell dictionaries is + // selected, i.e., by double-clicking, or pressing enter. Resets + // aspell configuration to use the selected dictionary. + if (handleSpellConfig(flistDicts->currentText())) + { + // PV - 7523: In the spell checker, the only option to change + // the dictionary is to open the spell checker dialog. However, + // changing the dictionary does not recheck first word + // --- ask user if he wants to restart spellchecker with new lang + if (QMessageBox::question(this, + tr("Spell Checker"), + tr("Do you want start from the beginning of the selection " + "with new language selected?"), + QMessageBox::Yes | QMessageBox::No) + == QMessageBox::Yes) + { + fpos = 0; + parseSelection(); + } + } +} +//__________________________________________________________________________ +void AspellPluginImpl::getPreferences() +{ + // Retrieves user preferences from saved settings. Defaults are + // supplied + flang = fprefs->get( "lang", Speller::Aspell::Suggest::kDEF_LANG ); + fjargon = fprefs->get( "jargon", Speller::Aspell::Suggest::kDEF_JARGON ); + // FIXME: Handle encodings other than UTF-8. + fencoding = fprefs->get( "encoding", Speller::Aspell::Suggest::kDEF_ENCODING ); + // Don't use kDEF_ASPELL_ENTRY here. It's checked + // against system locale later when there is no preferences for it. + fentry = fprefs->get( "entry", "");//kDEF_ASPELL_ENTRY ); +} +//__________________________________________________________________________ +void AspellPluginImpl::setPreferences(const QString& lang, + const QString& jargon, + const QString& encoding, + const QString& entry) +{ + // Saves user preferences using Scribus preferences manager. + fprefs->set( "lang", lang ); + QString val = jargon == Speller::Aspell::Suggest::kEMPTY ? "" : jargon; + fprefs->set( "jargon", val ); + fprefs->set( "encoding", encoding ); + fprefs->set( "entry", entry ); +} +//__________________________________________________________________________ +void AspellPluginImpl::languageChange() +{ + qWarning( "AspellPluginImpl::languageChange(): Not implemented yet" ); +} +//__________________________________________________________________________ +void AspellPluginImpl::parseItem() +{ + // Parse text in a frame, and spell-check it. + // Process only text frames + if( fFrame && fFrame->asTextFrame() ) + { + nextWord(); + checkText(); + } +} +//__________________________________________________________________________ +void AspellPluginImpl::parseSelection() +{ + fcontent.truncate( 0 ); // Start with empty string + uint ndocs = fdoc->m_Selection->count(); + for( uint i = 0; i < ndocs; ++i ) + { + fFrame = fdoc->m_Selection->itemAt( i ); + parseItem(); + } +} +//__________________________________________________________________________ +//@@@@@@@@@@@@@@@@@@@@@@@@@ END OF FILE @@@@@@@@@@@@@@@@@@@@@@@@@ |
