1 //
2 // Copyright (c) 1990-2011, Scientific Toolworks, Inc.
3 //
4 // The License.txt file describes the conditions under which this software may be distributed.
5 //
6 // Author: Jason Haslam
7 //
8 // Additions Copyright (c) 2011 Archaeopteryx Software, Inc. d/b/a Wingware
9 // ScintillaEditBase.cpp - Qt widget that wraps ScintillaQt and provides events and scrolling
10
11 #include "ScintillaEditBase.h"
12 #include "ScintillaQt.h"
13 #include "PlatQt.h"
14
15 #include <QApplication>
16 #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
17 #include <QInputContext>
18 #endif
19 #include <QPainter>
20 #include <QVarLengthArray>
21 #include <QScrollBar>
22 #include <QTextFormat>
23
24 #define INDIC_INPUTMETHOD 24
25
26 #define SC_INDICATOR_INPUT INDICATOR_IME
27 #define SC_INDICATOR_TARGET INDICATOR_IME+1
28 #define SC_INDICATOR_CONVERTED INDICATOR_IME+2
29 #define SC_INDICATOR_UNKNOWN INDICATOR_IME_MAX
30
31 // Q_WS_MAC and Q_WS_X11 aren't defined in Qt5
32 #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
33 #ifdef Q_OS_MAC
34 #define Q_WS_MAC 1
35 #endif
36
37 #if !defined(Q_OS_MAC) && !defined(Q_OS_WIN)
38 #define Q_WS_X11 1
39 #endif
40 #endif // QT_VERSION >= 5.0.0
41
42 using namespace Scintilla;
43
ScintillaEditBase(QWidget * parent)44 ScintillaEditBase::ScintillaEditBase(QWidget *parent)
45 : QAbstractScrollArea(parent), sqt(nullptr), preeditPos(-1), wheelDelta(0)
46 {
47 sqt = new ScintillaQt(this);
48
49 time.start();
50
51 // Set Qt defaults.
52 setAcceptDrops(true);
53 setMouseTracking(true);
54 setAutoFillBackground(false);
55 setFrameStyle(QFrame::NoFrame);
56 setFocusPolicy(Qt::StrongFocus);
57 setAttribute(Qt::WA_StaticContents);
58 viewport()->setAutoFillBackground(false);
59 setAttribute(Qt::WA_KeyCompression);
60 setAttribute(Qt::WA_InputMethodEnabled);
61
62 sqt->vs.indicators[SC_INDICATOR_UNKNOWN] = Indicator(INDIC_HIDDEN, ColourDesired(0, 0, 0xff));
63 sqt->vs.indicators[SC_INDICATOR_INPUT] = Indicator(INDIC_DOTS, ColourDesired(0, 0, 0xff));
64 sqt->vs.indicators[SC_INDICATOR_CONVERTED] = Indicator(INDIC_COMPOSITIONTHICK, ColourDesired(0, 0, 0xff));
65 sqt->vs.indicators[SC_INDICATOR_TARGET] = Indicator(INDIC_STRAIGHTBOX, ColourDesired(0, 0, 0xff));
66
67 connect(sqt, SIGNAL(notifyParent(SCNotification)),
68 this, SLOT(notifyParent(SCNotification)));
69
70 // Connect scroll bars.
71 connect(verticalScrollBar(), SIGNAL(valueChanged(int)),
72 this, SLOT(scrollVertical(int)));
73 connect(horizontalScrollBar(), SIGNAL(valueChanged(int)),
74 this, SLOT(scrollHorizontal(int)));
75
76 // Connect pass-through signals.
77 connect(sqt, SIGNAL(horizontalRangeChanged(int,int)),
78 this, SIGNAL(horizontalRangeChanged(int,int)));
79 connect(sqt, SIGNAL(verticalRangeChanged(int,int)),
80 this, SIGNAL(verticalRangeChanged(int,int)));
81 connect(sqt, SIGNAL(horizontalScrolled(int)),
82 this, SIGNAL(horizontalScrolled(int)));
83 connect(sqt, SIGNAL(verticalScrolled(int)),
84 this, SIGNAL(verticalScrolled(int)));
85
86 connect(sqt, SIGNAL(notifyChange()),
87 this, SIGNAL(notifyChange()));
88
89 connect(sqt, SIGNAL(command(uptr_t, sptr_t)),
90 this, SLOT(event_command(uptr_t, sptr_t)));
91
92 connect(sqt, SIGNAL(aboutToCopy(QMimeData *)),
93 this, SIGNAL(aboutToCopy(QMimeData *)));
94 }
95
~ScintillaEditBase()96 ScintillaEditBase::~ScintillaEditBase() {}
97
send(unsigned int iMessage,uptr_t wParam,sptr_t lParam) const98 sptr_t ScintillaEditBase::send(
99 unsigned int iMessage,
100 uptr_t wParam,
101 sptr_t lParam) const
102 {
103 return sqt->WndProc(iMessage, wParam, lParam);
104 }
105
sends(unsigned int iMessage,uptr_t wParam,const char * s) const106 sptr_t ScintillaEditBase::sends(
107 unsigned int iMessage,
108 uptr_t wParam,
109 const char *s) const
110 {
111 return sqt->WndProc(iMessage, wParam, (sptr_t)s);
112 }
113
scrollHorizontal(int value)114 void ScintillaEditBase::scrollHorizontal(int value)
115 {
116 sqt->HorizontalScrollTo(value);
117 }
118
scrollVertical(int value)119 void ScintillaEditBase::scrollVertical(int value)
120 {
121 sqt->ScrollTo(value);
122 }
123
event(QEvent * event)124 bool ScintillaEditBase::event(QEvent *event)
125 {
126 bool result = false;
127
128 if (event->type() == QEvent::KeyPress) {
129 // Circumvent the tab focus convention.
130 keyPressEvent(static_cast<QKeyEvent *>(event));
131 result = event->isAccepted();
132 } else if (event->type() == QEvent::Show) {
133 setMouseTracking(true);
134 result = QAbstractScrollArea::event(event);
135 } else if (event->type() == QEvent::Hide) {
136 setMouseTracking(false);
137 result = QAbstractScrollArea::event(event);
138 } else {
139 result = QAbstractScrollArea::event(event);
140 }
141
142 return result;
143 }
144
paintEvent(QPaintEvent * event)145 void ScintillaEditBase::paintEvent(QPaintEvent *event)
146 {
147 sqt->PartialPaint(PRectFromQRect(event->rect()));
148 }
149
wheelEvent(QWheelEvent * event)150 void ScintillaEditBase::wheelEvent(QWheelEvent *event)
151 {
152 if (event->orientation() == Qt::Horizontal) {
153 if (horizontalScrollBarPolicy() == Qt::ScrollBarAlwaysOff)
154 event->ignore();
155 else
156 QAbstractScrollArea::wheelEvent(event);
157 } else {
158 if (QApplication::keyboardModifiers() & Qt::ControlModifier) {
159 // Zoom! We play with the font sizes in the styles.
160 // Number of steps/line is ignored, we just care if sizing up or down
161 if (event->delta() > 0) {
162 sqt->KeyCommand(SCI_ZOOMIN);
163 } else {
164 sqt->KeyCommand(SCI_ZOOMOUT);
165 }
166 } else {
167 // Ignore wheel events when the scroll bars are disabled.
168 if (verticalScrollBarPolicy() == Qt::ScrollBarAlwaysOff) {
169 event->ignore();
170 } else {
171 // Scroll
172 QAbstractScrollArea::wheelEvent(event);
173 }
174 }
175 }
176 }
177
focusInEvent(QFocusEvent * event)178 void ScintillaEditBase::focusInEvent(QFocusEvent *event)
179 {
180 sqt->SetFocusState(true);
181
182 QAbstractScrollArea::focusInEvent(event);
183 }
184
focusOutEvent(QFocusEvent * event)185 void ScintillaEditBase::focusOutEvent(QFocusEvent *event)
186 {
187 sqt->SetFocusState(false);
188
189 QAbstractScrollArea::focusOutEvent(event);
190 }
191
resizeEvent(QResizeEvent *)192 void ScintillaEditBase::resizeEvent(QResizeEvent *)
193 {
194 sqt->ChangeSize();
195 emit resized();
196 }
197
keyPressEvent(QKeyEvent * event)198 void ScintillaEditBase::keyPressEvent(QKeyEvent *event)
199 {
200 // All keystrokes containing the meta modifier are
201 // assumed to be shortcuts not handled by scintilla.
202 if (QApplication::keyboardModifiers() & Qt::MetaModifier) {
203 QAbstractScrollArea::keyPressEvent(event);
204 emit keyPressed(event);
205 return;
206 }
207
208 int key = 0;
209 switch (event->key()) {
210 case Qt::Key_Down: key = SCK_DOWN; break;
211 case Qt::Key_Up: key = SCK_UP; break;
212 case Qt::Key_Left: key = SCK_LEFT; break;
213 case Qt::Key_Right: key = SCK_RIGHT; break;
214 case Qt::Key_Home: key = SCK_HOME; break;
215 case Qt::Key_End: key = SCK_END; break;
216 case Qt::Key_PageUp: key = SCK_PRIOR; break;
217 case Qt::Key_PageDown: key = SCK_NEXT; break;
218 case Qt::Key_Delete: key = SCK_DELETE; break;
219 case Qt::Key_Insert: key = SCK_INSERT; break;
220 case Qt::Key_Escape: key = SCK_ESCAPE; break;
221 case Qt::Key_Backspace: key = SCK_BACK; break;
222 case Qt::Key_Plus: key = SCK_ADD; break;
223 case Qt::Key_Minus: key = SCK_SUBTRACT; break;
224 case Qt::Key_Backtab: // fall through
225 case Qt::Key_Tab: key = SCK_TAB; break;
226 case Qt::Key_Enter: // fall through
227 case Qt::Key_Return: key = SCK_RETURN; break;
228 case Qt::Key_Control: key = 0; break;
229 case Qt::Key_Alt: key = 0; break;
230 case Qt::Key_Shift: key = 0; break;
231 case Qt::Key_Meta: key = 0; break;
232 default: key = event->key(); break;
233 }
234
235 bool shift = QApplication::keyboardModifiers() & Qt::ShiftModifier;
236 bool ctrl = QApplication::keyboardModifiers() & Qt::ControlModifier;
237 bool alt = QApplication::keyboardModifiers() & Qt::AltModifier;
238
239 bool consumed = false;
240 bool added = sqt->KeyDownWithModifiers(key,
241 ScintillaQt::ModifierFlags(shift, ctrl, alt),
242 &consumed) != 0;
243 if (!consumed)
244 consumed = added;
245
246 if (!consumed) {
247 // Don't insert text if the control key was pressed unless
248 // it was pressed in conjunction with alt for AltGr emulation.
249 bool input = (!ctrl || alt);
250
251 // Additionally, on non-mac platforms, don't insert text
252 // if the alt key was pressed unless control is also present.
253 // On mac alt can be used to insert special characters.
254 #ifndef Q_WS_MAC
255 input &= (!alt || ctrl);
256 #endif
257
258 QString text = event->text();
259 if (input && !text.isEmpty() && text[0].isPrint()) {
260 QByteArray utext = sqt->BytesForDocument(text);
261 sqt->InsertCharacter(std::string_view(utext.data(), utext.size()), EditModel::CharacterSource::directInput);
262 } else {
263 event->ignore();
264 }
265 }
266
267 emit keyPressed(event);
268 }
269
270 #ifdef Q_WS_X11
modifierTranslated(int sciModifier)271 static int modifierTranslated(int sciModifier)
272 {
273 switch (sciModifier) {
274 case SCMOD_SHIFT:
275 return Qt::ShiftModifier;
276 case SCMOD_CTRL:
277 return Qt::ControlModifier;
278 case SCMOD_ALT:
279 return Qt::AltModifier;
280 case SCMOD_SUPER:
281 return Qt::MetaModifier;
282 default:
283 return 0;
284 }
285 }
286 #endif
287
mousePressEvent(QMouseEvent * event)288 void ScintillaEditBase::mousePressEvent(QMouseEvent *event)
289 {
290 Point pos = PointFromQPoint(event->pos());
291
292 emit buttonPressed(event);
293
294 if (event->button() == Qt::MidButton &&
295 QApplication::clipboard()->supportsSelection()) {
296 SelectionPosition selPos = sqt->SPositionFromLocation(
297 pos, false, false, sqt->UserVirtualSpace());
298 sqt->sel.Clear();
299 sqt->SetSelection(selPos, selPos);
300 sqt->PasteFromMode(QClipboard::Selection);
301 return;
302 }
303
304 if (event->button() == Qt::LeftButton) {
305 bool shift = QApplication::keyboardModifiers() & Qt::ShiftModifier;
306 bool ctrl = QApplication::keyboardModifiers() & Qt::ControlModifier;
307 #ifdef Q_WS_X11
308 // On X allow choice of rectangular modifier since most window
309 // managers grab alt + click for moving windows.
310 bool alt = QApplication::keyboardModifiers() & modifierTranslated(sqt->rectangularSelectionModifier);
311 #else
312 bool alt = QApplication::keyboardModifiers() & Qt::AltModifier;
313 #endif
314
315 sqt->ButtonDownWithModifiers(pos, time.elapsed(), ScintillaQt::ModifierFlags(shift, ctrl, alt));
316 }
317
318 if (event->button() == Qt::RightButton) {
319 sqt->RightButtonDownWithModifiers(pos, time.elapsed(), ModifiersOfKeyboard());
320 }
321 }
322
mouseReleaseEvent(QMouseEvent * event)323 void ScintillaEditBase::mouseReleaseEvent(QMouseEvent *event)
324 {
325 Point point = PointFromQPoint(event->pos());
326 if (event->button() == Qt::LeftButton)
327 sqt->ButtonUpWithModifiers(point, time.elapsed(), ModifiersOfKeyboard());
328
329 int pos = send(SCI_POSITIONFROMPOINT, point.x, point.y);
330 int line = send(SCI_LINEFROMPOSITION, pos);
331 int modifiers = QApplication::keyboardModifiers();
332
333 emit textAreaClicked(line, modifiers);
334 emit buttonReleased(event);
335 }
336
mouseDoubleClickEvent(QMouseEvent * event)337 void ScintillaEditBase::mouseDoubleClickEvent(QMouseEvent *event)
338 {
339 // Scintilla does its own double-click detection.
340 mousePressEvent(event);
341 }
342
mouseMoveEvent(QMouseEvent * event)343 void ScintillaEditBase::mouseMoveEvent(QMouseEvent *event)
344 {
345 Point pos = PointFromQPoint(event->pos());
346
347 bool shift = QApplication::keyboardModifiers() & Qt::ShiftModifier;
348 bool ctrl = QApplication::keyboardModifiers() & Qt::ControlModifier;
349 #ifdef Q_WS_X11
350 // On X allow choice of rectangular modifier since most window
351 // managers grab alt + click for moving windows.
352 bool alt = QApplication::keyboardModifiers() & modifierTranslated(sqt->rectangularSelectionModifier);
353 #else
354 bool alt = QApplication::keyboardModifiers() & Qt::AltModifier;
355 #endif
356
357 const int modifiers = ScintillaQt::ModifierFlags(shift, ctrl, alt);
358
359 sqt->ButtonMoveWithModifiers(pos, time.elapsed(), modifiers);
360 }
361
contextMenuEvent(QContextMenuEvent * event)362 void ScintillaEditBase::contextMenuEvent(QContextMenuEvent *event)
363 {
364 Point pos = PointFromQPoint(event->globalPos());
365 Point pt = PointFromQPoint(event->pos());
366 if (!sqt->PointInSelection(pt)) {
367 sqt->SetEmptySelection(sqt->PositionFromLocation(pt));
368 }
369 if (sqt->ShouldDisplayPopup(pt)) {
370 sqt->ContextMenu(pos);
371 }
372 }
373
dragEnterEvent(QDragEnterEvent * event)374 void ScintillaEditBase::dragEnterEvent(QDragEnterEvent *event)
375 {
376 if (event->mimeData()->hasUrls()) {
377 event->acceptProposedAction();
378 } else if (event->mimeData()->hasText()) {
379 event->acceptProposedAction();
380
381 Point point = PointFromQPoint(event->pos());
382 sqt->DragEnter(point);
383 } else {
384 event->ignore();
385 }
386 }
387
dragLeaveEvent(QDragLeaveEvent *)388 void ScintillaEditBase::dragLeaveEvent(QDragLeaveEvent * /* event */)
389 {
390 sqt->DragLeave();
391 }
392
dragMoveEvent(QDragMoveEvent * event)393 void ScintillaEditBase::dragMoveEvent(QDragMoveEvent *event)
394 {
395 if (event->mimeData()->hasUrls()) {
396 event->acceptProposedAction();
397 } else if (event->mimeData()->hasText()) {
398 event->acceptProposedAction();
399
400 Point point = PointFromQPoint(event->pos());
401 sqt->DragMove(point);
402 } else {
403 event->ignore();
404 }
405 }
406
dropEvent(QDropEvent * event)407 void ScintillaEditBase::dropEvent(QDropEvent *event)
408 {
409 if (event->mimeData()->hasUrls()) {
410 event->acceptProposedAction();
411 sqt->DropUrls(event->mimeData());
412 } else if (event->mimeData()->hasText()) {
413 event->acceptProposedAction();
414
415 Point point = PointFromQPoint(event->pos());
416 bool move = (event->source() == this &&
417 event->proposedAction() == Qt::MoveAction);
418 sqt->Drop(point, event->mimeData(), move);
419 } else {
420 event->ignore();
421 }
422 }
423
IsHangul(const QChar qchar)424 bool ScintillaEditBase::IsHangul(const QChar qchar)
425 {
426 int unicode = (int)qchar.unicode();
427 // Korean character ranges used for preedit chars.
428 // http://www.programminginkorean.com/programming/hangul-in-unicode/
429 const bool HangulJamo = (0x1100 <= unicode && unicode <= 0x11FF);
430 const bool HangulCompatibleJamo = (0x3130 <= unicode && unicode <= 0x318F);
431 const bool HangulJamoExtendedA = (0xA960 <= unicode && unicode <= 0xA97F);
432 const bool HangulJamoExtendedB = (0xD7B0 <= unicode && unicode <= 0xD7FF);
433 const bool HangulSyllable = (0xAC00 <= unicode && unicode <= 0xD7A3);
434 return HangulJamo || HangulCompatibleJamo || HangulSyllable ||
435 HangulJamoExtendedA || HangulJamoExtendedB;
436 }
437
MoveImeCarets(int offset)438 void ScintillaEditBase::MoveImeCarets(int offset)
439 {
440 // Move carets relatively by bytes
441 for (size_t r=0; r < sqt->sel.Count(); r++) {
442 int positionInsert = sqt->sel.Range(r).Start().Position();
443 sqt->sel.Range(r).caret.SetPosition(positionInsert + offset);
444 sqt->sel.Range(r).anchor.SetPosition(positionInsert + offset);
445 }
446 }
447
DrawImeIndicator(int indicator,int len)448 void ScintillaEditBase::DrawImeIndicator(int indicator, int len)
449 {
450 // Emulate the visual style of IME characters with indicators.
451 // Draw an indicator on the character before caret by the character bytes of len
452 // so it should be called after InsertCharacter().
453 // It does not affect caret positions.
454 if (indicator < 8 || indicator > INDICATOR_MAX) {
455 return;
456 }
457 sqt->pdoc->DecorationSetCurrentIndicator(indicator);
458 for (size_t r=0; r< sqt-> sel.Count(); r++) {
459 int positionInsert = sqt->sel.Range(r).Start().Position();
460 sqt->pdoc->DecorationFillRange(positionInsert - len, 1, len);
461 }
462 }
463
GetImeCaretPos(QInputMethodEvent * event)464 static int GetImeCaretPos(QInputMethodEvent *event)
465 {
466 foreach (QInputMethodEvent::Attribute attr, event->attributes()) {
467 if (attr.type == QInputMethodEvent::Cursor)
468 return attr.start;
469 }
470 return 0;
471 }
472
MapImeIndicators(QInputMethodEvent * event)473 static std::vector<int> MapImeIndicators(QInputMethodEvent *event)
474 {
475 std::vector<int> imeIndicator(event->preeditString().size(), SC_INDICATOR_UNKNOWN);
476 foreach (QInputMethodEvent::Attribute attr, event->attributes()) {
477 if (attr.type == QInputMethodEvent::TextFormat) {
478 QTextFormat format = attr.value.value<QTextFormat>();
479 QTextCharFormat charFormat = format.toCharFormat();
480
481 int indicator = SC_INDICATOR_UNKNOWN;
482 switch (charFormat.underlineStyle()) {
483 case QTextCharFormat::NoUnderline: // win32, linux
484 indicator = SC_INDICATOR_TARGET;
485 break;
486 case QTextCharFormat::SingleUnderline: // osx
487 case QTextCharFormat::DashUnderline: // win32, linux
488 indicator = SC_INDICATOR_INPUT;
489 break;
490 case QTextCharFormat::DotLine:
491 case QTextCharFormat::DashDotLine:
492 case QTextCharFormat::WaveUnderline:
493 case QTextCharFormat::SpellCheckUnderline:
494 indicator = SC_INDICATOR_CONVERTED;
495 break;
496
497 default:
498 indicator = SC_INDICATOR_UNKNOWN;
499 }
500
501 if (format.hasProperty(QTextFormat::BackgroundBrush)) // win32, linux
502 indicator = SC_INDICATOR_TARGET;
503
504 #ifdef Q_OS_OSX
505 if (charFormat.underlineStyle() == QTextCharFormat::SingleUnderline) {
506 QColor uc = charFormat.underlineColor();
507 if (uc.lightness() < 2) { // osx
508 indicator = SC_INDICATOR_TARGET;
509 }
510 }
511 #endif
512
513 for (int i = attr.start; i < attr.start+attr.length; i++) {
514 imeIndicator[i] = indicator;
515 }
516 }
517 }
518 return imeIndicator;
519 }
520
inputMethodEvent(QInputMethodEvent * event)521 void ScintillaEditBase::inputMethodEvent(QInputMethodEvent *event)
522 {
523 // Copy & paste by johnsonj with a lot of helps of Neil
524 // Great thanks for my forerunners, jiniya and BLUEnLIVE
525
526 if (sqt->pdoc->IsReadOnly() || sqt->SelectionContainsProtected()) {
527 // Here, a canceling and/or completing composition function is needed.
528 return;
529 }
530
531 bool initialCompose = false;
532 if (sqt->pdoc->TentativeActive()) {
533 sqt->pdoc->TentativeUndo();
534 } else {
535 // No tentative undo means start of this composition so
536 // Fill in any virtual spaces.
537 initialCompose = true;
538 }
539
540 sqt->view.imeCaretBlockOverride = false;
541
542 if (!event->commitString().isEmpty()) {
543 const QString commitStr = event->commitString();
544 const unsigned int commitStrLen = commitStr.length();
545
546 for (unsigned int i = 0; i < commitStrLen;) {
547 const unsigned int ucWidth = commitStr.at(i).isHighSurrogate() ? 2 : 1;
548 const QString oneCharUTF16 = commitStr.mid(i, ucWidth);
549 const QByteArray oneChar = sqt->BytesForDocument(oneCharUTF16);
550
551 sqt->InsertCharacter(std::string_view(oneChar.data(), oneChar.length()), EditModel::CharacterSource::directInput);
552 i += ucWidth;
553 }
554
555 } else if (!event->preeditString().isEmpty()) {
556 const QString preeditStr = event->preeditString();
557 const unsigned int preeditStrLen = preeditStr.length();
558 if (preeditStrLen == 0) {
559 sqt->ShowCaretAtCurrentPosition();
560 return;
561 }
562
563 if (initialCompose)
564 sqt->ClearBeforeTentativeStart();
565 sqt->pdoc->TentativeStart(); // TentativeActive() from now on.
566
567 std::vector<int> imeIndicator = MapImeIndicators(event);
568
569 for (unsigned int i = 0; i < preeditStrLen;) {
570 const unsigned int ucWidth = preeditStr.at(i).isHighSurrogate() ? 2 : 1;
571 const QString oneCharUTF16 = preeditStr.mid(i, ucWidth);
572 const QByteArray oneChar = sqt->BytesForDocument(oneCharUTF16);
573 const int oneCharLen = oneChar.length();
574
575 sqt->InsertCharacter(std::string_view(oneChar.data(), oneCharLen), EditModel::CharacterSource::tentativeInput);
576
577 DrawImeIndicator(imeIndicator[i], oneCharLen);
578 i += ucWidth;
579 }
580
581 // Move IME carets.
582 int imeCaretPos = GetImeCaretPos(event);
583 int imeEndToImeCaretU16 = imeCaretPos - preeditStrLen;
584 int imeCaretPosDoc = sqt->pdoc->GetRelativePositionUTF16(sqt->CurrentPosition(), imeEndToImeCaretU16);
585
586 MoveImeCarets(- sqt->CurrentPosition() + imeCaretPosDoc);
587
588 if (IsHangul(preeditStr.at(0))) {
589 #ifndef Q_OS_WIN
590 if (imeCaretPos > 0) {
591 int oneCharBefore = sqt->pdoc->GetRelativePosition(sqt->CurrentPosition(), -1);
592 MoveImeCarets(- sqt->CurrentPosition() + oneCharBefore);
593 }
594 #endif
595 sqt->view.imeCaretBlockOverride = true;
596 }
597
598 // Set candidate box position for Qt::ImMicroFocus.
599 preeditPos = sqt->CurrentPosition();
600 sqt->EnsureCaretVisible();
601 updateMicroFocus();
602 }
603 sqt->ShowCaretAtCurrentPosition();
604 }
605
inputMethodQuery(Qt::InputMethodQuery query) const606 QVariant ScintillaEditBase::inputMethodQuery(Qt::InputMethodQuery query) const
607 {
608 int pos = send(SCI_GETCURRENTPOS);
609 int line = send(SCI_LINEFROMPOSITION, pos);
610
611 switch (query) {
612 case Qt::ImMicroFocus:
613 {
614 int startPos = (preeditPos >= 0) ? preeditPos : pos;
615 Point pt = sqt->LocationFromPosition(startPos);
616 int width = send(SCI_GETCARETWIDTH);
617 int height = send(SCI_TEXTHEIGHT, line);
618 return QRect(pt.x, pt.y, width, height);
619 }
620
621 case Qt::ImFont:
622 {
623 char fontName[64];
624 int style = send(SCI_GETSTYLEAT, pos);
625 int len = send(SCI_STYLEGETFONT, style, (sptr_t)fontName);
626 int size = send(SCI_STYLEGETSIZE, style);
627 bool italic = send(SCI_STYLEGETITALIC, style);
628 int weight = send(SCI_STYLEGETBOLD, style) ? QFont::Bold : -1;
629 return QFont(QString::fromUtf8(fontName, len), size, weight, italic);
630 }
631
632 case Qt::ImCursorPosition:
633 {
634 int paraStart = sqt->pdoc->ParaUp(pos);
635 return pos - paraStart;
636 }
637
638 case Qt::ImSurroundingText:
639 {
640 int paraStart = sqt->pdoc->ParaUp(pos);
641 int paraEnd = sqt->pdoc->ParaDown(pos);
642 QVarLengthArray<char,1024> buffer(paraEnd - paraStart + 1);
643
644 Sci_CharacterRange charRange;
645 charRange.cpMin = paraStart;
646 charRange.cpMax = paraEnd;
647
648 Sci_TextRange textRange;
649 textRange.chrg = charRange;
650 textRange.lpstrText = buffer.data();
651
652 send(SCI_GETTEXTRANGE, 0, (sptr_t)&textRange);
653
654 return sqt->StringFromDocument(buffer.constData());
655 }
656
657 case Qt::ImCurrentSelection:
658 {
659 QVarLengthArray<char,1024> buffer(send(SCI_GETSELTEXT));
660 send(SCI_GETSELTEXT, 0, (sptr_t)buffer.data());
661
662 return sqt->StringFromDocument(buffer.constData());
663 }
664
665 default:
666 return QVariant();
667 }
668 }
669
notifyParent(SCNotification scn)670 void ScintillaEditBase::notifyParent(SCNotification scn)
671 {
672 emit notify(&scn);
673 switch (scn.nmhdr.code) {
674 case SCN_STYLENEEDED:
675 emit styleNeeded(scn.position);
676 break;
677
678 case SCN_CHARADDED:
679 emit charAdded(scn.ch);
680 break;
681
682 case SCN_SAVEPOINTREACHED:
683 emit savePointChanged(false);
684 break;
685
686 case SCN_SAVEPOINTLEFT:
687 emit savePointChanged(true);
688 break;
689
690 case SCN_MODIFYATTEMPTRO:
691 emit modifyAttemptReadOnly();
692 break;
693
694 case SCN_KEY:
695 emit key(scn.ch);
696 break;
697
698 case SCN_DOUBLECLICK:
699 emit doubleClick(scn.position, scn.line);
700 break;
701
702 case SCN_UPDATEUI:
703 emit updateUi(scn.updated);
704 break;
705
706 case SCN_MODIFIED:
707 {
708 bool added = scn.modificationType & SC_MOD_INSERTTEXT;
709 bool deleted = scn.modificationType & SC_MOD_DELETETEXT;
710
711 int length = send(SCI_GETTEXTLENGTH);
712 bool firstLineAdded = (added && length == 1) ||
713 (deleted && length == 0);
714
715 if (scn.linesAdded != 0) {
716 emit linesAdded(scn.linesAdded);
717 } else if (firstLineAdded) {
718 emit linesAdded(added ? 1 : -1);
719 }
720
721 const QByteArray bytes = QByteArray::fromRawData(scn.text, scn.length);
722 emit modified(scn.modificationType, scn.position, scn.length,
723 scn.linesAdded, bytes, scn.line,
724 scn.foldLevelNow, scn.foldLevelPrev);
725 break;
726 }
727
728 case SCN_MACRORECORD:
729 emit macroRecord(scn.message, scn.wParam, scn.lParam);
730 break;
731
732 case SCN_MARGINCLICK:
733 emit marginClicked(scn.position, scn.modifiers, scn.margin);
734 break;
735
736 case SCN_NEEDSHOWN:
737 emit needShown(scn.position, scn.length);
738 break;
739
740 case SCN_PAINTED:
741 emit painted();
742 break;
743
744 case SCN_USERLISTSELECTION:
745 emit userListSelection();
746 break;
747
748 case SCN_URIDROPPED:
749 emit uriDropped(QString::fromUtf8(scn.text));
750 break;
751
752 case SCN_DWELLSTART:
753 emit dwellStart(scn.x, scn.y);
754 break;
755
756 case SCN_DWELLEND:
757 emit dwellEnd(scn.x, scn.y);
758 break;
759
760 case SCN_ZOOM:
761 emit zoom(send(SCI_GETZOOM));
762 break;
763
764 case SCN_HOTSPOTCLICK:
765 emit hotSpotClick(scn.position, scn.modifiers);
766 break;
767
768 case SCN_HOTSPOTDOUBLECLICK:
769 emit hotSpotDoubleClick(scn.position, scn.modifiers);
770 break;
771
772 case SCN_CALLTIPCLICK:
773 emit callTipClick();
774 break;
775
776 case SCN_AUTOCSELECTION:
777 emit autoCompleteSelection(scn.lParam, QString::fromUtf8(scn.text));
778 break;
779
780 case SCN_AUTOCCANCELLED:
781 emit autoCompleteCancelled();
782 break;
783
784 case SCN_FOCUSIN:
785 emit focusChanged(true);
786 break;
787
788 case SCN_FOCUSOUT:
789 emit focusChanged(false);
790 break;
791
792 default:
793 return;
794 }
795 }
796
event_command(uptr_t wParam,sptr_t lParam)797 void ScintillaEditBase::event_command(uptr_t wParam, sptr_t lParam)
798 {
799 emit command(wParam, lParam);
800 }
801
ModifiersOfKeyboard() const802 int ScintillaEditBase::ModifiersOfKeyboard() const
803 {
804 const bool shift = QApplication::keyboardModifiers() & Qt::ShiftModifier;
805 const bool ctrl = QApplication::keyboardModifiers() & Qt::ControlModifier;
806 const bool alt = QApplication::keyboardModifiers() & Qt::AltModifier;
807
808 return ScintillaQt::ModifierFlags(shift, ctrl, alt);
809 }
810