File indexing completed on 2024-04-28 03:40:31

0001 /***************************************************************************
0002  *   Copyright (C) 2002 by Gunnar Schmi Dt <kmouth@schmi-dt.de             *
0003  *             (C) 2015 by Jeremy Whiting <jpwhiting@kde.org>              *
0004  *                                                                         *
0005  *   This program is free software; you can redistribute it and/or modify  *
0006  *   it under the terms of the GNU General Public License as published by  *
0007  *   the Free Software Foundation; either version 2 of the License, or     *
0008  *   (at your option) any later version.                                   *
0009  *                                                                         *
0010  *   This program is distributed in the hope that it will be useful,       *
0011  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
0012  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
0013  *   GNU General Public License for more details.                          *
0014  *                                                                         *
0015  *   You should have received a copy of the GNU General Public License     *
0016  *   along with this program; if not, write to the                         *
0017  *   Free Software Foundation, Inc.,                                       *
0018  *   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.          *
0019  ***************************************************************************/
0020 
0021 #include "wordcompletion.h"
0022 
0023 #include <QFile>
0024 #include <QList>
0025 #include <QRegularExpression>
0026 #include <QStandardPaths>
0027 #include <QTextStream>
0028 
0029 #include <KConfigGroup>
0030 #include <KSharedConfig>
0031 
0032 class WordCompletion::WordCompletionPrivate
0033 {
0034     friend class WordCompletion;
0035 
0036 public:
0037     WordCompletionPrivate()
0038         : blockCurrentListSignal(false)
0039         , wordsToSave(false)
0040     {
0041     }
0042 
0043 private:
0044     typedef QMap<QString, int> WordMap;
0045     struct DictionaryDetails {
0046         QString filename;
0047         QString language;
0048     };
0049 
0050     QString lastText;
0051     QMap<QString, int> map;
0052     QMap<QString, int> addedWords;
0053     QMap<QString, DictionaryDetails> dictDetails;
0054     QStringList dictionaries;
0055     QString current;
0056     bool blockCurrentListSignal;
0057     bool wordsToSave;
0058 };
0059 
0060 WordCompletion::WordCompletion()
0061     : KCompletion()
0062 {
0063     d = new WordCompletionPrivate();
0064     configure();
0065 }
0066 
0067 WordCompletion::~WordCompletion()
0068 {
0069     save();
0070     delete d;
0071 }
0072 
0073 typedef QPair<int, QString> Match;
0074 typedef QList<Match> MatchList;
0075 
0076 QString WordCompletion::makeCompletion(const QString &text)
0077 {
0078     if (d->lastText != text) {
0079         d->lastText = text;
0080         KCompletion::clear();
0081 
0082         int border = text.lastIndexOf(QRegularExpression(QStringLiteral("\\W")));
0083         QString suffix = text.right(text.length() - border - 1).toLower();
0084         QString prefix = text.left(border + 1);
0085 
0086         if (suffix.length() > 0) {
0087             MatchList matches;
0088             QMap<QString, int>::ConstIterator it;
0089             for (it = d->map.constBegin(); it != d->map.constEnd(); ++it)
0090                 if (it.key().startsWith(suffix))
0091                     matches += Match(-it.value(), it.key());
0092             std::sort(matches.begin(), matches.end());
0093 
0094             MatchList::ConstIterator iter = matches.constBegin();
0095             for (int count = 0; (iter != matches.constEnd()) && (count < 10); ++iter, ++count) {
0096                 int length = (*iter).second.length() + prefix.length() - text.length();
0097                 KCompletion::addItem(text + (*iter).second.right(length), -(*iter).first);
0098             }
0099         }
0100     }
0101 
0102     // call the KCompletion::makeCompletion(...) method
0103     return KCompletion::makeCompletion(text);
0104 }
0105 
0106 QStringList WordCompletion::wordLists()
0107 {
0108     return d->dictionaries;
0109 }
0110 
0111 QStringList WordCompletion::wordLists(const QString &language)
0112 {
0113     QStringList result;
0114     for (QStringList::const_iterator it = d->dictionaries.constBegin(); it != d->dictionaries.constEnd(); ++it)
0115         if (d->dictDetails[*it].language == language)
0116             result += *it;
0117     return result;
0118 }
0119 
0120 QString WordCompletion::languageOfWordList(const QString &wordlist)
0121 {
0122     if (d->dictDetails.contains(wordlist))
0123         return d->dictDetails[wordlist].language;
0124     else
0125         return QString();
0126 }
0127 
0128 QString WordCompletion::currentWordList()
0129 {
0130     return d->current;
0131 }
0132 
0133 bool WordCompletion::isConfigured()
0134 {
0135     bool result = KSharedConfig::openConfig()->hasGroup(QLatin1String("Dictionary 0"));
0136 
0137     return result;
0138 }
0139 
0140 void WordCompletion::configure()
0141 {
0142     if (d->wordsToSave)
0143         save();
0144     d->wordsToSave = false;
0145 
0146     d->dictionaries.clear();
0147     d->dictDetails.clear();
0148 
0149     const QStringList groups = KSharedConfig::openConfig()->groupList();
0150     for (QStringList::const_iterator it = groups.constBegin(); it != groups.constEnd(); ++it)
0151         if ((*it).startsWith(QLatin1String("Dictionary "))) {
0152             KConfigGroup cg(KSharedConfig::openConfig(), *it);
0153             WordCompletionPrivate::DictionaryDetails details;
0154             details.filename = cg.readEntry("Filename");
0155             details.language = cg.readEntry("Language");
0156             QString name = cg.readEntry("Name");
0157             d->dictDetails[name] = details;
0158             d->dictionaries += name;
0159         }
0160 
0161     d->blockCurrentListSignal = true;
0162     setWordList(d->current);
0163     d->blockCurrentListSignal = false;
0164     Q_EMIT wordListsChanged(wordLists());
0165     Q_EMIT currentListChanged(d->current);
0166 }
0167 
0168 bool WordCompletion::setWordList(const QString &wordlist)
0169 {
0170     if (d->wordsToSave)
0171         save();
0172     d->wordsToSave = false;
0173 
0174     d->map.clear();
0175     bool result = d->dictDetails.contains(wordlist);
0176     if (result)
0177         d->current = wordlist;
0178     else {
0179         if (d->dictionaries.isEmpty())
0180             return false;
0181         d->current = d->dictionaries[0];
0182     }
0183 
0184     QString filename = d->dictDetails[d->current].filename;
0185     QString dictionaryFile = QStandardPaths::locate(QStandardPaths::AppDataLocation, filename);
0186     QFile file(dictionaryFile);
0187     if (file.exists() && file.open(QIODevice::ReadOnly)) {
0188         QTextStream stream(&file);
0189         stream.setEncoding(QStringConverter::Utf8);
0190         if (!stream.atEnd()) {
0191             if (stream.readLine() == QLatin1String("WPDictFile")) {
0192                 while (!stream.atEnd()) {
0193                     QString s = stream.readLine();
0194                     if (!(s.isNull() || s.isEmpty())) {
0195                         QStringList list = s.split(QLatin1Char('\t'));
0196                         bool ok;
0197                         int weight = list[1].toInt(&ok);
0198                         if (ok && (weight > 0))
0199                             d->map[list[0]] = weight;
0200                     }
0201                 }
0202             }
0203         }
0204         file.close();
0205     }
0206     if (!d->blockCurrentListSignal)
0207         Q_EMIT currentListChanged(d->current);
0208     d->lastText.clear();
0209     d->wordsToSave = false;
0210     return result;
0211 }
0212 
0213 void WordCompletion::addSentence(const QString &sentence)
0214 {
0215     const QStringList words = sentence.split(QRegularExpression(QStringLiteral("\\W")));
0216 
0217     QStringList::ConstIterator it;
0218     for (it = words.constBegin(); it != words.constEnd(); ++it) {
0219         if (!(*it).contains(QRegularExpression(QStringLiteral("\\d|_")))) {
0220             QString key = (*it).toLower();
0221             if (d->map.contains(key))
0222                 d->map[key] += 1;
0223             else
0224                 d->map[key] = 1;
0225             if (d->addedWords.contains(key))
0226                 d->addedWords[key] += 1;
0227             else
0228                 d->addedWords[key] = 1;
0229         }
0230     }
0231     d->wordsToSave = true;
0232 }
0233 
0234 void WordCompletion::save()
0235 {
0236     if (d->wordsToSave) {
0237         QString filename = d->dictDetails[d->current].filename;
0238         QString dictionaryFile = QStandardPaths::locate(QStandardPaths::AppDataLocation, filename);
0239         QFile file(dictionaryFile);
0240         if (!file.exists())
0241             return;
0242         if (!file.open(QIODevice::WriteOnly))
0243             return;
0244 
0245         QTextStream stream(&file);
0246         stream.setEncoding(QStringConverter::Utf8);
0247         stream << "WPDictFile\n";
0248         QMap<QString, int>::ConstIterator it;
0249         for (it = d->map.constBegin(); it != d->map.constEnd(); ++it) {
0250             if (d->addedWords.contains(it.key())) {
0251                 stream << it.key() << "\t" << d->addedWords[it.key()] << "\t1\n";
0252                 stream << it.key() << "\t" << it.value() - d->addedWords[it.key()] << "\t2\n";
0253             } else
0254                 stream << it.key() << "\t" << it.value() << "\t2\n";
0255         }
0256         file.close();
0257         d->wordsToSave = false;
0258     }
0259 }