1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283
|
// 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 "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->DecorationSetCurrentIndicator(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;
Scintilla::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();
}
}
|