20 ** |
20 ** |
21 ** If you have questions regarding the use of this file, please contact |
21 ** If you have questions regarding the use of this file, please contact |
22 ** Nokia at developer.feedback@nokia.com. |
22 ** Nokia at developer.feedback@nokia.com. |
23 ** |
23 ** |
24 ****************************************************************************/ |
24 ****************************************************************************/ |
|
25 #include "hbinputextrauserdictionary.h" |
|
26 #include "hbinputextrauserdictionary_p.h" |
|
27 |
25 #include <qbytearray.h> // For memmove() |
28 #include <qbytearray.h> // For memmove() |
26 |
29 |
27 #include <QFile> |
30 #include <QFile> |
28 #include <QDir> |
31 #include <QDir> |
29 #include <QSharedMemory> |
32 #include <QSharedMemory> |
30 #include <QVector> |
33 #include <QVector> |
31 |
34 |
32 #include "hbinputextrauserdictionary.h" |
|
33 #include "hbinputextrauserdictionary_p.h" |
|
34 #include "hbinputsettingproxy.h" |
35 #include "hbinputsettingproxy.h" |
35 |
36 |
36 const int HbExtraDictMaxFrequency = 255; |
37 const int HbExtraDictMaxFrequency = 255; |
37 |
38 |
38 /*! |
39 /*! |
109 return HbInputSettingProxy::extraDictionaryPath() + QDir::separator() + name() + QString(KExtraFileExt); |
110 return HbInputSettingProxy::extraDictionaryPath() + QDir::separator() + name() + QString(KExtraFileExt); |
110 } |
111 } |
111 |
112 |
112 bool HbExtraUserDictionaryPrivate::save(QString fileName) |
113 bool HbExtraUserDictionaryPrivate::save(QString fileName) |
113 { |
114 { |
114 QFile file(fileName); |
115 QFile file(fileName); |
115 if (file.open( QIODevice::WriteOnly )) { |
116 if (file.open(QIODevice::WriteOnly)) { |
116 file.write((char*)&id, sizeof(int)); |
117 file.write((char *)&id, sizeof(int)); |
117 if (sharedMemory.isAttached()) { |
118 if (sharedMemory.isAttached()) { |
118 file.write((char*)sharedMemory.data(), KExtraUDBlockSize); |
119 file.write((char *)sharedMemory.data(), KExtraUDBlockSize); |
119 } |
120 } |
120 file.close(); |
121 file.close(); |
121 dataHeader()->modified = false; |
122 dataHeader()->modified = false; |
122 return true; |
123 return true; |
123 } |
124 } |
124 |
125 |
125 return false; |
126 return false; |
126 } |
127 } |
127 |
128 |
128 int HbExtraUserDictionaryPrivate::findFirstMatch(int start, int end, const QString& searchString, int knownMatch, Qt::CaseSensitivity caseSensitivity) const |
129 int HbExtraUserDictionaryPrivate::findFirstMatch(int start, int end, const QString &searchString, int knownMatch, Qt::CaseSensitivity caseSensitivity) const |
129 { |
130 { |
130 HbExtraUDDirectoryEntry *dir = directory(); |
131 HbExtraUDDirectoryEntry *dir = directory(); |
131 QChar *data = dataArea(); |
132 QChar *data = dataArea(); |
132 |
133 |
133 if (start >= end) { |
134 if (start >= end) { |
139 |
140 |
140 int half = (start + end) / 2; |
141 int half = (start + end) / 2; |
141 if (QString(&data[dir[half].start], dir[half].length).startsWith(searchString, Qt::CaseInsensitive)) { |
142 if (QString(&data[dir[half].start], dir[half].length).startsWith(searchString, Qt::CaseInsensitive)) { |
142 knownMatch = half; |
143 knownMatch = half; |
143 return findFirstMatch(start, half, searchString, knownMatch, caseSensitivity); |
144 return findFirstMatch(start, half, searchString, knownMatch, caseSensitivity); |
144 } |
145 } |
145 |
146 |
146 if (compareWords(half, searchString, caseSensitivity) > 0) { |
147 if (compareWords(half, searchString, caseSensitivity) > 0) { |
147 return findFirstMatch(half + 1, end, searchString, knownMatch, caseSensitivity); |
148 return findFirstMatch(half + 1, end, searchString, knownMatch, caseSensitivity); |
148 } |
149 } |
149 |
150 |
161 // First move trailing directory entries and the beginning of the data area |
162 // First move trailing directory entries and the beginning of the data area |
162 // by size of one dictionary entry item. |
163 // by size of one dictionary entry item. |
163 memmove((char *)&dir[index], (char *)&dir[index + 1], (dataHeader()->numWords - index) * sizeof(HbExtraUDDirectoryEntry) + start * sizeof(QChar)); |
164 memmove((char *)&dir[index], (char *)&dir[index + 1], (dataHeader()->numWords - index) * sizeof(HbExtraUDDirectoryEntry) + start * sizeof(QChar)); |
164 |
165 |
165 // Then move trailing part of the data area. |
166 // Then move trailing part of the data area. |
166 memmove(((char*)&data[start]) - sizeof(HbExtraUDDirectoryEntry), (char*)&data[start + length], (dataHeader()->dataSize - (start + length)) * 2); |
167 memmove(((char *)&data[start]) - sizeof(HbExtraUDDirectoryEntry), (char *)&data[start + length], (dataHeader()->dataSize - (start + length)) * 2); |
167 |
168 |
168 // Update word count. |
169 // Update word count. |
169 dataHeader()->numWords--; |
170 dataHeader()->numWords--; |
170 dataHeader()->dataSize -= length; |
171 dataHeader()->dataSize -= length; |
171 dataHeader()->modified = true; |
172 dataHeader()->modified = true; |
172 |
173 |
173 // Then update remaining dictionary entries. |
174 // Then update remaining dictionary entries. |
174 const int rounds = dataHeader()->numWords; |
175 const int rounds = dataHeader()->numWords; |
175 for (int i = index; i < rounds; i++) { |
176 for (int i = index; i < rounds; i++) { |
176 dir[i].start -= length; |
177 dir[i].start -= length; |
177 } |
178 } |
178 } |
179 } |
179 |
180 |
180 void HbExtraUserDictionaryPrivate::addEntry(int index, const QString& newWord) |
181 void HbExtraUserDictionaryPrivate::addEntry(int index, const QString &newWord) |
181 { |
182 { |
182 HbExtraUDDirectoryEntry *dir = directory(); |
183 HbExtraUDDirectoryEntry *dir = directory(); |
183 QChar *data = dataArea(); |
184 QChar *data = dataArea(); |
184 |
185 |
185 const int origNumWords = dataHeader()->numWords; |
186 const int origNumWords = dataHeader()->numWords; |
186 if (origNumWords > 0) { |
187 if (origNumWords > 0) { |
187 if (index < origNumWords) { |
188 if (index < origNumWords) { |
188 // First move the trailing part of the data area to make space for the new word. |
189 // First move the trailing part of the data area to make space for the new word. |
189 memmove((char*)&data[dir[index].start + newWord.size()] + sizeof(HbExtraUDDirectoryEntry), |
190 memmove((char *)&data[dir[index].start + newWord.size()] + sizeof(HbExtraUDDirectoryEntry), |
190 (char*)(&data[dir[index].start]), |
191 (char *)(&data[dir[index].start]), |
191 (dataHeader()->dataSize - dir[index].start) * 2); |
192 (dataHeader()->dataSize - dir[index].start) * 2); |
192 |
193 |
193 // Then move the trailing part of the dictionary and the leading part of the data area. |
194 // Then move the trailing part of the dictionary and the leading part of the data area. |
194 memmove((char*)(&dir[index + 1]), |
195 memmove((char *)(&dir[index + 1]), |
195 (char*)(&dir[index]), |
196 (char *)(&dir[index]), |
196 ((dataHeader()->numWords - index) * sizeof(HbExtraUDDirectoryEntry)) + (dir[index].start * 2)); |
197 ((dataHeader()->numWords - index) * sizeof(HbExtraUDDirectoryEntry)) + (dir[index].start * 2)); |
197 } else { |
198 } else { |
198 // This will be the last one. Just make room for new directory entry. |
199 // This will be the last one. Just make room for new directory entry. |
199 memmove((char*)data + sizeof(HbExtraUDDirectoryEntry), |
200 memmove((char *)data + sizeof(HbExtraUDDirectoryEntry), |
200 (char*)data, |
201 (char *)data, |
201 dataHeader()->dataSize * 2); |
202 dataHeader()->dataSize * 2); |
202 } |
203 } |
203 } |
204 } |
204 |
205 |
205 // Update word count. |
206 // Update word count. |
206 dataHeader()->numWords++; |
207 dataHeader()->numWords++; |
207 dataHeader()->dataSize += newWord.size(); |
208 dataHeader()->dataSize += newWord.size(); |
223 } |
224 } |
224 |
225 |
225 // Then update remaining dictionary entries. |
226 // Then update remaining dictionary entries. |
226 const int rounds = dataHeader()->numWords; |
227 const int rounds = dataHeader()->numWords; |
227 for (int i = index + 1; i < rounds; i++) { |
228 for (int i = index + 1; i < rounds; i++) { |
228 dir[i].start += newWord.size(); |
229 dir[i].start += newWord.size(); |
229 } |
230 } |
230 } |
231 } |
231 |
232 |
232 int HbExtraUserDictionaryPrivate::findWord(int startIndex, int endIndex, const QString& newWord, Qt::CaseSensitivity caseSensitivity) const |
233 int HbExtraUserDictionaryPrivate::findWord(int startIndex, int endIndex, const QString &newWord, Qt::CaseSensitivity caseSensitivity) const |
233 { |
234 { |
234 if (startIndex >= endIndex) { |
235 if (startIndex >= endIndex) { |
235 if (startIndex < dataHeader()->numWords && compareWords(startIndex, newWord, caseSensitivity) == 0) { |
236 if (startIndex < dataHeader()->numWords && compareWords(startIndex, newWord, caseSensitivity) == 0) { |
236 return startIndex; |
237 return startIndex; |
237 } |
238 } |
273 } else { |
274 } else { |
274 return findIndexForNewWord(start, half, newWord); |
275 return findIndexForNewWord(start, half, newWord); |
275 } |
276 } |
276 } |
277 } |
277 |
278 |
278 int HbExtraUserDictionaryPrivate::compareWords(int index, const QString& otherWord, Qt::CaseSensitivity caseSensitivity) const |
279 int HbExtraUserDictionaryPrivate::compareWords(int index, const QString &otherWord, Qt::CaseSensitivity caseSensitivity) const |
279 { |
280 { |
280 HbExtraUDDirectoryEntry *dir = directory(); |
281 HbExtraUDDirectoryEntry *dir = directory(); |
281 QChar *data = dataArea(); |
282 QChar *data = dataArea(); |
282 |
283 |
283 const int start = dir[index].start; |
284 const int start = dir[index].start; |
284 const int rounds = (dir[index].length > otherWord.size() ? otherWord.size() : dir[index].length); |
285 const int rounds = (dir[index].length > otherWord.size() ? otherWord.size() : dir[index].length); |
285 if (caseSensitivity == Qt::CaseSensitive) { |
286 if (caseSensitivity == Qt::CaseSensitive) { |
286 for (int i = 0; i < rounds; i++) { |
287 for (int i = 0; i < rounds; i++) { |
287 if (data[start + i] == otherWord[i]) { |
288 if (data[start + i] == otherWord[i]) { |
288 continue; |
289 continue; |
289 } |
290 } |
290 |
291 |
291 if (otherWord[i] > data[start + i]) { |
292 if (otherWord[i] > data[start + i]) { |
292 return 1; |
293 return 1; |
293 } |
294 } |
294 |
295 |
295 return -1; |
296 return -1; |
296 } |
297 } |
297 } else { |
298 } else { |
298 for (int i = 0; i < rounds; i++) { |
299 for (int i = 0; i < rounds; i++) { |
299 if (data[start + i].toCaseFolded() == otherWord[i].toCaseFolded()) { |
300 if (data[start + i].toCaseFolded() == otherWord[i].toCaseFolded()) { |
300 continue; |
301 continue; |
301 } |
302 } |
302 |
303 |
303 if (otherWord[i].toCaseFolded() > data[start + i].toCaseFolded()) { |
304 if (otherWord[i].toCaseFolded() > data[start + i].toCaseFolded()) { |
304 return 1; |
305 return 1; |
305 } |
306 } |
306 |
307 |
307 return -1; |
308 return -1; |
308 } |
309 } |
309 } |
310 } |
310 |
311 |
311 if (dir[index].length == otherWord.size()) { |
312 if (dir[index].length == otherWord.size()) { |
312 return 0; // Match! |
313 return 0; // Match! |
313 } |
314 } |
314 |
315 |
570 } |
571 } |
571 |
572 |
572 /*! |
573 /*! |
573 Returns pointer to host prediction engine. |
574 Returns pointer to host prediction engine. |
574 */ |
575 */ |
575 HbPredictionBase* HbExtraUserDictionary::hostEngine() const |
576 HbPredictionBase *HbExtraUserDictionary::hostEngine() const |
576 { |
577 { |
577 Q_D(const HbExtraUserDictionary); |
578 Q_D(const HbExtraUserDictionary); |
578 return d->hostEngine; |
579 return d->hostEngine; |
579 } |
580 } |
580 |
581 |
581 /*! |
582 /*! |
582 Sets host prediction engine. |
583 Sets host prediction engine. |
583 */ |
584 */ |
584 void HbExtraUserDictionary::setHostEngine(HbPredictionBase* host) |
585 void HbExtraUserDictionary::setHostEngine(HbPredictionBase *host) |
585 { |
586 { |
586 Q_D(HbExtraUserDictionary); |
587 Q_D(HbExtraUserDictionary); |
587 d->hostEngine = host; |
588 d->hostEngine = host; |
588 } |
589 } |
589 |
590 |
590 /*! |
591 /*! |
591 Loads dictionary from disk. |
592 Loads dictionary from disk. |
592 */ |
593 */ |
593 bool HbExtraUserDictionary::load(const QString& nameOfTheFile) |
594 bool HbExtraUserDictionary::load(const QString &nameOfTheFile) |
594 { |
595 { |
595 Q_D(HbExtraUserDictionary); |
596 Q_D(HbExtraUserDictionary); |
596 |
597 |
597 QString realFileName = nameOfTheFile; |
598 QString realFileName = nameOfTheFile; |
598 if (realFileName.size() == 0) { |
599 if (realFileName.size() == 0) { |
599 realFileName = fileName(); |
600 realFileName = fileName(); |
600 } |
601 } |
601 |
602 |
602 if (attach()) { |
603 if (attach()) { |
603 d->lock(); |
604 d->lock(); |
604 QFile file(realFileName); |
605 QFile file(realFileName); |
605 if (file.open( QIODevice::ReadOnly )) { |
606 if (file.open(QIODevice::ReadOnly)) { |
606 int numUsers = d->dataHeader()->numUsers; |
607 int numUsers = d->dataHeader()->numUsers; |
607 file.read((char*)&d->id, sizeof(int)); |
608 file.read((char *)&d->id, sizeof(int)); |
608 file.read((char*)d->sharedMemory.data(), KExtraUDBlockSize); |
609 file.read((char *)d->sharedMemory.data(), KExtraUDBlockSize); |
609 file.close(); |
610 file.close(); |
610 d->dataHeader()->numUsers = numUsers; |
611 d->dataHeader()->numUsers = numUsers; |
611 d->unlock(); |
612 d->unlock(); |
612 return true; |
613 return true; |
613 } |
614 } |
614 d->unlock(); |
615 d->unlock(); |
615 } |
616 } |
616 |
617 |
617 return false; |
618 return false; |
618 } |
619 } |
619 |
620 |
620 /*! |
621 /*! |
621 Saves dictionary to disk. If no file name is give, fileName() will |
622 Saves dictionary to disk. If no file name is give, fileName() will |
622 be used. |
623 be used. |
623 |
624 |
624 \sa fileName |
625 \sa fileName |
625 */ |
626 */ |
626 bool HbExtraUserDictionary::save(const QString& nameOfTheFile) |
627 bool HbExtraUserDictionary::save(const QString &nameOfTheFile) |
627 { |
628 { |
628 Q_D(HbExtraUserDictionary); |
629 Q_D(HbExtraUserDictionary); |
629 |
630 |
630 QString realFileName = nameOfTheFile; |
631 QString realFileName = nameOfTheFile; |
631 if (realFileName.size() == 0) { |
632 if (realFileName.size() == 0) { |
632 realFileName = fileName(); |
633 realFileName = fileName(); |
633 } |
634 } |
634 |
635 |
635 bool ret = false; |
636 bool ret = false; |
636 d->lock(); |
637 d->lock(); |
637 ret = d->save(realFileName); |
638 ret = d->save(realFileName); |
638 d->unlock(); |
639 d->unlock(); |
639 |
640 |
640 return ret; |
641 return ret; |
641 } |
642 } |
642 |
643 |
643 /*! |
644 /*! |
644 Sets dictionary id. |
645 Sets dictionary id. |
645 */ |
646 */ |
758 } |
759 } |
759 |
760 |
760 /*! |
761 /*! |
761 Increases word frequency counter for given word if it is in the dictionary. |
762 Increases word frequency counter for given word if it is in the dictionary. |
762 */ |
763 */ |
763 void HbExtraUserDictionary::incrementUseCount(const QString& word) |
764 void HbExtraUserDictionary::incrementUseCount(const QString &word) |
764 { |
765 { |
765 Q_D(const HbExtraUserDictionary); |
766 Q_D(const HbExtraUserDictionary); |
766 |
767 |
767 if (d->dataHeader()->numWords) { |
768 if (d->dataHeader()->numWords) { |
768 HbExtraUDDirectoryEntry *dir = d->directory(); |
769 HbExtraUDDirectoryEntry *dir = d->directory(); |
769 |
770 |
770 int first = d->findFirstMatch(0, d->dataHeader()->numWords - 1, word); |
771 int first = d->findFirstMatch(0, d->dataHeader()->numWords - 1, word); |
771 if (first >= 0 && dir[first].frequency < HbExtraDictMaxFrequency) { |
772 if (first >= 0 && dir[first].frequency < HbExtraDictMaxFrequency) { |
772 dir[first].frequency++; |
773 dir[first].frequency++; |
773 d->dataHeader()->modified = true; |
774 d->dataHeader()->modified = true; |
774 } |
775 } |
775 } |
776 } |
776 } |
777 } |
777 |
778 |
778 /*! |
779 /*! |
779 Returns true if given word exits in the dictionary. |
780 Returns true if given word exits in the dictionary. |
780 */ |
781 */ |
781 bool HbExtraUserDictionary::hasWord(const QString& word, Qt::CaseSensitivity caseSensitivity) const |
782 bool HbExtraUserDictionary::hasWord(const QString &word, Qt::CaseSensitivity caseSensitivity) const |
782 { |
783 { |
783 Q_D(const HbExtraUserDictionary); |
784 Q_D(const HbExtraUserDictionary); |
784 |
785 |
785 if (d->dataHeader()->numWords) { |
786 if (d->dataHeader()->numWords) { |
786 QChar *data = d->dataArea(); |
787 QChar *data = d->dataArea(); |
787 HbExtraUDDirectoryEntry *dir = d->directory(); |
788 HbExtraUDDirectoryEntry *dir = d->directory(); |
788 int first = d->findFirstMatch(0, d->dataHeader()->numWords - 1, word,-1, caseSensitivity); |
789 int first = d->findFirstMatch(0, d->dataHeader()->numWords - 1, word, -1, caseSensitivity); |
789 if (first >= 0) { |
790 if (first >= 0) { |
790 if (caseSensitivity == Qt::CaseSensitive) { |
791 if (caseSensitivity == Qt::CaseSensitive) { |
791 if (QString(&data[dir[first].start], dir[first].length) == word) { |
792 if (QString(&data[dir[first].start], dir[first].length) == word) { |
792 return true; |
793 return true; |
|
794 } |
|
795 |
|
796 const int rounds = d->dataHeader()->numWords; |
|
797 for (int i = first + 1; i <= rounds; i++) { |
|
798 QString candidate(&data[dir[i].start], dir[i].length); |
|
799 if (candidate.startsWith(word, Qt::CaseInsensitive)) { |
|
800 if (candidate == word) { |
|
801 return true; |
|
802 } |
|
803 } else { |
|
804 break; |
793 } |
805 } |
794 |
806 } |
795 const int rounds = d->dataHeader()->numWords; |
|
796 for (int i = first + 1; i <= rounds; i++) { |
|
797 QString candidate(&data[dir[i].start], dir[i].length); |
|
798 if (candidate.startsWith(word, Qt::CaseInsensitive)) { |
|
799 if (candidate == word) { |
|
800 return true; |
|
801 } |
|
802 } else { |
|
803 break; |
|
804 } |
|
805 } |
|
806 } else { |
807 } else { |
807 if (QString(&data[dir[first].start], dir[first].length).toCaseFolded() == word.toCaseFolded()) { |
808 if (QString(&data[dir[first].start], dir[first].length).toCaseFolded() == word.toCaseFolded()) { |
808 return true; |
809 return true; |
809 } |
810 } |
810 const int rounds = d->dataHeader()->numWords; |
811 const int rounds = d->dataHeader()->numWords; |