123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286 |
- // Copyright (c) 2017 Riverbank Computing Limited
- // Copyright (c) 2011 Archaeopteryx Software, Inc.
- // Copyright (c) 1990-2011, Scientific Toolworks, Inc.
- //
- // The License.txt file describes the conditions under which this software may
- // be distributed.
- #include <qglobal.h>
- #include <QColor>
- #include <QFont>
- #include <QInputMethodEvent>
- #include <QRect>
- #include <QTextCharFormat>
- #include <QTextFormat>
- #include <QVariant>
- #include <QVarLengthArray>
- #include "SciNamespace.h"
- #include "Qsci/qsciscintillabase.h"
- #include "ScintillaQt.h"
- #define INDIC_INPUTMETHOD 24
- #define MAXLENINPUTIME 200
- #define SC_INDICATOR_INPUT INDIC_IME
- #define SC_INDICATOR_TARGET INDIC_IME+1
- #define SC_INDICATOR_CONVERTED INDIC_IME+2
- #define SC_INDICATOR_UNKNOWN INDIC_IME_MAX
- static bool IsHangul(const QChar qchar)
- {
- int unicode = (int)qchar.unicode();
- // Korean character ranges used for preedit chars.
- // http://www.programminginkorean.com/programming/hangul-in-unicode/
- const bool HangulJamo = (0x1100 <= unicode && unicode <= 0x11FF);
- const bool HangulCompatibleJamo = (0x3130 <= unicode && unicode <= 0x318F);
- const bool HangulJamoExtendedA = (0xA960 <= unicode && unicode <= 0xA97F);
- const bool HangulJamoExtendedB = (0xD7B0 <= unicode && unicode <= 0xD7FF);
- const bool HangulSyllable = (0xAC00 <= unicode && unicode <= 0xD7A3);
- return HangulJamo || HangulCompatibleJamo || HangulSyllable ||
- HangulJamoExtendedA || HangulJamoExtendedB;
- }
- static void MoveImeCarets(QsciScintillaQt *sqt, int offset)
- {
- // Move carets relatively by bytes
- for (size_t r=0; r < sqt->sel.Count(); r++) {
- int positionInsert = sqt->sel.Range(r).Start().Position();
- sqt->sel.Range(r).caret.SetPosition(positionInsert + offset);
- sqt->sel.Range(r).anchor.SetPosition(positionInsert + offset);
- }
- }
- static void DrawImeIndicator(QsciScintillaQt *sqt, int indicator, int len)
- {
- // Emulate the visual style of IME characters with indicators.
- // Draw an indicator on the character before caret by the character bytes of len
- // so it should be called after AddCharUTF().
- // It does not affect caret positions.
- if (indicator < 8 || indicator > INDIC_MAX) {
- return;
- }
- sqt->pdoc->decorations.SetCurrentIndicator(indicator);
- for (size_t r=0; r< sqt-> sel.Count(); r++) {
- int positionInsert = sqt->sel.Range(r).Start().Position();
- sqt->pdoc->DecorationFillRange(positionInsert - len, 1, len);
- }
- }
- static int GetImeCaretPos(QInputMethodEvent *event)
- {
- foreach (QInputMethodEvent::Attribute attr, event->attributes()) {
- if (attr.type == QInputMethodEvent::Cursor)
- return attr.start;
- }
- return 0;
- }
- static std::vector<int> MapImeIndicators(QInputMethodEvent *event)
- {
- std::vector<int> imeIndicator(event->preeditString().size(), SC_INDICATOR_UNKNOWN);
- foreach (QInputMethodEvent::Attribute attr, event->attributes()) {
- if (attr.type == QInputMethodEvent::TextFormat) {
- QTextFormat format = attr.value.value<QTextFormat>();
- QTextCharFormat charFormat = format.toCharFormat();
- int indicator = SC_INDICATOR_UNKNOWN;
- switch (charFormat.underlineStyle()) {
- case QTextCharFormat::NoUnderline: // win32, linux
- indicator = SC_INDICATOR_TARGET;
- break;
- case QTextCharFormat::SingleUnderline: // osx
- case QTextCharFormat::DashUnderline: // win32, linux
- indicator = SC_INDICATOR_INPUT;
- break;
- case QTextCharFormat::DotLine:
- case QTextCharFormat::DashDotLine:
- case QTextCharFormat::WaveUnderline:
- case QTextCharFormat::SpellCheckUnderline:
- indicator = SC_INDICATOR_CONVERTED;
- break;
- default:
- indicator = SC_INDICATOR_UNKNOWN;
- }
- if (format.hasProperty(QTextFormat::BackgroundBrush)) // win32, linux
- indicator = SC_INDICATOR_TARGET;
- #ifdef Q_OS_OSX
- if (charFormat.underlineStyle() == QTextCharFormat::SingleUnderline) {
- QColor uc = charFormat.underlineColor();
- if (uc.lightness() < 2) { // osx
- indicator = SC_INDICATOR_TARGET;
- }
- }
- #endif
- for (int i = attr.start; i < attr.start+attr.length; i++) {
- imeIndicator[i] = indicator;
- }
- }
- }
- return imeIndicator;
- }
- void QsciScintillaBase::inputMethodEvent(QInputMethodEvent *event)
- {
- // Copy & paste by johnsonj with a lot of helps of Neil
- // Great thanks for my forerunners, jiniya and BLUEnLIVE
- if (sci->pdoc->IsReadOnly() || sci->SelectionContainsProtected()) {
- // Here, a canceling and/or completing composition function is needed.
- return;
- }
- if (sci->pdoc->TentativeActive()) {
- sci->pdoc->TentativeUndo();
- } else {
- // No tentative undo means start of this composition so
- // Fill in any virtual spaces.
- sci->ClearBeforeTentativeStart();
- }
- sci->view.imeCaretBlockOverride = false;
- if (!event->commitString().isEmpty()) {
- const QString commitStr = event->commitString();
- const unsigned int commitStrLen = commitStr.length();
- for (unsigned int i = 0; i < commitStrLen;) {
- const unsigned int ucWidth = commitStr.at(i).isHighSurrogate() ? 2 : 1;
- const QString oneCharUTF16 = commitStr.mid(i, ucWidth);
- const QByteArray oneChar = textAsBytes(oneCharUTF16);
- const int oneCharLen = oneChar.length();
- sci->AddCharUTF(oneChar.data(), oneCharLen);
- i += ucWidth;
- }
- } else if (!event->preeditString().isEmpty()) {
- const QString preeditStr = event->preeditString();
- const unsigned int preeditStrLen = preeditStr.length();
- if ((preeditStrLen == 0) || (preeditStrLen > MAXLENINPUTIME)) {
- sci->ShowCaretAtCurrentPosition();
- return;
- }
- sci->pdoc->TentativeStart(); // TentativeActive() from now on.
- std::vector<int> imeIndicator = MapImeIndicators(event);
- const bool recording = sci->recordingMacro;
- sci->recordingMacro = false;
- for (unsigned int i = 0; i < preeditStrLen;) {
- const unsigned int ucWidth = preeditStr.at(i).isHighSurrogate() ? 2 : 1;
- const QString oneCharUTF16 = preeditStr.mid(i, ucWidth);
- const QByteArray oneChar = textAsBytes(oneCharUTF16);
- const int oneCharLen = oneChar.length();
- sci->AddCharUTF(oneChar.data(), oneCharLen);
- DrawImeIndicator(sci, imeIndicator[i], oneCharLen);
- i += ucWidth;
- }
- sci->recordingMacro = recording;
- // Move IME carets.
- int imeCaretPos = GetImeCaretPos(event);
- int imeEndToImeCaretU16 = imeCaretPos - preeditStrLen;
- int imeCaretPosDoc = sci->pdoc->GetRelativePositionUTF16(sci->CurrentPosition(), imeEndToImeCaretU16);
- MoveImeCarets(sci, - sci->CurrentPosition() + imeCaretPosDoc);
- if (IsHangul(preeditStr.at(0))) {
- #ifndef Q_OS_WIN
- if (imeCaretPos > 0) {
- int oneCharBefore = sci->pdoc->GetRelativePosition(sci->CurrentPosition(), -1);
- MoveImeCarets(sci, - sci->CurrentPosition() + oneCharBefore);
- }
- #endif
- sci->view.imeCaretBlockOverride = true;
- }
- // Set candidate box position for Qt::ImMicroFocus.
- preeditPos = sci->CurrentPosition();
- sci->EnsureCaretVisible();
- updateMicroFocus();
- }
- sci->ShowCaretAtCurrentPosition();
- }
- QVariant QsciScintillaBase::inputMethodQuery(Qt::InputMethodQuery query) const
- {
- int pos = SendScintilla(SCI_GETCURRENTPOS);
- int line = SendScintilla(SCI_LINEFROMPOSITION, pos);
- switch (query) {
- #if QT_VERSION >= 0x050000
- case Qt::ImHints:
- return QWidget::inputMethodQuery(query);
- #endif
- case Qt::ImMicroFocus:
- {
- int startPos = (preeditPos >= 0) ? preeditPos : pos;
- QSCI_SCI_NAMESPACE(Point) pt = sci->LocationFromPosition(startPos);
- int width = SendScintilla(SCI_GETCARETWIDTH);
- int height = SendScintilla(SCI_TEXTHEIGHT, line);
- return QRect(pt.x, pt.y, width, height);
- }
- case Qt::ImFont:
- {
- char fontName[64];
- int style = SendScintilla(SCI_GETSTYLEAT, pos);
- int len = SendScintilla(SCI_STYLEGETFONT, style, (sptr_t)fontName);
- int size = SendScintilla(SCI_STYLEGETSIZE, style);
- bool italic = SendScintilla(SCI_STYLEGETITALIC, style);
- int weight = SendScintilla(SCI_STYLEGETBOLD, style) ? QFont::Bold : -1;
- return QFont(QString::fromUtf8(fontName, len), size, weight, italic);
- }
- case Qt::ImCursorPosition:
- {
- int paraStart = sci->pdoc->ParaUp(pos);
- return pos - paraStart;
- }
- case Qt::ImSurroundingText:
- {
- int paraStart = sci->pdoc->ParaUp(pos);
- int paraEnd = sci->pdoc->ParaDown(pos);
- QVarLengthArray<char,1024> buffer(paraEnd - paraStart + 1);
- Sci_CharacterRange charRange;
- charRange.cpMin = paraStart;
- charRange.cpMax = paraEnd;
- Sci_TextRange textRange;
- textRange.chrg = charRange;
- textRange.lpstrText = buffer.data();
- SendScintilla(SCI_GETTEXTRANGE, 0, (sptr_t)&textRange);
- return bytesAsText(buffer.constData());
- }
- case Qt::ImCurrentSelection:
- {
- QVarLengthArray<char,1024> buffer(SendScintilla(SCI_GETSELTEXT));
- SendScintilla(SCI_GETSELTEXT, 0, (sptr_t)buffer.data());
- return bytesAsText(buffer.constData());
- }
- default:
- return QVariant();
- }
- }
|