diff options
| author | 2015-09-13 01:15:07 +0200 | |
|---|---|---|
| committer | 2015-09-13 01:15:07 +0200 | |
| commit | 7635196b727fb0757122643af992a974b008b4ff (patch) | |
| tree | fb97e70cee378bdeabf5c0fb546677394a4aa929 /src | |
| parent | Merge branch 'master' of https://github.com/kvirc/KVIrc (diff) | |
| download | KVIrc-7635196b727fb0757122643af992a974b008b4ff.tar.gz KVIrc-7635196b727fb0757122643af992a974b008b4ff.tar.bz2 KVIrc-7635196b727fb0757122643af992a974b008b4ff.zip | |
Improve spellchecker support.
Better highlight spelling errors (fixes #1562)
Add $spellchecker.suggestions().
Fix the KviInputEditor context popup clipboard label with Qt5.
Improve KviInputEditor context popup readability.
...
Diffstat (limited to 'src')
| -rw-r--r-- | src/kvirc/kvs/KviKvsArray.cpp | 6 | ||||
| -rw-r--r-- | src/kvirc/kvs/KviKvsArray.h | 5 | ||||
| -rw-r--r-- | src/kvirc/ui/KviInputEditor.cpp | 481 | ||||
| -rw-r--r-- | src/kvirc/ui/KviInputEditor.h | 15 | ||||
| -rw-r--r-- | src/kvirc/ui/KviTextIconWindow.cpp | 37 | ||||
| -rw-r--r-- | src/kvirc/ui/KviWindow.h | 15 | ||||
| -rw-r--r-- | src/modules/spellchecker/libkvispellchecker.cpp | 68 |
7 files changed, 443 insertions, 184 deletions
diff --git a/src/kvirc/kvs/KviKvsArray.cpp b/src/kvirc/kvs/KviKvsArray.cpp index bd1b268ed..7e1028871 100644 --- a/src/kvirc/kvs/KviKvsArray.cpp +++ b/src/kvirc/kvs/KviKvsArray.cpp @@ -181,6 +181,12 @@ void KviKvsArray::set(kvs_uint_t uIdx, KviKvsVariant * pVal) } } +void KviKvsArray::append(KviKvsVariant * pVal) +{ + set(m_uSize,pVal); +} + + KviKvsVariant * KviKvsArray::getAt(kvs_uint_t uIdx) { if(uIdx >= m_uSize) diff --git a/src/kvirc/kvs/KviKvsArray.h b/src/kvirc/kvs/KviKvsArray.h index 09cfe1813..3c74cd7ce 100644 --- a/src/kvirc/kvs/KviKvsArray.h +++ b/src/kvirc/kvs/KviKvsArray.h @@ -83,6 +83,11 @@ public: void set(kvs_uint_t uIdx, KviKvsVariant * pVal); /** + * Appends a variant to this array. + */ + void append(KviKvsVariant * pVal); + + /** * \brief Returns the element at the given index * \param uIdx The index of the element to retrieve * \return KviKvsVariant * diff --git a/src/kvirc/ui/KviInputEditor.cpp b/src/kvirc/ui/KviInputEditor.cpp index 3e1bb870d..2f464a6ec 100644 --- a/src/kvirc/ui/KviInputEditor.cpp +++ b/src/kvirc/ui/KviInputEditor.cpp @@ -48,6 +48,7 @@ #include "KviUserListView.h" #include "KviShortcut.h" #include "KviKvsEventTriggers.h" +#include "KviChannelWindow.h" #include <QClipboard> #include <QLabel> @@ -65,6 +66,7 @@ #include <QInputContext> #endif #include <QMenu> +#include <QWidgetAction> #include <QStyleFactory> #include <QTextBoundaryFinder> @@ -81,83 +83,6 @@ extern KviColorWindow * g_pColorWindow; extern QPixmap * g_pShadedChildGlobalDesktopBackground; #endif -#ifdef COMPILE_ENCHANT_SUPPORT -static void checkWordSpelling(const QString& szWord, QString* szResult, const QString& szToAppend) { - // TODO: If szWord is a nick of a person in this channel, don't mark it as a spelling mistake. - KviKvsVariant bCorrect; - if (szWord.isEmpty()) { - bCorrect.setBoolean(true); - } else { - KviKvsVariantList params(new KviKvsVariant(szWord)); - KviKvsScript::evaluate("$spellchecker.check($0)", NULL, ¶ms, &bCorrect); - } - if (!bCorrect.asBoolean()) { - *szResult += QChar(KviControlCodes::SpellingMistake); - } - *szResult += szToAppend; - if (!bCorrect.asBoolean()) { - *szResult += QChar(KviControlCodes::SpellingMistake); - } -} -// Insert KviControlCodes::SpellingMistake around every word with a spelling mistake -static QString substituteSpelling(const QString& szText) { - // TODO: If no dictionaries are enabled, just return szText. - QString szResult; - // QTextBoundaryFinder will want a string without embedded control codes (bold, colors, etc), so strip them from string. - QString szCleanText; - // But need to remember where they were, so that when copying parts of string into result with or without spelling mistake markers, control codes will be copied too. - // For Nth character boundary in clean text, that character boundary is at N+offsets[N] position in original text. - QList<int> offsets; - int iCurrentOffset = 0; - offsets.push_back(iCurrentOffset); - for (int i = 0; i < szText.length(); ++i) { - QChar c = szText[i]; - switch (c.unicode()) { - case KviControlCodes::Bold: - case KviControlCodes::Underline: - case KviControlCodes::Reset: - case KviControlCodes::Reverse: - case KviControlCodes::CryptEscape: - case KviControlCodes::Icon: - iCurrentOffset++; - break; - case KviControlCodes::Color: - { - i++; // \0x03 character itself - iCurrentOffset++; - if (i >= szText.length()) break; - unsigned char uFore; - unsigned char uBack; - int j = KviControlCodes::getUnicodeColorBytes(szText, i, &uFore, &uBack); - iCurrentOffset += j - i; - i = j - 1; // It will now ++i again in for-loop - break; - } - default: - szCleanText += c; - offsets.push_back(iCurrentOffset); - } - } - - QTextBoundaryFinder finder(QTextBoundaryFinder::Word, szCleanText); - finder.toStart(); - int iPreviousBoundary = 0; - while (true) { - int iBoundary = finder.toNextBoundary(); - if (iBoundary == -1) { - break; - } - if (!finder.boundaryReasons()) { - continue; - } - checkWordSpelling(szCleanText.mid(iPreviousBoundary, iBoundary - iPreviousBoundary), &szResult, szText.mid(iPreviousBoundary + offsets[iPreviousBoundary], iBoundary + offsets[iBoundary] - iPreviousBoundary - offsets[iPreviousBoundary])); - iPreviousBoundary = iBoundary; - } - checkWordSpelling(szCleanText.mid(iPreviousBoundary), &szResult, szText.mid(iPreviousBoundary + offsets[iPreviousBoundary])); - return szResult; -} -#endif - //static members initialization int KviInputEditor::g_iInputInstances = 0; int KviInputEditor::g_iInputFontCharWidth[256]; @@ -169,8 +94,9 @@ int KviInputEditor::g_iCachedHeight = 0; KviInputEditor::KviInputEditor(QWidget * pPar, KviWindow * pWnd, KviUserListView * pView) : QWidget(pPar) { - ++g_iInputInstances; + g_iInputInstances++; setObjectName("input_widget"); + m_pIconMenu = 0; m_pInputParent = pPar; m_iMaxBufferSize = KVI_INPUT_MAX_BUFFER_SIZE; @@ -184,6 +110,7 @@ KviInputEditor::KviInputEditor(QWidget * pPar, KviWindow * pWnd, KviUserListView m_iIMLength = 0; //Length of the preedit string. m_iIMSelectionBegin = 0; //Index of the start of the selection in preedit string. m_iIMSelectionLength = 0; //Length of the selection in preedit string. + m_bTextDisplayBufferDirty = true; m_bCursorOn = false; //Cursor state m_iCursorTimer = 0; //Timer that iverts the cursor state @@ -426,6 +353,199 @@ void KviInputEditor::paintEvent(QPaintEvent *) drawContents(&p); } +void KviInputEditor::checkWordSpelling(const QString &szWord,QString * pszResult) +{ +#ifdef COMPILE_ENCHANT_SUPPORT + // TODO: If szWord is a nick of a person in this channel, don't mark it as a spelling mistake. + if(szWord.isEmpty()) + return; + + if(m_pKviWindow && m_pKviWindow->isChannel()) + { + KviChannelWindow * c = static_cast<KviChannelWindow *>(m_pKviWindow); + if(c->isOn(szWord)) + { + // a nickname on channel + pszResult->append(szWord); + return; + } + } + + KviKvsVariant bCorrect; + KviKvsVariantList params(new KviKvsVariant(szWord)); + KviKvsScript::evaluate("$spellchecker.check($0)", NULL, ¶ms, &bCorrect); + + if(bCorrect.asBoolean()) + { + pszResult->append(szWord); + return; + } + + pszResult->append(QChar(KviControlCodes::SpellingMistake)); + pszResult->append(szWord); + pszResult->append(QChar(KviControlCodes::SpellingMistake)); +#endif +} + +// Insert KviControlCodes::SpellingMistake around every word with a spelling mistake + +QString KviInputEditor::checkSpelling(const QString &szText) +{ +#ifdef COMPILE_ENCHANT_SUPPORT + if(szText.isEmpty()) + return szText; + + const QChar * b = szText.unicode(); + const QChar * p = b; + const QChar * e = b + szText.length(); + + QString szResult; + + // first of all skip any spaces + while(p < e) + { + if(!p->isSpace()) + break; + p++; + } + + if(p > b) + szResult.append(b,p-b); + + // check if the buffer starts with a command + if((p < e) && (p->unicode() == '/')) + { + const QChar * pCommandBlockBegin = p; + + // a command. + p++; + // skip spaces + while(p < e) + { + if(!p->isSpace()) + break; + p++; + } + + + const QChar * pCommandWordBegin = p; + + // skip command word + while(p < e) + { + if(p->isSpace()) + break; + p++; + } + + if(p > pCommandWordBegin) + { + QString szCommand = QString(pCommandWordBegin,p - pCommandWordBegin).toLower(); + + // common commands that may be spellcheckable + static QString szMe("me"); + static QString szMsg("msg"); + static QString szSay("msg"); + static QString szPart("part"); + static QString szQuit("quit"); + + if( + (szCommand != szMe) && + (szCommand != szMsg) && + (szCommand != szSay) && + (szCommand != szPart) && + (szCommand != szQuit) + ) + return szText; // the command parameters usually have no spellcheckable text + + } + + szResult.append(pCommandBlockBegin,p-pCommandBlockBegin); + } + + const QChar * pNonWordBegin = NULL; + const QChar * pWordBegin = NULL; + + while(p < e) + { + switch(p->category()) + { + case QChar::Letter_Uppercase: + case QChar::Letter_Lowercase: + case QChar::Letter_Titlecase: + case QChar::Letter_Modifier: + case QChar::Letter_Other: + if(!pWordBegin) + { + // a word starts here + if(pNonWordBegin) + { + szResult.append(pNonWordBegin,p - pNonWordBegin); + pNonWordBegin = NULL; + } + pWordBegin = p; + } else { + // we're already in a word, go on + } + break; + case QChar::Number_DecimalDigit: + case QChar::Number_Letter: + case QChar::Number_Other: + if(!pWordBegin && !pNonWordBegin) + pNonWordBegin = p; // start a non-word + // else just go ahead keeping current state + break; + default: + if(!pNonWordBegin) + { + if(p->unicode() == '\'') + { + // special case for the ' character which can be part of a word in many languages + if(!pWordBegin) // word not started + pNonWordBegin = p; // then start a nonwod + // else keep current state + } else { + if(pWordBegin) + { + // a word to spellcheck + if((p-1)->unicode() == '\'') + { + // exclude the trailing ' from the word + checkWordSpelling(QString(pWordBegin,p - 1 - pWordBegin),&szResult); + szResult.append(QChar('\'')); + } else { + checkWordSpelling(QString(pWordBegin,p - pWordBegin),&szResult); + } + pWordBegin = NULL; + } + pNonWordBegin = p; + } + } else { + // already in a non-word + } + break; + } + + p++; + } + + if(pWordBegin) + { + // last word to spellcheck + checkWordSpelling(QString(pWordBegin,p - pWordBegin),&szResult); + } else if(pNonWordBegin) + { + // last non-word + szResult.append(pNonWordBegin,p - pNonWordBegin); + } + + return szResult; +#else + return szText; +#endif +} + + void KviInputEditor::drawContents(QPainter * p) { QRect rect = p->clipRegion().boundingRect(); @@ -438,11 +558,15 @@ void KviInputEditor::drawContents(QPainter * p) m_bCurBold = false; m_bCurUnderline = false; m_bCurSpellingMistake = false; + + if(m_bTextDisplayBufferDirty) + { #ifdef COMPILE_ENCHANT_SUPPORT - m_szTextDisplayBuffer = substituteSpelling(m_szTextBuffer); + m_szTextDisplayBuffer = checkSpelling(m_szTextBuffer); #else - m_szTextDisplayBuffer = m_szTextBuffer; + m_szTextDisplayBuffer = m_szTextBuffer; #endif + } int iTop = KVI_INPUT_XTRAPADDING; int iBottom = rect.height() - KVI_INPUT_XTRAPADDING; @@ -627,8 +751,16 @@ void KviInputEditor::drawTextBlock(QPainter * pa, int iTop, int iBottom, int iCu if(m_bCurSpellingMistake) { - pa->setPen(QPen(Qt::red, 1, Qt::DotLine)); - pa->drawLine(iCurXPos,iTextBaseline + fm->descent() - 1,iCurXPos+m_iBlockWidth,iTextBaseline + fm->descent() - 1); + //pa->setPen(QPen(Qt::red, 1, Qt::DotLine)); + pa->setPen(QPen(Qt::red, 1)); + + // red overlay + int iY = iTextBaseline + fm->descent() - 1; + pa->fillRect(iCurXPos,iTop,m_iBlockWidth,iY - iTop,QColor(255,0,0,30)); // alpha = 30 + pa->drawLine(iCurXPos,iY,iCurXPos+m_iBlockWidth,iY); + + //int iY = iTextBaseline + fm->descent() - 1; + //pa->fillRect(iCurXPos,iY,m_iBlockWidth,iBottom - iY,Qt::red); } } @@ -905,6 +1037,7 @@ void KviInputEditor::mousePressEvent(QMouseEvent * e) szClip.truncate(60); szClip.append("..."); } + szClip.replace(QChar('&'),"&"); szClip.replace(QChar('<'),"<"); szClip.replace(QChar('>'),">"); @@ -928,69 +1061,54 @@ void KviInputEditor::mousePressEvent(QMouseEvent * e) pLabel->setFrameStyle(QFrame::Raised | QFrame::StyledPanel); pLabel->setMargin(5); - delete pLabel; + QWidgetAction * pAction = new QWidgetAction(g_pInputPopup); + pAction->setDefaultWidget(pLabel); + + g_pInputPopup->addAction(pAction); + + //delete pLabel; } } - QAction * pAction = g_pInputPopup->addAction(__tr2qs("&Undo") + ACCEL_KEY(Z),this,SLOT(undo())); - pAction->setEnabled(isUndoAvailable()); - pAction = g_pInputPopup->addAction(__tr2qs("&Redo") + ACCEL_KEY(Y),this,SLOT(redo())); - pAction->setEnabled(isRedoAvailable()); - pAction = g_pInputPopup->addAction(__tr2qs("Cu&t") + ACCEL_KEY(X),this,SLOT(cut())); - pAction->setEnabled(hasSelection()); - pAction = g_pInputPopup->addAction(__tr2qs("&Copy") + ACCEL_KEY(C),this,SLOT(copyToClipboard())); - pAction->setEnabled(hasSelection()); - pAction = g_pInputPopup->addAction(__tr2qs("&Paste") + ACCEL_KEY(V),this,SLOT(pasteClipboardWithConfirmation())); - pAction->setEnabled(!szClip.isEmpty() && !m_bReadOnly); - pAction = g_pInputPopup->addAction(__tr2qs("Paste (Slowly)"),this,SLOT(pasteSlow())); + QAction * pAction = g_pInputPopup->addAction(__tr2qs("&Undo") + ACCEL_KEY(Z),this,SLOT(undo())); + pAction->setEnabled(isUndoAvailable()); + pAction = g_pInputPopup->addAction(__tr2qs("&Redo") + ACCEL_KEY(Y),this,SLOT(redo())); + pAction->setEnabled(isRedoAvailable()); + + g_pInputPopup->addSeparator(); + + pAction = g_pInputPopup->addAction(__tr2qs("Cu&t") + ACCEL_KEY(X),this,SLOT(cut())); + pAction->setEnabled(hasSelection()); + pAction = g_pInputPopup->addAction(__tr2qs("&Copy") + ACCEL_KEY(C),this,SLOT(copyToClipboard())); + pAction->setEnabled(hasSelection()); + pAction = g_pInputPopup->addAction(__tr2qs("&Paste") + ACCEL_KEY(V),this,SLOT(pasteClipboardWithConfirmation())); + pAction->setEnabled(!szClip.isEmpty() && !m_bReadOnly); + pAction = g_pInputPopup->addAction(__tr2qs("Paste (Slowly)"),this,SLOT(pasteSlow())); if ((iType == KviWindow::Channel) || (iType == KviWindow::Query) || (iType == KviWindow::DccChat)) - pAction->setEnabled(!szClip.isEmpty() && !m_bReadOnly); + pAction->setEnabled(!szClip.isEmpty() && !m_bReadOnly); else - pAction->setEnabled(false); - pAction = g_pInputPopup->addAction(__tr2qs("Paste &File") + ACCEL_KEY(L),this,SLOT(pasteFile())); + pAction->setEnabled(false); + pAction = g_pInputPopup->addAction(__tr2qs("Paste &File") + ACCEL_KEY(L),this,SLOT(pasteFile())); if ((iType != KviWindow::Channel) && (iType != KviWindow::Query) && (iType != KviWindow::DccChat)) - pAction->setEnabled(false); + pAction->setEnabled(false); else - pAction->setEnabled(!m_bReadOnly); - if(m_bSpSlowFlag) - { - pAction = g_pInputPopup->addAction(__tr2qs("Stop Paste"),this,SLOT(stopPasteSlow())); /*G&N 2005*/ - } - pAction = g_pInputPopup->addAction(__tr2qs("Clear"),this,SLOT(clear())); - pAction->setEnabled(!m_szTextBuffer.isEmpty() && !m_bReadOnly); - g_pInputPopup->addSeparator(); - pAction = g_pInputPopup->addAction(__tr2qs("Select All"),this,SLOT(selectAll())); - pAction->setEnabled((!m_szTextBuffer.isEmpty())); + pAction->setEnabled(!m_bReadOnly); - g_pInputPopup->addSeparator(); - m_pIconMenu->clear(); - - KviPointerHashTable<QString,KviTextIcon> * d = g_pTextIconManager->textIconDict(); - KviPointerHashTableIterator<QString,KviTextIcon> it(*d); - QStringList szList; + if(m_bSpSlowFlag) + pAction = g_pInputPopup->addAction(__tr2qs("Stop Paste"),this,SLOT(stopPasteSlow())); /*G&N 2005*/ - while(it.current()) - { - szList.append(it.currentKey()); - ++it; - } - szList.sort(); + g_pInputPopup->addSeparator(); - KviTextIcon * pIcon; - QPixmap * pPix; + pAction = g_pInputPopup->addAction(__tr2qs("Clear"),this,SLOT(clear())); + pAction->setEnabled(!m_szTextBuffer.isEmpty() && !m_bReadOnly); + g_pInputPopup->addSeparator(); + pAction = g_pInputPopup->addAction(__tr2qs("Select All"),this,SLOT(selectAll())); + pAction->setEnabled((!m_szTextBuffer.isEmpty())); - for(QStringList::Iterator iter = szList.begin(); iter != szList.end(); ++iter) - { - pIcon = g_pTextIconManager->lookupTextIcon(*iter); - if(pIcon) - { - pPix = pIcon->pixmap(); - if(pPix) m_pIconMenu->addAction(*pPix,*iter); - } - } + g_pInputPopup->addSeparator(); - pAction = g_pInputPopup->addAction(*(g_pIconManager->getSmallIcon(KviIconManager::BigGrin)),__tr2qs("Insert Icon")); - pAction->setMenu(m_pIconMenu); + if(!m_bReadOnly) + pAction = g_pInputPopup->addAction(*(g_pIconManager->getSmallIcon(KviIconManager::BigGrin)),__tr2qs("Insert Icon"),this,SLOT(popupTextIconWindow())); #if (QT_VERSION >= 0x050000) /* @@ -1081,6 +1199,7 @@ void KviInputEditor::removeSelected() addUndo(new EditCommand(EditCommand::RemoveText,m_szTextBuffer.mid(m_iSelectionBegin, m_iSelectionEnd-m_iSelectionBegin+1),m_iSelectionBegin)); m_szTextBuffer.remove(m_iSelectionBegin,(m_iSelectionEnd-m_iSelectionBegin)+1); + m_bTextDisplayBufferDirty = true; moveCursorTo(m_iSelectionBegin,false); selectOneChar(-1); repaintWithCursorOn(); @@ -1098,6 +1217,7 @@ void KviInputEditor::cut() addUndo(new EditCommand(EditCommand::RemoveText,m_szTextBuffer.mid(m_iSelectionBegin, m_iSelectionEnd-m_iSelectionBegin+1),m_iSelectionBegin)); m_szTextBuffer.remove(m_iSelectionBegin,(m_iSelectionEnd-m_iSelectionBegin)+1); + m_bTextDisplayBufferDirty = true; moveCursorTo(m_iSelectionBegin,false); selectOneChar(-1); repaintWithCursorOn(); @@ -1120,6 +1240,7 @@ void KviInputEditor::insertText(const QString & szTxt) m_szTextBuffer.insert(m_iCursorPosition,szText); m_szTextBuffer.truncate(m_iMaxBufferSize); + m_bTextDisplayBufferDirty = true; moveCursorTo(m_iCursorPosition + szText.length()); } else { //Multiline paste...do not execute commands here @@ -1139,6 +1260,7 @@ void KviInputEditor::insertText(const QString & szTxt) m_szTextBuffer.insert(m_iCursorPosition,szBlock); m_szTextBuffer.truncate(m_iMaxBufferSize); + m_bTextDisplayBufferDirty = true; int iPos = 0; while((iPos < ((int)(m_szTextBuffer.length()))) && (m_szTextBuffer[iPos] < 33)) iPos++; @@ -1156,6 +1278,7 @@ int KviInputEditor::replaceSegment(int iStart, int iLength, const QString & szTe addUndo(new EditCommand(EditCommand::InsertText,szText,iStart)); m_szTextBuffer.insert(iStart, szText); m_szTextBuffer.truncate(m_iMaxBufferSize); + m_bTextDisplayBufferDirty = true; repaintWithCursorOn(); int iInsertedLength = szText.length(); @@ -1238,6 +1361,7 @@ void KviInputEditor::clear() { addUndo(new EditCommand(EditCommand::RemoveText,m_szTextBuffer,0)); m_szTextBuffer = ""; + m_bTextDisplayBufferDirty = true; selectOneChar(-1); home(); } @@ -1248,6 +1372,7 @@ void KviInputEditor::setText(const QString szText) addUndo(new EditCommand(EditCommand::RemoveText,szText,0)); m_szTextBuffer = szText; m_szTextBuffer.truncate(m_iMaxBufferSize); + m_bTextDisplayBufferDirty = true; selectOneChar(-1); end(); } @@ -1861,6 +1986,7 @@ void KviInputEditor::replaceWordBeforeCursor(const QString & szWord, const QStri m_szTextBuffer.remove(m_iCursorPosition,szWord.length()); m_szTextBuffer.insert(m_iCursorPosition,szReplacement); m_szTextBuffer.truncate(m_iMaxBufferSize); + m_bTextDisplayBufferDirty = true; moveCursorTo(m_iCursorPosition + szReplacement.length()); if(bRepaint) repaintWithCursorOn(); @@ -2002,6 +2128,7 @@ void KviInputEditor::insertChar(QChar c) } selectOneChar(-1); m_szTextBuffer.insert(m_iCursorPosition,c); + m_bTextDisplayBufferDirty = true; addUndo(new EditCommand(EditCommand::InsertText,c,m_iCursorPosition)); @@ -2116,10 +2243,12 @@ void KviInputEditor::undo() { case EditCommand::InsertText: m_szTextBuffer.remove(pCommand->startPosition(),pCommand->text().length()); + m_bTextDisplayBufferDirty = true; moveCursorTo(pCommand->startPosition()); break; case EditCommand::RemoveText: m_szTextBuffer.insert(pCommand->startPosition(),pCommand->text()); + m_bTextDisplayBufferDirty = true; moveCursorTo(pCommand->startPosition()+pCommand->text().length()); break; default: @@ -2164,10 +2293,12 @@ void KviInputEditor::redo() { case EditCommand::InsertText: m_szTextBuffer.insert(pCommand->startPosition(),pCommand->text()); + m_bTextDisplayBufferDirty = true; moveCursorTo(pCommand->startPosition()+pCommand->text().length()); break; case EditCommand::RemoveText: m_szTextBuffer.remove(pCommand->startPosition(),pCommand->text().length()); + m_bTextDisplayBufferDirty = true; moveCursorTo(pCommand->startPosition()); break; default: @@ -2370,34 +2501,54 @@ void KviInputEditor::insertPlainText() void KviInputEditor::insertIcon() { - if(!m_bReadOnly) - { - insertChar(KviControlCodes::Icon); // THE NEXT WORD IS AN ICON NAME - int iXPos = xPositionFromCharIndex(m_iCursorPosition); - if(iXPos > 24) - iXPos-=24; - if(!g_pTextIconWindow) - g_pTextIconWindow = new KviTextIconWindow(); + if(m_bReadOnly) + return; - if(iXPos+g_pTextIconWindow->width() > width()) - iXPos = width()-(g_pTextIconWindow->width()+2); - g_pTextIconWindow->move(mapToGlobal(QPoint(iXPos,-KVI_TEXTICON_WIN_HEIGHT))); - g_pTextIconWindow->popup(this,false); - } + insertChar(KviControlCodes::Icon); // THE NEXT WORD IS AN ICON NAME + + popupTextIconWindow(); } -void KviInputEditor::insertColor() +void KviInputEditor::popupTextIconWindow() { - if(!m_bReadOnly) + if(m_bReadOnly) + return; + + int iXPos = xPositionFromCharIndex(m_iCursorPosition); + if(iXPos > 24) + iXPos-=24; + if(!g_pTextIconWindow) + g_pTextIconWindow = new KviTextIconWindow(); + + if(iXPos+g_pTextIconWindow->width() > width()) + iXPos = width()-(g_pTextIconWindow->width()+2); + g_pTextIconWindow->move(mapToGlobal(QPoint(iXPos,-KVI_TEXTICON_WIN_HEIGHT))); + g_pTextIconWindow->popup(this,false); +} + + +void KviInputEditor::insertIconCode(const QString &szCode) +{ + if((m_iCursorPosition > 0) && (m_iCursorPosition <= m_szTextBuffer.length())) { - insertChar(KviControlCodes::Color); - int xPos = xPositionFromCharIndex(m_iCursorPosition); - if(xPos > 24)xPos-=24; - if(!g_pColorWindow)g_pColorWindow = new KviColorWindow(); - if(xPos+g_pColorWindow->width() > width())xPos = width()-(g_pColorWindow->width()+2); - g_pColorWindow->move(mapToGlobal(QPoint(xPos,-35))); - g_pColorWindow->popup(this); + QChar c = m_szTextBuffer[m_iCursorPosition]; + if(c.unicode() != KviControlCodes::Icon) + insertChar(KviControlCodes::Icon); } + insertText(szCode); +} + +void KviInputEditor::insertColor() +{ + if(m_bReadOnly) + return; + insertChar(KviControlCodes::Color); + int xPos = xPositionFromCharIndex(m_iCursorPosition); + if(xPos > 24)xPos-=24; + if(!g_pColorWindow)g_pColorWindow = new KviColorWindow(); + if(xPos+g_pColorWindow->width() > width())xPos = width()-(g_pColorWindow->width()+2); + g_pColorWindow->move(mapToGlobal(QPoint(xPos,-35))); + g_pColorWindow->popup(this); } void KviInputEditor::copyInternal() @@ -2445,6 +2596,7 @@ void KviInputEditor::deleteWord() if(!m_szTextBuffer.at(m_iCursorPosition - 1).isSpace()) break; m_szTextBuffer.remove(m_iCursorPosition-1,1); + m_bTextDisplayBufferDirty = true; m_iCursorPosition--; if(m_iFirstVisibleChar > m_iCursorPosition) m_iFirstVisibleChar--; @@ -2455,6 +2607,7 @@ void KviInputEditor::deleteWord() if(m_szTextBuffer.at(m_iCursorPosition - 1).isSpace()) break; m_szTextBuffer.remove(m_iCursorPosition-1,1); + m_bTextDisplayBufferDirty = true; m_iCursorPosition--; if(m_iFirstVisibleChar > m_iCursorPosition) m_iFirstVisibleChar--; @@ -2513,7 +2666,8 @@ void KviInputEditor::sendPlain() g_pColorWindow->hide(); QString szBuffer(m_szTextBuffer); - m_szTextBuffer=""; + m_szTextBuffer = ""; + m_bTextDisplayBufferDirty = true; selectOneChar(-1); m_iCursorPosition = 0; m_iFirstVisibleChar = 0; @@ -2542,7 +2696,8 @@ void KviInputEditor::sendKvs() g_pColorWindow->hide(); QString szBuffer(m_szTextBuffer), szTmp(m_szTextBuffer); - m_szTextBuffer=""; + m_szTextBuffer = ""; + m_bTextDisplayBufferDirty = true; selectOneChar(-1); m_iCursorPosition = 0; m_iFirstVisibleChar = 0; @@ -2623,14 +2778,17 @@ void KviInputEditor::historyPrev() { m_szSaveTextBuffer = m_szTextBuffer; m_szTextBuffer = *(m_pHistory->at(0)); + m_bTextDisplayBufferDirty = true; m_iCurHistoryIdx = 0; } else if(m_iCurHistoryIdx >= (int)(m_pHistory->count()-1)) { - m_szTextBuffer=m_szSaveTextBuffer; + m_szTextBuffer = m_szSaveTextBuffer; + m_bTextDisplayBufferDirty = true; m_iCurHistoryIdx = -1; } else { m_iCurHistoryIdx++; m_szTextBuffer = *(m_pHistory->at(m_iCurHistoryIdx)); + m_bTextDisplayBufferDirty = true; } selectOneChar(-1); if(KVI_OPTION_BOOL(KviOption_boolInputHistoryCursorAtEnd))end(); @@ -2652,12 +2810,13 @@ void KviInputEditor::historyNext() m_iCurHistoryIdx =m_pHistory->count()-1; } else if(m_iCurHistoryIdx == 0) { - m_szTextBuffer=m_szSaveTextBuffer; + m_szTextBuffer = m_szSaveTextBuffer; m_iCurHistoryIdx = -1; } else { m_iCurHistoryIdx--; m_szTextBuffer = *(m_pHistory->at(m_iCurHistoryIdx)); } + m_bTextDisplayBufferDirty = true; selectOneChar(-1); if(KVI_OPTION_BOOL(KviOption_boolInputHistoryCursorAtEnd))end(); else home(); @@ -2679,6 +2838,7 @@ void KviInputEditor::backspaceHit() //remove the selection addUndo(new EditCommand(EditCommand::RemoveText,m_szTextBuffer.mid(m_iSelectionBegin,(m_iSelectionEnd-m_iSelectionBegin)+1),m_iSelectionBegin)); m_szTextBuffer.remove(m_iSelectionBegin,(m_iSelectionEnd-m_iSelectionBegin)+1); + m_bTextDisplayBufferDirty = true; m_iCursorPosition = m_iSelectionBegin; if(m_iFirstVisibleChar > m_iCursorPosition) m_iFirstVisibleChar = m_iCursorPosition; @@ -2687,6 +2847,7 @@ void KviInputEditor::backspaceHit() m_iCursorPosition--; addUndo(new EditCommand(EditCommand::RemoveText,m_szTextBuffer.mid(m_iCursorPosition,1),m_iCursorPosition)); m_szTextBuffer.remove(m_iCursorPosition,1); + m_bTextDisplayBufferDirty = true; if(m_iFirstVisibleChar > m_iCursorPosition) m_iFirstVisibleChar--; } @@ -2708,6 +2869,7 @@ void KviInputEditor::deleteHit() if(m_iCursorPosition < (int)m_szTextBuffer.length()) { m_szTextBuffer.remove(m_iCursorPosition,1); + m_bTextDisplayBufferDirty = true; selectOneChar(-1); repaintWithCursorOn(); } @@ -2716,7 +2878,6 @@ void KviInputEditor::deleteHit() void KviInputEditor::escapeHit() { emit escapePressed(); - return; } void KviInputEditor::toggleCommandMode() diff --git a/src/kvirc/ui/KviInputEditor.h b/src/kvirc/ui/KviInputEditor.h index e8b507f82..ad9b326ab 100644 --- a/src/kvirc/ui/KviInputEditor.h +++ b/src/kvirc/ui/KviInputEditor.h @@ -80,6 +80,7 @@ class KVIRC_API KviInputEditor : public QWidget //Q_PROPERTY(int KviProperty_FocusOwner READ heightHint) Q_PROPERTY(int TransparencyCapable READ heightHint) Q_OBJECT + friend class KviTextIconWindow; // it uses insetIconCode(const QString &) friend class KviTopicWidget; // it uses home() public: /** @@ -100,8 +101,9 @@ protected: static QFontMetrics * g_pLastFontMetrics; static int g_iInputInstances; static int g_iCachedHeight; - QString m_szTextBuffer; - QString m_szTextDisplayBuffer; + QString m_szTextBuffer; // original buffer + QString m_szTextDisplayBuffer; // buffer with spelling mistakes marked + bool m_bTextDisplayBufferDirty; int m_iCursorPosition; int m_iFirstVisibleChar; int m_iSelectionBegin; @@ -917,7 +919,14 @@ private slots: * \return void */ void zoomDefault(); + + /** + * Shows the text icon window at cursor position + */ + void popupTextIconWindow(); + protected: + void insertIconCode(const QString &szCode); void completionEscapeUnsafeToken(QString &szToken); void installShortcuts(); virtual void drawContents(QPainter *); @@ -934,6 +943,8 @@ protected: virtual void dropEvent(QDropEvent * e); virtual void inputMethodEvent(QInputMethodEvent * e) ; virtual void paintEvent(QPaintEvent * e); + QString checkSpelling(const QString &szText); + void checkWordSpelling(const QString &szWord,QString * pszResult); signals: /** * \brief Called when the user press escape diff --git a/src/kvirc/ui/KviTextIconWindow.cpp b/src/kvirc/ui/KviTextIconWindow.cpp index b01451d2a..d9b426d3e 100644 --- a/src/kvirc/ui/KviTextIconWindow.cpp +++ b/src/kvirc/ui/KviTextIconWindow.cpp @@ -158,28 +158,27 @@ void KviTextIconWindow::doHide() void KviTextIconWindow::cellSelected(int row, int column) { - if(m_pTable->cellWidget(row, column)) - { -// qDebug("%i %i %i %s",m_pOwner->inherits("KviInputEditor"),m_pOwner->inherits("KviInput"),m_pOwner->inherits("QLineEdit"),m_pOwner->className()); - QString szItem(m_pTable->cellWidget(row, column)->toolTip()); - szItem.append(' '); + if(!m_pTable->cellWidget(row, column)) + return; - if(m_bAltMode) - szItem.prepend(KviControlCodes::Icon); + QString szItem(m_pTable->cellWidget(row, column)->toolTip()); + szItem.append(' '); - if(m_pOwner->inherits("KviInputEditor")) - ((KviInputEditor *)m_pOwner)->insertText(szItem); - else if(m_pOwner->inherits("KviInput")) - ((KviInput *)m_pOwner)->insertText(szItem); - else if(m_pOwner->inherits("QLineEdit")) - { - QString szTmp = ((QLineEdit *)m_pOwner)->text(); - szTmp.insert(((QLineEdit *)m_pOwner)->cursorPosition(),szItem); - ((QLineEdit *)m_pOwner)->setText(szTmp); - ((QLineEdit *)m_pOwner)->setCursorPosition(((QLineEdit *)m_pOwner)->cursorPosition() + szItem.length()); - } - doHide(); + if(m_bAltMode) + szItem.prepend(KviControlCodes::Icon); + + if(m_pOwner->inherits("KviInputEditor")) + ((KviInputEditor *)m_pOwner)->insertIconCode(szItem); + else if(m_pOwner->inherits("KviInput")) + ((KviInput *)m_pOwner)->insertText(szItem); + else if(m_pOwner->inherits("QLineEdit")) + { + QString szTmp = ((QLineEdit *)m_pOwner)->text(); + szTmp.insert(((QLineEdit *)m_pOwner)->cursorPosition(),szItem); + ((QLineEdit *)m_pOwner)->setText(szTmp); + ((QLineEdit *)m_pOwner)->setCursorPosition(((QLineEdit *)m_pOwner)->cursorPosition() + szItem.length()); } + doHide(); } diff --git a/src/kvirc/ui/KviWindow.h b/src/kvirc/ui/KviWindow.h index 8f6e50f95..84fe62735 100644 --- a/src/kvirc/ui/KviWindow.h +++ b/src/kvirc/ui/KviWindow.h @@ -249,6 +249,21 @@ public: inline Type type() const { return m_eType; }; /** + * Returns true if the window is a channel + */ + inline bool isChannel() const { return m_eType == Channel; }; + + /** + * Returns true if the window is a query + */ + inline bool isQuery() const { return m_eType == Query; }; + + /** + * Returns true if the window is a console + */ + inline bool isConsole() const { return m_eType == Console; }; + + /** * \brief Returns a descriptive name of the window type * \return const char * */ diff --git a/src/modules/spellchecker/libkvispellchecker.cpp b/src/modules/spellchecker/libkvispellchecker.cpp index caa64c817..669d4f283 100644 --- a/src/modules/spellchecker/libkvispellchecker.cpp +++ b/src/modules/spellchecker/libkvispellchecker.cpp @@ -45,12 +45,20 @@ static KviPointerList<EnchantDict>* g_pEnchantDicts = NULL; This function returns a hash. For every supported dictionary, key is the language code, value is name of provider for that language (e.g. Aspell). */ -static void spellchecker_enumerate_dicts(const char* szLang, const char* /*szName*/, - const char* szDesc, const char* /*szFile*/, void* pData) { +static void spellchecker_enumerate_dicts( + const char* szLang, + const char* /*szName*/, + const char* szDesc, + const char* /*szFile*/, + void* pData + ) +{ KviKvsHash* pHash = reinterpret_cast<KviKvsHash*>(pData); pHash->set(szLang, new KviKvsVariant(szDesc)); } -static bool spellchecker_kvs_available_dictionaries(KviKvsModuleFunctionCall* c) { + +static bool spellchecker_kvs_available_dictionaries(KviKvsModuleFunctionCall* c) +{ KVSM_PARAMETERS_BEGIN(c) KVSM_PARAMETERS_END(c) KviKvsHash* pHash = new KviKvsHash; @@ -88,6 +96,59 @@ static bool spellchecker_kvs_check(KviKvsModuleFunctionCall* c) return true; } + +/* + @doc: spellchecker.suggestions + @type: + function + @title: + $spellchecker.suggestions + @short: + Get spelling suggestions for a single word + @syntax: + <array> $spellchecker.suggestions(<word:string>) + @description: + This function returns the suggestions for the specified word. + If the word seems to be spelled correctly or there are no dictionaries + selected then the functon returns an empty array. +*/ +static bool spellchecker_kvs_suggestions(KviKvsModuleFunctionCall* c) +{ + QString szWord; + KVSM_PARAMETERS_BEGIN(c) + KVSM_PARAMETER("word", KVS_PT_STRING, 0, szWord) + KVSM_PARAMETERS_END(c) + + QHash<QString,int> hAllSuggestions; + + if(!g_pEnchantDicts->isEmpty()) + { + QByteArray utf8 = szWord.toUtf8(); + + KviPointerListIterator<EnchantDict> it(*g_pEnchantDicts); + for(bool b = it.moveFirst(); b; b = it.moveNext()) + { + size_t iCount = 0; + char ** suggestions = enchant_dict_suggest(*it,utf8.data(),utf8.size(),&iCount); + if(suggestions) + { + for(int i=0;i<iCount;i++) + hAllSuggestions.insert(QString::fromUtf8(suggestions[i]),1); + enchant_dict_free_string_list(*it,suggestions); + } + } + } + + KviKvsArray * pArray = new KviKvsArray(); + + QList<QString> lSuggestions = hAllSuggestions.keys(); + Q_FOREACH(QString szSuggestion,lSuggestions) + pArray->append(new KviKvsVariant(szSuggestion)); + + c->returnValue()->setArray(pArray); + return true; +} + static void spellchecker_reload_dicts() { while (!g_pEnchantDicts->isEmpty()) @@ -135,6 +196,7 @@ static bool spellchecker_module_init(KviModule* m) KVSM_REGISTER_SIMPLE_COMMAND(m, "reloadDictionaries", spellchecker_kvs_reload_dictionaries); KVSM_REGISTER_FUNCTION(m, "availableDictionaries", spellchecker_kvs_available_dictionaries); KVSM_REGISTER_FUNCTION(m, "check", spellchecker_kvs_check); + KVSM_REGISTER_FUNCTION(m, "suggestions", spellchecker_kvs_suggestions); return true; } |
