1 /* * This file is part of Maliit framework *
2  *
3  * Copyright (C) 2012 Canonical Ltd
4  *
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License version 2.1 as published by the Free Software Foundation
9  * and appearing in the file LICENSE.LGPL included in the packaging
10  * of this file.
11  */
12 
13 #include <cerrno> // for errno
14 #include <cstring> // for strerror
15 #include <QGuiApplication>
16 #include <QKeyEvent>
17 #include <qpa/qplatformnativeinterface.h>
18 
19 #include "wayland-client.h"
20 #include <qwayland-input-method-unstable-v1.h>
21 #include <qwayland-text-input-unstable-v1.h>
22 
23 #include <xkbcommon/xkbcommon.h>
24 
25 #include "waylandinputmethodconnection.h"
26 
27 Q_LOGGING_CATEGORY(lcWaylandConnection, "maliit.connection.wayland")
28 
29 namespace {
30 
31 // TODO: Deduplicate it. Those values are used in
32 // minputcontextconnection, mimpluginmanager,
33 // mattributeextensionmanager and in input context implementations.
34 const char * const FocusStateAttribute = "focusState";
35 const char * const ContentTypeAttribute = "contentType";
36 const char * const CorrectionAttribute = "correctionEnabled";
37 const char * const PredictionAttribute = "predictionEnabled";
38 const char * const AutoCapitalizationAttribute = "autocapitalizationEnabled";
39 const char * const SurroundingTextAttribute = "surroundingText";
40 const char * const AnchorPositionAttribute = "anchorPosition";
41 const char * const CursorPositionAttribute = "cursorPosition";
42 const char * const HasSelectionAttribute = "hasSelection";
43 const char * const HiddenTextAttribute = "hiddenText";
44 
45 typedef QPair<Qt::KeyboardModifiers, const char *> Modifier;
46 const Modifier modifiers[] = {
47     Modifier(Qt::ShiftModifier, XKB_MOD_NAME_SHIFT),
48     Modifier(Qt::ControlModifier, XKB_MOD_NAME_CTRL),
49     Modifier(Qt::AltModifier, XKB_MOD_NAME_ALT),
50     Modifier(Qt::MetaModifier, XKB_MOD_NAME_LOGO),
51     Modifier(Qt::KeypadModifier, XKB_LED_NAME_NUM)
52 };
53 
modifiersMap()54 QByteArray modifiersMap()
55 {
56     QByteArray mod_map;
57     for (unsigned int iter(0); iter < (sizeof(modifiers) / sizeof(modifiers[0])); ++iter) {
58         mod_map.append(modifiers[iter].second);
59     }
60     return mod_map;
61 }
62 
modifiersFromQt(const Qt::KeyboardModifiers qt_mods)63 xkb_mod_mask_t modifiersFromQt(const Qt::KeyboardModifiers qt_mods)
64 {
65     xkb_mod_mask_t mod_mask(0);
66 
67     if (qt_mods == Qt::NoModifier) {
68         return mod_mask;
69     }
70 
71     for (unsigned int iter(0); iter < (sizeof(modifiers) / sizeof(modifiers[0])); ++iter) {
72         if ((qt_mods & modifiers[iter].first) == modifiers[iter].first) {
73             mod_mask |= 1 << iter;
74         }
75     }
76 
77     return mod_mask;
78 }
79 
keyFromQt(int qt_key)80 xkb_keysym_t keyFromQt(int qt_key)
81 {
82     switch (qt_key) {
83     case Qt::Key_Backspace:
84         return XKB_KEY_BackSpace;
85     case Qt::Key_Return:
86         return XKB_KEY_Return;
87     case Qt::Key_Left:
88         return XKB_KEY_Left;
89     case Qt::Key_Up:
90         return XKB_KEY_Up;
91     case Qt::Key_Right:
92         return XKB_KEY_Right;
93     case Qt::Key_Down:
94         return XKB_KEY_Down;
95     default:
96         return XKB_KEY_NoSymbol;
97     }
98 }
99 
preeditStyleFromMaliit(Maliit::PreeditFace face)100 QtWayland::zwp_text_input_v1::preedit_style preeditStyleFromMaliit(Maliit::PreeditFace face)
101 {
102     switch (face) {
103     case Maliit::PreeditDefault:
104         return QtWayland::zwp_text_input_v1::preedit_style_default;
105     case Maliit::PreeditNoCandidates:
106         return QtWayland::zwp_text_input_v1::preedit_style_incorrect;
107     case Maliit::PreeditKeyPress:
108         return QtWayland::zwp_text_input_v1::preedit_style_highlight;
109     case Maliit::PreeditUnconvertible:
110         return QtWayland::zwp_text_input_v1::preedit_style_inactive;
111     case Maliit::PreeditActive:
112         return QtWayland::zwp_text_input_v1::preedit_style_active;
113     default:
114         return QtWayland::zwp_text_input_v1::preedit_style_none;
115     }
116 }
117 
contentTypeFromWayland(uint32_t purpose)118 Maliit::TextContentType contentTypeFromWayland(uint32_t purpose)
119 {
120     switch (purpose) {
121     case QtWayland::zwp_text_input_v1::content_purpose_normal:
122         return Maliit::FreeTextContentType;
123     case QtWayland::zwp_text_input_v1::content_purpose_digits:
124     case QtWayland::zwp_text_input_v1::content_purpose_number:
125         return Maliit::NumberContentType;
126     case QtWayland::zwp_text_input_v1::content_purpose_phone:
127         return Maliit::PhoneNumberContentType;
128     case QtWayland::zwp_text_input_v1::content_purpose_url:
129         return Maliit::UrlContentType;
130     case QtWayland::zwp_text_input_v1::content_purpose_email:
131         return Maliit::EmailContentType;
132     default:
133         return Maliit::CustomContentType;
134     }
135 }
136 
matchesFlag(int value,int flag)137 bool matchesFlag(int value,
138                  int flag)
139 {
140     return ((value & flag) == flag);
141 }
142 
143 const unsigned int wayland_connection_id(1);
144 
145 } // unnamed namespace
146 
147 namespace Maliit {
148 namespace Wayland {
149 
150 class InputMethodContext;
151 
152 class InputMethod : public QtWayland::zwp_input_method_v1
153 {
154 public:
155     InputMethod(MInputContextConnection *connection, struct wl_registry *registry, int id);
156     ~InputMethod();
157 
158     InputMethodContext *context() const;
159 
160 protected:
161     void zwp_input_method_v1_activate(struct ::zwp_input_method_context_v1 *id) Q_DECL_OVERRIDE;
162     void zwp_input_method_v1_deactivate(struct ::zwp_input_method_context_v1 *context) Q_DECL_OVERRIDE;
163 
164 private:
165     MInputContextConnection *m_connection;
166     QScopedPointer<InputMethodContext> m_context;
167 };
168 
169 class InputMethodContext : public QtWayland::zwp_input_method_context_v1
170 {
171 public:
172     InputMethodContext(MInputContextConnection *connection, struct ::zwp_input_method_context_v1 *object);
173     ~InputMethodContext();
174 
175     QString selection() const;
176     uint32_t serial() const;
177 
178 protected:
179     void zwp_input_method_context_v1_commit_state(uint32_t serial) Q_DECL_OVERRIDE;
180     void zwp_input_method_context_v1_content_type(uint32_t hint, uint32_t purpose) Q_DECL_OVERRIDE;
181     void zwp_input_method_context_v1_invoke_action(uint32_t button, uint32_t index) Q_DECL_OVERRIDE;
182     void zwp_input_method_context_v1_preferred_language(const QString &language) Q_DECL_OVERRIDE;
183     void zwp_input_method_context_v1_reset() Q_DECL_OVERRIDE;
184     void zwp_input_method_context_v1_surrounding_text(const QString &text, uint32_t cursor, uint32_t anchor) Q_DECL_OVERRIDE;
185 
186 private:
187     MInputContextConnection *m_connection;
188     QVariantMap m_stateInfo;
189     uint32_t m_serial;
190     QString m_selection;
191 };
192 
193 }
194 }
195 
196 struct WaylandInputMethodConnectionPrivate
197 {
198     Q_DECLARE_PUBLIC(WaylandInputMethodConnection)
199 
200     WaylandInputMethodConnectionPrivate(WaylandInputMethodConnection *connection);
201     ~WaylandInputMethodConnectionPrivate();
202 
203     void handleRegistryGlobal(uint32_t name,
204                               const char *interface,
205                               uint32_t version);
206     void handleRegistryGlobalRemove(uint32_t name);
207 
208     Maliit::Wayland::InputMethodContext *context();
209 
210     WaylandInputMethodConnection *q_ptr;
211     wl_display *display;
212     wl_registry *registry;
213     QScopedPointer<Maliit::Wayland::InputMethod> input_method;
214 };
215 
216 namespace {
217 
registryGlobal(void * data,wl_registry * registry,uint32_t name,const char * interface,uint32_t version)218 void registryGlobal(void *data,
219                     wl_registry *registry,
220                     uint32_t name,
221                     const char *interface,
222                     uint32_t version)
223 {
224     WaylandInputMethodConnectionPrivate *d =
225             static_cast<WaylandInputMethodConnectionPrivate *>(data);
226 
227     Q_UNUSED(registry);
228     d->handleRegistryGlobal(name, interface, version);
229 }
230 
registryGlobalRemove(void * data,wl_registry * registry,uint32_t name)231 void registryGlobalRemove(void *data,
232                           wl_registry *registry,
233                           uint32_t name)
234 {
235     WaylandInputMethodConnectionPrivate *d =
236             static_cast<WaylandInputMethodConnectionPrivate *>(data);
237 
238     Q_UNUSED(registry);
239     d->handleRegistryGlobalRemove(name);
240 }
241 
242 const wl_registry_listener maliit_registry_listener = {
243     registryGlobal,
244     registryGlobalRemove
245 };
246 
247 
248 } // unnamed namespace
249 
WaylandInputMethodConnectionPrivate(WaylandInputMethodConnection * connection)250 WaylandInputMethodConnectionPrivate::WaylandInputMethodConnectionPrivate(WaylandInputMethodConnection *connection)
251     : q_ptr(connection),
252       display(0),
253       registry(0),
254       input_method()
255 {
256     display = static_cast<wl_display *>(QGuiApplication::platformNativeInterface()->nativeResourceForIntegration("display"));
257     if (!display) {
258         qCritical() << Q_FUNC_INFO << "Failed to get a display.";
259         return;
260     }
261     registry = wl_display_get_registry(display);
262     wl_registry_add_listener(registry, &maliit_registry_listener, this);
263 }
264 
~WaylandInputMethodConnectionPrivate()265 WaylandInputMethodConnectionPrivate::~WaylandInputMethodConnectionPrivate()
266 {
267     input_method.reset();
268     if (registry) {
269         wl_registry_destroy(registry);
270     }
271 }
272 
handleRegistryGlobal(uint32_t name,const char * interface,uint32_t version)273 void WaylandInputMethodConnectionPrivate::handleRegistryGlobal(uint32_t name,
274                                                                const char *interface,
275                                                                uint32_t version)
276 {
277     Q_UNUSED(version);
278     Q_Q(WaylandInputMethodConnection);
279 
280     if (!strcmp(interface, "zwp_input_method_v1")) {
281         input_method.reset(new Maliit::Wayland::InputMethod(q, registry, name));
282     }
283 }
284 
handleRegistryGlobalRemove(uint32_t name)285 void WaylandInputMethodConnectionPrivate::handleRegistryGlobalRemove(uint32_t name)
286 {
287     qCDebug(lcWaylandConnection) << Q_FUNC_INFO << name;
288 }
289 
context()290 Maliit::Wayland::InputMethodContext *WaylandInputMethodConnectionPrivate::context()
291 {
292     return input_method ? input_method->context() : 0;
293 }
294 
295 // MInputContextWestonIMProtocolConnection
296 
WaylandInputMethodConnection()297 WaylandInputMethodConnection::WaylandInputMethodConnection()
298     : d_ptr(new WaylandInputMethodConnectionPrivate(this))
299 {
300 }
301 
~WaylandInputMethodConnection()302 WaylandInputMethodConnection::~WaylandInputMethodConnection()
303 {
304 }
305 
sendPreeditString(const QString & string,const QList<Maliit::PreeditTextFormat> & preedit_formats,int replace_start,int replace_length,int cursor_pos)306 void WaylandInputMethodConnection::sendPreeditString(const QString &string,
307                                                      const QList<Maliit::PreeditTextFormat> &preedit_formats,
308                                                      int replace_start,
309                                                      int replace_length,
310                                                      int cursor_pos)
311 {
312     Q_D(WaylandInputMethodConnection);
313 
314     qCDebug(lcWaylandConnection) << Q_FUNC_INFO << string << replace_start << replace_length << cursor_pos;
315 
316     if (!d->context())
317         return;
318 
319     MInputContextConnection::sendPreeditString(string, preedit_formats,
320                                                replace_start, replace_length,
321                                                cursor_pos);
322 
323     if (replace_length > 0) {
324         int cursor = widgetState().value(CursorPositionAttribute).toInt();
325         uint32_t index = string.midRef(qMin(cursor + replace_start, cursor), qAbs(replace_start)).toUtf8().size();
326         uint32_t length = string.midRef(cursor + replace_start, replace_length).toUtf8().size();
327         d->context()->delete_surrounding_text(index, length);
328     }
329 
330     Q_FOREACH (const Maliit::PreeditTextFormat& format, preedit_formats) {
331         QtWayland::zwp_text_input_v1::preedit_style style = preeditStyleFromMaliit(format.preeditFace);
332         uint32_t index = string.leftRef(format.start).toUtf8().size();
333         uint32_t length = string.leftRef(format.start + format.length).toUtf8().size() - index;
334         qCDebug(lcWaylandConnection) << Q_FUNC_INFO << "preedit_styling" << index << length;
335         d->context()->preedit_styling(index, length, style);
336     }
337 
338     // TODO check if defined like that/required
339     if (cursor_pos < 0) {
340         cursor_pos = string.size() + 1 - cursor_pos;
341     }
342 
343     qCDebug(lcWaylandConnection) << Q_FUNC_INFO << "preedit_cursor" << string.leftRef(cursor_pos).toUtf8().size();
344     d->context()->preedit_cursor(string.leftRef(cursor_pos).toUtf8().size());
345     qCDebug(lcWaylandConnection) << Q_FUNC_INFO << "preedit_string" << string;
346     d->context()->preedit_string(d->context()->serial(), string, string);
347 }
348 
349 
sendCommitString(const QString & string,int replace_start,int replace_length,int cursor_pos)350 void WaylandInputMethodConnection::sendCommitString(const QString &string,
351                                                     int replace_start,
352                                                     int replace_length,
353                                                     int cursor_pos)
354 {
355     Q_D(WaylandInputMethodConnection);
356 
357     qCDebug(lcWaylandConnection) << Q_FUNC_INFO << string << replace_start << replace_length << cursor_pos;
358 
359     if (!d->context())
360         return;
361 
362     MInputContextConnection::sendCommitString(string, replace_start, replace_length, cursor_pos);
363 
364     if (cursor_pos != 0) {
365         qCWarning(lcWaylandConnection) << Q_FUNC_INFO << "cursor_pos:" << cursor_pos << "!= 0 not supported yet";
366         cursor_pos = 0;
367     }
368 
369     if (replace_length > 0) {
370         int cursor = widgetState().value(CursorPositionAttribute).toInt();
371         uint32_t index = string.midRef(qMin(cursor + replace_start, cursor), qAbs(replace_start)).toUtf8().size();
372         uint32_t length = string.midRef(cursor + replace_start, replace_length).toUtf8().size();
373         d->context()->delete_surrounding_text(index, length);
374     }
375 
376     cursor_pos = string.leftRef(cursor_pos).toUtf8().size();
377     d->context()->cursor_position(cursor_pos, cursor_pos);
378     d->context()->commit_string(d->context()->serial(), string);
379 }
380 
sendKeyEvent(const QKeyEvent & keyEvent,Maliit::EventRequestType requestType)381 void WaylandInputMethodConnection::sendKeyEvent(const QKeyEvent &keyEvent,
382                                                 Maliit::EventRequestType requestType)
383 {
384     Q_D(WaylandInputMethodConnection);
385 
386     qCDebug(lcWaylandConnection) << Q_FUNC_INFO;
387 
388     if (!d->context())
389         return;
390 
391     xkb_keysym_t sym(keyFromQt(keyEvent.key()));
392 
393     if (sym == XKB_KEY_NoSymbol) {
394         qCWarning(lcWaylandConnection) << "No conversion from Qt::Key:" << keyEvent.key() << "to XKB key. Update the keyFromQt() function.";
395         return;
396     }
397 
398     wl_keyboard_key_state state;
399 
400     switch (keyEvent.type()) {
401     case QEvent::KeyPress:
402         state = WL_KEYBOARD_KEY_STATE_PRESSED;
403         break;
404 
405     case QEvent::KeyRelease:
406         state = WL_KEYBOARD_KEY_STATE_RELEASED;
407         break;
408 
409     default:
410         qCWarning(lcWaylandConnection) << "Unknown QKeyEvent type:" << keyEvent.type();
411         return;
412     }
413 
414     xkb_mod_mask_t modifiers(modifiersFromQt(keyEvent.modifiers()));
415 
416     MInputContextConnection::sendKeyEvent(keyEvent, requestType);
417 
418     d->context()->keysym(d->context()->serial(),
419                          keyEvent.timestamp(),
420                          sym, state, modifiers);
421 }
422 
selection(bool & valid)423 QString WaylandInputMethodConnection::selection(bool &valid)
424 {
425     Q_D(WaylandInputMethodConnection);
426 
427     qCDebug(lcWaylandConnection) << Q_FUNC_INFO;
428 
429     Maliit::Wayland::InputMethodContext *context = d->input_method->context();
430 
431     valid = context && !context->selection().isEmpty();
432     return context ? context->selection() : QString();
433 }
434 
setLanguage(const QString & language)435 void WaylandInputMethodConnection::setLanguage(const QString &language)
436 {
437     Q_D(WaylandInputMethodConnection);
438 
439     qCDebug(lcWaylandConnection) << Q_FUNC_INFO;
440 
441     if (!d->context())
442         return;
443 
444     d->context()->language(d->context()->serial(), language);
445 }
446 
setSelection(int start,int length)447 void WaylandInputMethodConnection::setSelection(int start, int length)
448 {
449     Q_D (WaylandInputMethodConnection);
450 
451     qCDebug(lcWaylandConnection) << Q_FUNC_INFO;
452 
453     if (!d->context())
454         return;
455 
456     QString surrounding = widgetState().value(SurroundingTextAttribute).toString();
457     uint32_t index(surrounding.leftRef(start + length).toUtf8().size());
458     uint32_t anchor(surrounding.leftRef(start).toUtf8().size());
459 
460     d->context()->cursor_position(index, anchor);
461     d->context()->commit_string(d->context()->serial(), QString());
462 }
463 
464 namespace Maliit {
465 namespace Wayland {
466 
InputMethod(MInputContextConnection * connection,struct wl_registry * registry,int id)467 InputMethod::InputMethod(MInputContextConnection *connection, struct wl_registry *registry, int id)
468     : QtWayland::zwp_input_method_v1(registry, id, 1)
469     , m_connection(connection)
470     , m_context()
471 {
472     qCDebug(lcWaylandConnection) << Q_FUNC_INFO;
473 }
474 
~InputMethod()475 InputMethod::~InputMethod()
476 {
477 }
478 
context() const479 InputMethodContext *InputMethod::context() const
480 {
481     return m_context.data();
482 }
483 
zwp_input_method_v1_activate(struct::zwp_input_method_context_v1 * id)484 void InputMethod::zwp_input_method_v1_activate(struct ::zwp_input_method_context_v1 *id)
485 {
486     qCDebug(lcWaylandConnection) << Q_FUNC_INFO;
487 
488     m_context.reset(new InputMethodContext(m_connection, id));
489 
490     m_context->modifiers_map(modifiersMap());
491 
492 }
493 
zwp_input_method_v1_deactivate(struct zwp_input_method_context_v1 *)494 void InputMethod::zwp_input_method_v1_deactivate(struct zwp_input_method_context_v1 *)
495 {
496     qCDebug(lcWaylandConnection) << Q_FUNC_INFO;
497 
498     m_context.reset();
499 
500     m_connection->handleDisconnection(wayland_connection_id);
501 }
502 
InputMethodContext(MInputContextConnection * connection,struct::zwp_input_method_context_v1 * object)503 InputMethodContext::InputMethodContext(MInputContextConnection *connection, struct ::zwp_input_method_context_v1 *object)
504     : QtWayland::zwp_input_method_context_v1(object)
505     , m_connection(connection)
506     , m_stateInfo()
507     , m_serial(0)
508     , m_selection()
509 {
510     qCDebug(lcWaylandConnection) << Q_FUNC_INFO;
511 
512     m_stateInfo[FocusStateAttribute] = true;
513     m_connection->activateContext(wayland_connection_id);
514     m_connection->showInputMethod(wayland_connection_id);
515 }
516 
~InputMethodContext()517 InputMethodContext::~InputMethodContext()
518 {
519     qCDebug(lcWaylandConnection) << Q_FUNC_INFO;
520 
521     m_stateInfo.clear();
522     m_stateInfo[FocusStateAttribute] = false;
523     m_connection->updateWidgetInformation(wayland_connection_id, m_stateInfo, true);
524     m_connection->hideInputMethod(wayland_connection_id);
525 }
526 
selection() const527 QString InputMethodContext::selection() const
528 {
529     return m_selection;
530 }
531 
serial() const532 uint32_t InputMethodContext::serial() const
533 {
534     return m_serial;
535 }
536 
zwp_input_method_context_v1_commit_state(uint32_t serial)537 void InputMethodContext::zwp_input_method_context_v1_commit_state(uint32_t serial)
538 {
539     qCDebug(lcWaylandConnection) << Q_FUNC_INFO;
540 
541     m_serial = serial;
542     m_connection->updateWidgetInformation(wayland_connection_id, m_stateInfo, false);
543 }
544 
zwp_input_method_context_v1_content_type(uint32_t hint,uint32_t purpose)545 void InputMethodContext::zwp_input_method_context_v1_content_type(uint32_t hint, uint32_t purpose)
546 {
547     qCDebug(lcWaylandConnection) << Q_FUNC_INFO;
548 
549     m_stateInfo[ContentTypeAttribute] = contentTypeFromWayland(purpose);
550     m_stateInfo[AutoCapitalizationAttribute] = matchesFlag(hint, QtWayland::zwp_text_input_v1::content_hint_auto_capitalization);
551     m_stateInfo[CorrectionAttribute] = matchesFlag(hint, QtWayland::zwp_text_input_v1::content_hint_auto_correction);
552     m_stateInfo[PredictionAttribute] = matchesFlag(hint, QtWayland::zwp_text_input_v1::content_hint_auto_completion);
553     m_stateInfo[HiddenTextAttribute] = matchesFlag(hint, QtWayland::zwp_text_input_v1::content_hint_hidden_text);
554 }
555 
zwp_input_method_context_v1_invoke_action(uint32_t button,uint32_t index)556 void InputMethodContext::zwp_input_method_context_v1_invoke_action(uint32_t button, uint32_t index)
557 {
558     qCDebug(lcWaylandConnection) << Q_FUNC_INFO << button << index;
559 }
560 
zwp_input_method_context_v1_preferred_language(const QString & language)561 void InputMethodContext::zwp_input_method_context_v1_preferred_language(const QString &language)
562 {
563     qCDebug(lcWaylandConnection) << Q_FUNC_INFO << language;
564 }
565 
zwp_input_method_context_v1_reset()566 void InputMethodContext::zwp_input_method_context_v1_reset()
567 {
568     qCDebug(lcWaylandConnection) << Q_FUNC_INFO;
569 
570     m_connection->reset(wayland_connection_id);
571     m_connection->showInputMethod(wayland_connection_id);
572 }
573 
zwp_input_method_context_v1_surrounding_text(const QString & text,uint32_t cursor,uint32_t anchor)574 void InputMethodContext::zwp_input_method_context_v1_surrounding_text(const QString &text, uint32_t cursor, uint32_t anchor)
575 {
576     qCDebug(lcWaylandConnection) << Q_FUNC_INFO;
577 
578     const QByteArray &utf8_text(text.toUtf8());
579 
580     m_stateInfo[SurroundingTextAttribute] = text;
581     m_stateInfo[CursorPositionAttribute] = QString::fromUtf8(utf8_text.constData(), cursor).size();
582     m_stateInfo[AnchorPositionAttribute] = QString::fromUtf8(utf8_text.constData(), anchor).size();
583     if (cursor == anchor) {
584         m_stateInfo[HasSelectionAttribute] = false;
585         m_selection.clear();
586     } else {
587         m_stateInfo[HasSelectionAttribute] = true;
588         uint32_t begin = qMin(anchor, cursor);
589         uint32_t end = qMax(anchor, cursor);
590         m_selection = QString::fromUtf8(utf8_text.constData() + begin, end - begin);
591     }
592 }
593 
594 }
595 }
596