2017-10-27 13:15:37 +02:00
|
|
|
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
|
|
/*
|
|
|
|
* This file is part of the LibreOffice project.
|
|
|
|
*
|
|
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
*
|
|
|
|
* This file incorporates work covered by the following license notice:
|
|
|
|
*
|
|
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
|
|
* contributor license agreements. See the NOTICE file distributed
|
|
|
|
* with this work for additional information regarding copyright
|
|
|
|
* ownership. The ASF licenses this file to you under the Apache
|
|
|
|
* License, Version 2.0 (the "License"); you may not use this file
|
|
|
|
* except in compliance with the License. You may obtain a copy of
|
|
|
|
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
|
|
|
|
*/
|
|
|
|
|
2018-06-01 15:28:26 +02:00
|
|
|
#include <Qt5Widget.hxx>
|
2017-10-30 18:45:46 +01:00
|
|
|
#include <Qt5Widget.moc>
|
2017-10-27 13:15:37 +02:00
|
|
|
|
2018-06-01 15:28:26 +02:00
|
|
|
#include <Qt5Frame.hxx>
|
|
|
|
#include <Qt5Graphics.hxx>
|
2019-05-12 14:30:06 +02:00
|
|
|
#include <Qt5Instance.hxx>
|
2019-05-24 15:36:39 +00:00
|
|
|
#include <Qt5SvpGraphics.hxx>
|
2019-08-08 17:59:20 +00:00
|
|
|
#include <Qt5Transferable.hxx>
|
2018-06-01 15:28:26 +02:00
|
|
|
#include <Qt5Tools.hxx>
|
2017-10-31 01:07:06 +01:00
|
|
|
|
2018-12-12 12:33:26 +00:00
|
|
|
#include <QtCore/QMimeData>
|
|
|
|
#include <QtGui/QDrag>
|
2017-11-07 13:18:05 +01:00
|
|
|
#include <QtGui/QFocusEvent>
|
2019-05-04 00:55:24 +00:00
|
|
|
#include <QtGui/QGuiApplication>
|
2017-10-31 01:07:06 +01:00
|
|
|
#include <QtGui/QImage>
|
2017-11-07 13:18:05 +01:00
|
|
|
#include <QtGui/QKeyEvent>
|
2017-10-30 19:55:18 +01:00
|
|
|
#include <QtGui/QMouseEvent>
|
2017-10-31 01:07:06 +01:00
|
|
|
#include <QtGui/QPainter>
|
|
|
|
#include <QtGui/QPaintEvent>
|
2018-10-31 15:13:42 +03:00
|
|
|
#include <QtGui/QResizeEvent>
|
2017-11-07 13:13:10 +01:00
|
|
|
#include <QtGui/QShowEvent>
|
2019-05-03 21:18:34 +02:00
|
|
|
#include <QtGui/QTextCharFormat>
|
2017-10-30 19:55:18 +01:00
|
|
|
#include <QtGui/QWheelEvent>
|
2018-05-14 15:39:30 +02:00
|
|
|
#include <QtWidgets/QMainWindow>
|
2018-12-12 12:33:26 +00:00
|
|
|
#include <QtWidgets/QWidget>
|
2017-10-31 01:07:06 +01:00
|
|
|
|
2017-10-24 19:49:45 +02:00
|
|
|
#include <cairo.h>
|
2019-01-04 15:40:06 +01:00
|
|
|
#include <vcl/commandevent.hxx>
|
2019-01-07 10:03:39 +01:00
|
|
|
#include <vcl/event.hxx>
|
2019-05-04 01:04:35 +00:00
|
|
|
#include <window.h>
|
2019-06-18 09:33:28 +02:00
|
|
|
#include <tools/diagnose_ex.h>
|
2019-05-04 01:04:35 +00:00
|
|
|
|
|
|
|
#include <com/sun/star/accessibility/XAccessibleContext.hpp>
|
|
|
|
#include <com/sun/star/accessibility/XAccessibleEditableText.hpp>
|
|
|
|
|
|
|
|
using namespace com::sun::star;
|
2017-10-24 19:49:45 +02:00
|
|
|
|
2018-08-10 18:29:10 +02:00
|
|
|
void Qt5Widget::paintEvent(QPaintEvent* pEvent)
|
2017-10-27 13:15:37 +02:00
|
|
|
{
|
2018-08-10 18:29:10 +02:00
|
|
|
QPainter p(this);
|
2019-05-05 23:22:09 +00:00
|
|
|
if (!m_rFrame.m_bNullRegion)
|
|
|
|
p.setClipRegion(m_rFrame.m_aRegion);
|
2018-09-04 09:17:11 +02:00
|
|
|
|
2019-05-05 23:22:09 +00:00
|
|
|
if (m_rFrame.m_bUseCairo)
|
2017-10-24 19:49:45 +02:00
|
|
|
{
|
2019-05-05 23:22:09 +00:00
|
|
|
cairo_surface_t* pSurface = m_rFrame.m_pSurface.get();
|
2017-10-24 19:49:45 +02:00
|
|
|
cairo_surface_flush(pSurface);
|
|
|
|
|
2018-08-10 18:29:10 +02:00
|
|
|
QImage aImage(cairo_image_surface_get_data(pSurface), size().width(), size().height(),
|
|
|
|
Qt5_DefaultFormat32);
|
2017-10-24 19:49:45 +02:00
|
|
|
p.drawImage(pEvent->rect().topLeft(), aImage, pEvent->rect());
|
|
|
|
}
|
|
|
|
else
|
2019-05-05 23:22:09 +00:00
|
|
|
p.drawImage(pEvent->rect().topLeft(), *m_rFrame.m_pQImage, pEvent->rect());
|
2017-10-31 01:07:06 +01:00
|
|
|
}
|
|
|
|
|
2018-10-31 15:13:42 +03:00
|
|
|
void Qt5Widget::resizeEvent(QResizeEvent* pEvent)
|
2017-10-31 01:07:06 +01:00
|
|
|
{
|
2019-06-21 04:09:02 +00:00
|
|
|
const int nWidth = pEvent->size().width();
|
|
|
|
const int nHeight = pEvent->size().height();
|
|
|
|
|
|
|
|
m_rFrame.maGeometry.nWidth = nWidth;
|
|
|
|
m_rFrame.maGeometry.nHeight = nHeight;
|
|
|
|
|
2019-05-05 23:22:09 +00:00
|
|
|
if (m_rFrame.m_bUseCairo)
|
2017-10-24 19:49:45 +02:00
|
|
|
{
|
2019-05-05 23:22:09 +00:00
|
|
|
if (m_rFrame.m_pSvpGraphics)
|
2018-08-09 17:14:14 +02:00
|
|
|
{
|
|
|
|
cairo_surface_t* pSurface
|
2019-06-21 04:09:02 +00:00
|
|
|
= cairo_image_surface_create(CAIRO_FORMAT_ARGB32, nWidth, nHeight);
|
2018-08-09 17:14:14 +02:00
|
|
|
cairo_surface_set_user_data(pSurface, SvpSalGraphics::getDamageKey(),
|
2019-05-05 23:22:09 +00:00
|
|
|
&m_rFrame.m_aDamageHandler, nullptr);
|
2019-06-21 04:09:02 +00:00
|
|
|
m_rFrame.m_pSvpGraphics->setSurface(pSurface, basegfx::B2IVector(nWidth, nHeight));
|
2019-05-05 23:22:09 +00:00
|
|
|
UniqueCairoSurface old_surface(m_rFrame.m_pSurface.release());
|
|
|
|
m_rFrame.m_pSurface.reset(pSurface);
|
2018-10-31 15:13:42 +03:00
|
|
|
|
2019-06-21 04:09:02 +00:00
|
|
|
int min_width = qMin(pEvent->oldSize().width(), nWidth);
|
|
|
|
int min_height = qMin(pEvent->oldSize().height(), nHeight);
|
2018-10-31 15:13:42 +03:00
|
|
|
|
|
|
|
SalTwoRect rect(0, 0, min_width, min_height, 0, 0, min_width, min_height);
|
|
|
|
|
2019-05-05 23:22:09 +00:00
|
|
|
m_rFrame.m_pSvpGraphics->copySource(rect, old_surface.get());
|
2018-08-09 17:14:14 +02:00
|
|
|
}
|
2017-10-24 19:49:45 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2018-11-07 17:48:06 +03:00
|
|
|
QImage* pImage = nullptr;
|
|
|
|
|
2019-05-05 23:22:09 +00:00
|
|
|
if (m_rFrame.m_pQImage)
|
2019-06-21 04:09:02 +00:00
|
|
|
pImage = new QImage(m_rFrame.m_pQImage->copy(0, 0, nWidth, nHeight));
|
2018-11-07 17:48:06 +03:00
|
|
|
else
|
|
|
|
{
|
2019-06-21 04:09:02 +00:00
|
|
|
pImage = new QImage(nWidth, nHeight, Qt5_DefaultFormat32);
|
2018-11-07 17:48:06 +03:00
|
|
|
pImage->fill(Qt::transparent);
|
|
|
|
}
|
|
|
|
|
2019-05-05 23:22:09 +00:00
|
|
|
m_rFrame.m_pQt5Graphics->ChangeQImage(pImage);
|
|
|
|
m_rFrame.m_pQImage.reset(pImage);
|
2017-10-24 19:49:45 +02:00
|
|
|
}
|
|
|
|
|
2019-05-05 23:22:09 +00:00
|
|
|
m_rFrame.CallCallback(SalEvent::Resize, nullptr);
|
2017-10-31 01:07:06 +01:00
|
|
|
}
|
|
|
|
|
2019-09-28 10:32:30 +02:00
|
|
|
void Qt5Widget::handleMouseButtonEvent(const Qt5Frame& rFrame, const QMouseEvent* pEvent,
|
2019-05-30 13:39:11 +00:00
|
|
|
const ButtonKeyState eState)
|
2017-10-30 19:55:18 +01:00
|
|
|
{
|
|
|
|
SalMouseEvent aEvent;
|
|
|
|
switch (pEvent->button())
|
|
|
|
{
|
|
|
|
case Qt::LeftButton:
|
|
|
|
aEvent.mnButton = MOUSE_LEFT;
|
|
|
|
break;
|
|
|
|
case Qt::MidButton:
|
|
|
|
aEvent.mnButton = MOUSE_MIDDLE;
|
|
|
|
break;
|
|
|
|
case Qt::RightButton:
|
|
|
|
aEvent.mnButton = MOUSE_RIGHT;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
aEvent.mnTime = pEvent->timestamp();
|
2019-06-06 02:55:22 +00:00
|
|
|
aEvent.mnX = static_cast<long>(QGuiApplication::isLeftToRight()
|
|
|
|
? pEvent->pos().x()
|
|
|
|
: rFrame.GetQWidget()->width() - pEvent->pos().x());
|
2018-01-12 20:10:40 +01:00
|
|
|
aEvent.mnY = static_cast<long>(pEvent->pos().y());
|
2017-10-30 19:55:18 +01:00
|
|
|
aEvent.mnCode = GetKeyModCode(pEvent->modifiers()) | GetMouseModCode(pEvent->buttons());
|
|
|
|
|
|
|
|
SalEvent nEventType;
|
2019-05-30 13:39:11 +00:00
|
|
|
if (eState == ButtonKeyState::Pressed)
|
2017-10-30 19:55:18 +01:00
|
|
|
nEventType = SalEvent::MouseButtonDown;
|
2019-05-30 13:39:11 +00:00
|
|
|
else
|
|
|
|
nEventType = SalEvent::MouseButtonUp;
|
|
|
|
rFrame.CallCallback(nEventType, &aEvent);
|
2017-10-30 19:55:18 +01:00
|
|
|
}
|
|
|
|
|
2019-05-30 13:39:11 +00:00
|
|
|
void Qt5Widget::mousePressEvent(QMouseEvent* pEvent) { handleMousePressEvent(m_rFrame, pEvent); }
|
2017-10-30 19:55:18 +01:00
|
|
|
|
2019-05-30 13:39:11 +00:00
|
|
|
void Qt5Widget::mouseReleaseEvent(QMouseEvent* pEvent)
|
|
|
|
{
|
|
|
|
handleMouseReleaseEvent(m_rFrame, pEvent);
|
|
|
|
}
|
2017-10-30 19:55:18 +01:00
|
|
|
|
2018-08-10 18:29:10 +02:00
|
|
|
void Qt5Widget::mouseMoveEvent(QMouseEvent* pEvent)
|
2017-10-30 19:55:18 +01:00
|
|
|
{
|
2018-10-12 16:19:07 +02:00
|
|
|
QPoint point = pEvent->pos();
|
|
|
|
|
2017-10-30 19:55:18 +01:00
|
|
|
SalMouseEvent aEvent;
|
|
|
|
aEvent.mnTime = pEvent->timestamp();
|
2019-06-06 02:55:22 +00:00
|
|
|
aEvent.mnX = QGuiApplication::isLeftToRight() ? point.x() : width() - point.x();
|
2018-10-12 16:19:07 +02:00
|
|
|
aEvent.mnY = point.y();
|
2017-10-30 19:55:18 +01:00
|
|
|
aEvent.mnCode = GetKeyModCode(pEvent->modifiers()) | GetMouseModCode(pEvent->buttons());
|
|
|
|
aEvent.mnButton = 0;
|
|
|
|
|
2019-05-05 23:22:09 +00:00
|
|
|
m_rFrame.CallCallback(SalEvent::MouseMove, &aEvent);
|
2017-10-30 19:55:18 +01:00
|
|
|
pEvent->accept();
|
|
|
|
}
|
|
|
|
|
2018-08-10 18:29:10 +02:00
|
|
|
void Qt5Widget::wheelEvent(QWheelEvent* pEvent)
|
2017-10-30 19:55:18 +01:00
|
|
|
{
|
|
|
|
SalWheelMouseEvent aEvent;
|
|
|
|
|
|
|
|
aEvent.mnTime = pEvent->timestamp();
|
|
|
|
aEvent.mnX = pEvent->pos().x();
|
|
|
|
aEvent.mnY = pEvent->pos().y();
|
|
|
|
aEvent.mnCode = GetKeyModCode(pEvent->modifiers()) | GetMouseModCode(pEvent->buttons());
|
|
|
|
|
2019-06-06 01:01:11 +00:00
|
|
|
// mouse wheel ticks are 120, which we map to 3 lines.
|
|
|
|
// we have to accumulate for touch scroll to keep track of the absolute delta.
|
|
|
|
|
|
|
|
int nDelta = pEvent->angleDelta().y(), lines;
|
|
|
|
aEvent.mbHorz = nDelta == 0;
|
|
|
|
if (aEvent.mbHorz)
|
|
|
|
{
|
2019-08-13 11:34:40 +02:00
|
|
|
nDelta = (QGuiApplication::isLeftToRight() ? 1 : -1) * pEvent->angleDelta().x();
|
2019-06-06 01:01:11 +00:00
|
|
|
if (!nDelta)
|
|
|
|
return;
|
|
|
|
|
|
|
|
m_nDeltaX += nDelta;
|
|
|
|
lines = m_nDeltaX / 40;
|
|
|
|
m_nDeltaX = m_nDeltaX % 40;
|
|
|
|
}
|
|
|
|
else
|
2017-10-30 19:55:18 +01:00
|
|
|
{
|
2019-06-06 01:01:11 +00:00
|
|
|
m_nDeltaY += nDelta;
|
|
|
|
lines = m_nDeltaY / 40;
|
|
|
|
m_nDeltaY = m_nDeltaY % 40;
|
2017-10-30 19:55:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
aEvent.mnDelta = nDelta;
|
2019-06-06 01:01:11 +00:00
|
|
|
aEvent.mnNotchDelta = nDelta < 0 ? -1 : 1;
|
|
|
|
aEvent.mnScrollLines = std::abs(lines);
|
2017-10-30 19:55:18 +01:00
|
|
|
|
2019-05-05 23:22:09 +00:00
|
|
|
m_rFrame.CallCallback(SalEvent::WheelMouse, &aEvent);
|
2017-10-30 19:55:18 +01:00
|
|
|
pEvent->accept();
|
|
|
|
}
|
|
|
|
|
2018-10-12 16:19:07 +02:00
|
|
|
void Qt5Widget::dragEnterEvent(QDragEnterEvent* event)
|
|
|
|
{
|
2019-08-08 17:59:20 +00:00
|
|
|
if (dynamic_cast<const Qt5MimeData*>(event->mimeData()))
|
2018-10-12 16:19:07 +02:00
|
|
|
event->accept();
|
2019-01-25 12:24:50 +01:00
|
|
|
else
|
|
|
|
event->acceptProposedAction();
|
2018-10-12 16:19:07 +02:00
|
|
|
}
|
|
|
|
|
2019-08-08 17:59:20 +00:00
|
|
|
// also called when a drop is rejected
|
|
|
|
void Qt5Widget::dragLeaveEvent(QDragLeaveEvent*) { m_rFrame.handleDragLeave(); }
|
2018-10-15 14:44:29 +02:00
|
|
|
|
2019-08-08 17:59:20 +00:00
|
|
|
void Qt5Widget::dragMoveEvent(QDragMoveEvent* pEvent) { m_rFrame.handleDragMove(pEvent); }
|
2018-10-18 01:01:14 +02:00
|
|
|
|
2019-08-08 17:59:20 +00:00
|
|
|
void Qt5Widget::dropEvent(QDropEvent* pEvent) { m_rFrame.handleDrop(pEvent); }
|
2018-10-12 16:19:07 +02:00
|
|
|
|
2019-08-15 10:35:33 +00:00
|
|
|
void Qt5Widget::moveEvent(QMoveEvent* pEvent)
|
2019-06-21 04:09:02 +00:00
|
|
|
{
|
|
|
|
if (m_rFrame.m_pTopLevel)
|
|
|
|
return;
|
|
|
|
|
2019-08-15 10:35:33 +00:00
|
|
|
m_rFrame.maGeometry.nX = pEvent->pos().x();
|
|
|
|
m_rFrame.maGeometry.nY = pEvent->pos().y();
|
2019-06-21 04:09:02 +00:00
|
|
|
m_rFrame.CallCallback(SalEvent::Move, nullptr);
|
|
|
|
}
|
2017-10-30 19:55:18 +01:00
|
|
|
|
2018-08-10 18:29:10 +02:00
|
|
|
void Qt5Widget::showEvent(QShowEvent*)
|
2017-11-07 13:13:10 +01:00
|
|
|
{
|
2019-05-05 23:22:09 +00:00
|
|
|
QSize aSize(m_rFrame.GetQWidget()->size());
|
tdf#128710 Qt5 don't force immediate paint on show
I tried to build a minimal reproducer with QMainWindow and a
QDialog and miserably failed. All variants of show, hide, modal
change, move, resize, processEvents(QEventLoop::AllEvents) just
worked. I was able to produce "qt.qpa.xcb: QXcbConnection: XCB
error: 3 (BadWindow) ... major code: 18 (ChangeProperty)" entries
in my ~/.xsession-errors, but that didn't matter for my program.
Then I started to comment code blocks in Qt5Frame and after that
continued in Qt5Widget, found that ShowEvent produced a modal
window, albeit with an obviously missing paint event. But that is
fixable by just queuing one in LO.
I tried to debug that broken behaviour to find the real cause of
the bug, but as I already knew from commit e770bacc85a0 ("Qt5
workaround modal change after show bug"), this bug has some timing
related component, so that sadly didn't result in any conclusion.
Change-Id: Iaec45997179365fae5430120f86e435aa5e88447
Reviewed-on: https://gerrit.libreoffice.org/82745
Tested-by: Jenkins
Reviewed-by: Michael Weghorn <m.weghorn@posteo.de>
Tested-by: Michael Weghorn <m.weghorn@posteo.de>
Reviewed-by: Jan-Marek Glogowski <glogow@fbihome.de>
2019-11-14 23:25:10 +01:00
|
|
|
// forcing an immediate update somehow interferes with the hide + show
|
|
|
|
// sequence from Qt5Frame::SetModal, if the frame was already set visible,
|
|
|
|
// resulting in a hidden / unmapped window
|
|
|
|
SalPaintEvent aPaintEvt(0, 0, aSize.width(), aSize.height());
|
2019-05-05 23:22:09 +00:00
|
|
|
m_rFrame.CallCallback(SalEvent::Paint, &aPaintEvt);
|
2017-11-07 13:13:10 +01:00
|
|
|
}
|
2017-10-30 19:55:18 +01:00
|
|
|
|
2018-08-10 18:29:10 +02:00
|
|
|
void Qt5Widget::closeEvent(QCloseEvent* /*pEvent*/)
|
2018-04-16 13:30:23 +02:00
|
|
|
{
|
2019-05-05 23:22:09 +00:00
|
|
|
m_rFrame.CallCallback(SalEvent::Close, nullptr);
|
2018-04-16 13:30:23 +02:00
|
|
|
}
|
|
|
|
|
2019-02-15 20:41:07 +01:00
|
|
|
static sal_uInt16 GetKeyCode(int keyval, Qt::KeyboardModifiers modifiers)
|
2017-11-07 13:18:05 +01:00
|
|
|
{
|
|
|
|
sal_uInt16 nCode = 0;
|
|
|
|
if (keyval >= Qt::Key_0 && keyval <= Qt::Key_9)
|
|
|
|
nCode = KEY_0 + (keyval - Qt::Key_0);
|
|
|
|
else if (keyval >= Qt::Key_A && keyval <= Qt::Key_Z)
|
|
|
|
nCode = KEY_A + (keyval - Qt::Key_A);
|
|
|
|
else if (keyval >= Qt::Key_F1 && keyval <= Qt::Key_F26)
|
|
|
|
nCode = KEY_F1 + (keyval - Qt::Key_F1);
|
2019-02-15 20:41:07 +01:00
|
|
|
else if (modifiers.testFlag(Qt::KeypadModifier)
|
|
|
|
&& (keyval == Qt::Key_Period || keyval == Qt::Key_Comma))
|
|
|
|
// Qt doesn't use a special keyval for decimal separator ("," or ".")
|
|
|
|
// on numerical keypad, but sets Qt::KeypadModifier in addition
|
|
|
|
nCode = KEY_DECIMAL;
|
2017-11-07 13:18:05 +01:00
|
|
|
else
|
|
|
|
{
|
|
|
|
switch (keyval)
|
|
|
|
{
|
|
|
|
case Qt::Key_Down:
|
|
|
|
nCode = KEY_DOWN;
|
|
|
|
break;
|
|
|
|
case Qt::Key_Up:
|
|
|
|
nCode = KEY_UP;
|
|
|
|
break;
|
|
|
|
case Qt::Key_Left:
|
|
|
|
nCode = KEY_LEFT;
|
|
|
|
break;
|
|
|
|
case Qt::Key_Right:
|
|
|
|
nCode = KEY_RIGHT;
|
|
|
|
break;
|
|
|
|
case Qt::Key_Home:
|
|
|
|
nCode = KEY_HOME;
|
|
|
|
break;
|
|
|
|
case Qt::Key_End:
|
|
|
|
nCode = KEY_END;
|
|
|
|
break;
|
|
|
|
case Qt::Key_PageUp:
|
|
|
|
nCode = KEY_PAGEUP;
|
|
|
|
break;
|
|
|
|
case Qt::Key_PageDown:
|
|
|
|
nCode = KEY_PAGEDOWN;
|
|
|
|
break;
|
|
|
|
case Qt::Key_Return:
|
2018-11-08 15:21:14 +03:00
|
|
|
case Qt::Key_Enter:
|
2017-11-07 13:18:05 +01:00
|
|
|
nCode = KEY_RETURN;
|
|
|
|
break;
|
|
|
|
case Qt::Key_Escape:
|
|
|
|
nCode = KEY_ESCAPE;
|
|
|
|
break;
|
|
|
|
case Qt::Key_Tab:
|
2018-06-26 11:48:52 +02:00
|
|
|
// oddly enough, Qt doesn't send Shift-Tab event as 'Tab key pressed with Shift
|
|
|
|
// modifier' but as 'Backtab key pressed' (while its modifier bits are still
|
|
|
|
// set to Shift) -- so let's map both Key_Tab and Key_Backtab to VCL's KEY_TAB
|
|
|
|
case Qt::Key_Backtab:
|
2017-11-07 13:18:05 +01:00
|
|
|
nCode = KEY_TAB;
|
|
|
|
break;
|
|
|
|
case Qt::Key_Backspace:
|
|
|
|
nCode = KEY_BACKSPACE;
|
|
|
|
break;
|
|
|
|
case Qt::Key_Space:
|
|
|
|
nCode = KEY_SPACE;
|
|
|
|
break;
|
|
|
|
case Qt::Key_Insert:
|
|
|
|
nCode = KEY_INSERT;
|
|
|
|
break;
|
|
|
|
case Qt::Key_Delete:
|
|
|
|
nCode = KEY_DELETE;
|
|
|
|
break;
|
|
|
|
case Qt::Key_Plus:
|
|
|
|
nCode = KEY_ADD;
|
|
|
|
break;
|
|
|
|
case Qt::Key_Minus:
|
|
|
|
nCode = KEY_SUBTRACT;
|
|
|
|
break;
|
|
|
|
case Qt::Key_Asterisk:
|
|
|
|
nCode = KEY_MULTIPLY;
|
|
|
|
break;
|
|
|
|
case Qt::Key_Slash:
|
|
|
|
nCode = KEY_DIVIDE;
|
|
|
|
break;
|
|
|
|
case Qt::Key_Period:
|
|
|
|
nCode = KEY_POINT;
|
|
|
|
break;
|
|
|
|
case Qt::Key_Comma:
|
|
|
|
nCode = KEY_COMMA;
|
|
|
|
break;
|
|
|
|
case Qt::Key_Less:
|
|
|
|
nCode = KEY_LESS;
|
|
|
|
break;
|
|
|
|
case Qt::Key_Greater:
|
|
|
|
nCode = KEY_GREATER;
|
|
|
|
break;
|
|
|
|
case Qt::Key_Equal:
|
|
|
|
nCode = KEY_EQUAL;
|
|
|
|
break;
|
|
|
|
case Qt::Key_Find:
|
|
|
|
nCode = KEY_FIND;
|
|
|
|
break;
|
|
|
|
case Qt::Key_Menu:
|
|
|
|
nCode = KEY_CONTEXTMENU;
|
|
|
|
break;
|
|
|
|
case Qt::Key_Help:
|
|
|
|
nCode = KEY_HELP;
|
|
|
|
break;
|
|
|
|
case Qt::Key_Undo:
|
|
|
|
nCode = KEY_UNDO;
|
|
|
|
break;
|
|
|
|
case Qt::Key_Redo:
|
|
|
|
nCode = KEY_REPEAT;
|
|
|
|
break;
|
|
|
|
case Qt::Key_Cancel:
|
|
|
|
nCode = KEY_F11;
|
|
|
|
break;
|
|
|
|
case Qt::Key_AsciiTilde:
|
|
|
|
nCode = KEY_TILDE;
|
|
|
|
break;
|
|
|
|
case Qt::Key_QuoteLeft:
|
|
|
|
nCode = KEY_QUOTELEFT;
|
|
|
|
break;
|
|
|
|
case Qt::Key_BracketLeft:
|
|
|
|
nCode = KEY_BRACKETLEFT;
|
|
|
|
break;
|
|
|
|
case Qt::Key_BracketRight:
|
|
|
|
nCode = KEY_BRACKETRIGHT;
|
|
|
|
break;
|
|
|
|
case Qt::Key_Semicolon:
|
|
|
|
nCode = KEY_SEMICOLON;
|
|
|
|
break;
|
|
|
|
case Qt::Key_Copy:
|
|
|
|
nCode = KEY_COPY;
|
|
|
|
break;
|
|
|
|
case Qt::Key_Cut:
|
|
|
|
nCode = KEY_CUT;
|
|
|
|
break;
|
|
|
|
case Qt::Key_Open:
|
|
|
|
nCode = KEY_OPEN;
|
|
|
|
break;
|
|
|
|
case Qt::Key_Paste:
|
|
|
|
nCode = KEY_PASTE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nCode;
|
|
|
|
}
|
|
|
|
|
2019-05-30 13:39:11 +00:00
|
|
|
void Qt5Widget::commitText(Qt5Frame& rFrame, const QString& aText)
|
2019-06-01 19:46:16 +00:00
|
|
|
{
|
|
|
|
SalExtTextInputEvent aInputEvent;
|
|
|
|
aInputEvent.mpTextAttr = nullptr;
|
|
|
|
aInputEvent.mnCursorFlags = 0;
|
|
|
|
aInputEvent.maText = toOUString(aText);
|
|
|
|
aInputEvent.mnCursorPos = aInputEvent.maText.getLength();
|
|
|
|
|
|
|
|
SolarMutexGuard aGuard;
|
2019-05-30 13:39:11 +00:00
|
|
|
vcl::DeletionListener aDel(&rFrame);
|
|
|
|
rFrame.CallCallback(SalEvent::ExtTextInput, &aInputEvent);
|
2019-06-01 19:46:16 +00:00
|
|
|
if (!aDel.isDeleted())
|
2019-05-30 13:39:11 +00:00
|
|
|
rFrame.CallCallback(SalEvent::EndExtTextInput, nullptr);
|
2019-06-01 19:46:16 +00:00
|
|
|
}
|
|
|
|
|
2019-05-30 13:39:11 +00:00
|
|
|
bool Qt5Widget::handleKeyEvent(Qt5Frame& rFrame, const QWidget& rWidget, QKeyEvent* pEvent,
|
|
|
|
const ButtonKeyState eState)
|
2017-11-07 13:18:05 +01:00
|
|
|
{
|
2019-06-01 19:46:16 +00:00
|
|
|
sal_uInt16 nCode = GetKeyCode(pEvent->key(), pEvent->modifiers());
|
2019-05-30 13:39:11 +00:00
|
|
|
if (eState == ButtonKeyState::Pressed && nCode == 0 && !pEvent->text().isEmpty()
|
|
|
|
&& rWidget.testAttribute(Qt::WA_InputMethodEnabled))
|
2019-06-01 19:46:16 +00:00
|
|
|
{
|
2019-05-30 13:39:11 +00:00
|
|
|
commitText(rFrame, pEvent->text());
|
|
|
|
pEvent->accept();
|
2019-06-01 19:46:16 +00:00
|
|
|
return true;
|
|
|
|
}
|
2017-11-07 13:18:05 +01:00
|
|
|
|
2019-06-01 19:46:16 +00:00
|
|
|
SalKeyEvent aEvent;
|
2017-11-07 13:18:05 +01:00
|
|
|
aEvent.mnCharCode = (pEvent->text().isEmpty() ? 0 : pEvent->text().at(0).unicode());
|
|
|
|
aEvent.mnRepeat = 0;
|
2019-06-01 19:46:16 +00:00
|
|
|
aEvent.mnCode = nCode;
|
2017-11-07 13:18:05 +01:00
|
|
|
aEvent.mnCode |= GetKeyModCode(pEvent->modifiers());
|
|
|
|
|
2019-05-04 00:55:24 +00:00
|
|
|
QGuiApplication::inputMethod()->update(Qt::ImCursorRectangle);
|
|
|
|
|
2017-11-07 13:18:05 +01:00
|
|
|
bool bStopProcessingKey;
|
2019-05-30 13:39:11 +00:00
|
|
|
if (eState == ButtonKeyState::Pressed)
|
|
|
|
bStopProcessingKey = rFrame.CallCallback(SalEvent::KeyInput, &aEvent);
|
2017-11-07 13:18:05 +01:00
|
|
|
else
|
2019-05-30 13:39:11 +00:00
|
|
|
bStopProcessingKey = rFrame.CallCallback(SalEvent::KeyUp, &aEvent);
|
|
|
|
if (bStopProcessingKey)
|
|
|
|
pEvent->accept();
|
2017-11-07 13:18:05 +01:00
|
|
|
return bStopProcessingKey;
|
|
|
|
}
|
|
|
|
|
2019-05-30 13:39:11 +00:00
|
|
|
bool Qt5Widget::handleEvent(Qt5Frame& rFrame, const QWidget& rWidget, QEvent* pEvent)
|
2019-01-16 14:57:42 +03:00
|
|
|
{
|
|
|
|
if (pEvent->type() == QEvent::ShortcutOverride)
|
|
|
|
{
|
|
|
|
// Accepted event disables shortcut activation,
|
|
|
|
// but enables keypress event.
|
|
|
|
// If event is not accepted and shortcut is successfully activated,
|
|
|
|
// KeyPress event is omitted.
|
2019-01-21 12:16:13 +03:00
|
|
|
//
|
|
|
|
// Instead of processing keyPressEvent, handle ShortcutOverride event,
|
|
|
|
// and if it's handled - disable the shortcut, it should have been activated.
|
|
|
|
// Don't process keyPressEvent generated after disabling shortcut since it was handled here.
|
|
|
|
// If event is not handled, don't accept it and let Qt activate related shortcut.
|
2019-05-30 13:39:11 +00:00
|
|
|
if (handleKeyEvent(rFrame, rWidget, static_cast<QKeyEvent*>(pEvent),
|
|
|
|
ButtonKeyState::Pressed))
|
|
|
|
return true;
|
2019-01-16 14:57:42 +03:00
|
|
|
}
|
2019-05-30 13:39:11 +00:00
|
|
|
return false;
|
|
|
|
}
|
2019-01-16 14:57:42 +03:00
|
|
|
|
2019-05-30 13:39:11 +00:00
|
|
|
bool Qt5Widget::event(QEvent* pEvent)
|
|
|
|
{
|
|
|
|
return handleEvent(m_rFrame, *this, pEvent) || QWidget::event(pEvent);
|
2019-01-16 14:57:42 +03:00
|
|
|
}
|
|
|
|
|
2018-08-10 18:29:10 +02:00
|
|
|
void Qt5Widget::keyReleaseEvent(QKeyEvent* pEvent)
|
2017-11-07 13:18:05 +01:00
|
|
|
{
|
2019-05-30 13:39:11 +00:00
|
|
|
if (!handleKeyReleaseEvent(m_rFrame, *this, pEvent))
|
|
|
|
QWidget::keyReleaseEvent(pEvent);
|
2017-11-07 13:18:05 +01:00
|
|
|
}
|
|
|
|
|
2019-05-05 23:22:09 +00:00
|
|
|
void Qt5Widget::focusInEvent(QFocusEvent*) { m_rFrame.CallCallback(SalEvent::GetFocus, nullptr); }
|
2017-11-07 13:18:05 +01:00
|
|
|
|
Qt5 IM handle (spurious?) all-empty IM events
2nd attempt to fix the bug described in commit 00221089800c ("Qt5
IM allow committing empty strings") and various siblings of it.
This also reverts it.
What I see is calls with "all-empty" events (preedit, commit and
replacementLength() == 0; no QInputMethodEvent::Attribute), some
from QIBusPlatformInputContext::updatePreeditText.
There are various Writer document edit states with (selected)
text, undo, cursor position and focus changes to other windows via
Ctrl+Tab, which will result in inputMethodEvent calls totally in
contrast to the expected text state, all somehow always related to
all-empty events. They currently result in wrongly deleted
selected text, change of selection, cursor movement or general
change of text from old preedit. Most time on focus out / window
change, some times at first meta-key press after focus in.
This patch tries hard not to corrupt Writers edit state with these
all-empty events. No idea if this is some bug on LO's qt5 side or
expected, but KDE kate and VCL gtk3 and gen work fine, so I assume
Qt's behaviour is correct. FWIW gtk3 also does some extended IM
handling with focus, so probably this is the Qt equivalent of it.
But then I couldn't find some eqivalent code in Qt's source code.
I actually expected an even more complex solution (if this really
fixes all cases).
Works for a multitude of tests I tried to come up with, but is
quite probably not the final fix to this, as qt5 current doesn't
handle replacementStart() and replacementLength() at all.
Also never saw a call to Qt5Frame::EndExtTextInput.
Change-Id: I4210e0588041cfb4d80dbdfdb937e430a5f7cbfb
Reviewed-on: https://gerrit.libreoffice.org/71988
Tested-by: Jenkins
Reviewed-by: Jan-Marek Glogowski <glogow@fbihome.de>
2019-05-08 11:51:15 +00:00
|
|
|
void Qt5Widget::focusOutEvent(QFocusEvent*)
|
|
|
|
{
|
|
|
|
endExtTextInput();
|
|
|
|
m_rFrame.CallCallback(SalEvent::LoseFocus, nullptr);
|
|
|
|
}
|
2017-11-07 13:18:05 +01:00
|
|
|
|
2018-08-10 18:29:10 +02:00
|
|
|
Qt5Widget::Qt5Widget(Qt5Frame& rFrame, Qt::WindowFlags f)
|
|
|
|
: QWidget(Q_NULLPTR, f)
|
2019-05-05 23:22:09 +00:00
|
|
|
, m_rFrame(rFrame)
|
Qt5 IM handle (spurious?) all-empty IM events
2nd attempt to fix the bug described in commit 00221089800c ("Qt5
IM allow committing empty strings") and various siblings of it.
This also reverts it.
What I see is calls with "all-empty" events (preedit, commit and
replacementLength() == 0; no QInputMethodEvent::Attribute), some
from QIBusPlatformInputContext::updatePreeditText.
There are various Writer document edit states with (selected)
text, undo, cursor position and focus changes to other windows via
Ctrl+Tab, which will result in inputMethodEvent calls totally in
contrast to the expected text state, all somehow always related to
all-empty events. They currently result in wrongly deleted
selected text, change of selection, cursor movement or general
change of text from old preedit. Most time on focus out / window
change, some times at first meta-key press after focus in.
This patch tries hard not to corrupt Writers edit state with these
all-empty events. No idea if this is some bug on LO's qt5 side or
expected, but KDE kate and VCL gtk3 and gen work fine, so I assume
Qt's behaviour is correct. FWIW gtk3 also does some extended IM
handling with focus, so probably this is the Qt equivalent of it.
But then I couldn't find some eqivalent code in Qt's source code.
I actually expected an even more complex solution (if this really
fixes all cases).
Works for a multitude of tests I tried to come up with, but is
quite probably not the final fix to this, as qt5 current doesn't
handle replacementStart() and replacementLength() at all.
Also never saw a call to Qt5Frame::EndExtTextInput.
Change-Id: I4210e0588041cfb4d80dbdfdb937e430a5f7cbfb
Reviewed-on: https://gerrit.libreoffice.org/71988
Tested-by: Jenkins
Reviewed-by: Jan-Marek Glogowski <glogow@fbihome.de>
2019-05-08 11:51:15 +00:00
|
|
|
, m_bNonEmptyIMPreeditSeen(false)
|
2019-06-06 01:01:11 +00:00
|
|
|
, m_nDeltaX(0)
|
|
|
|
, m_nDeltaY(0)
|
2018-05-14 15:39:30 +02:00
|
|
|
{
|
2018-08-10 18:29:10 +02:00
|
|
|
create();
|
|
|
|
setMouseTracking(true);
|
|
|
|
setFocusPolicy(Qt::StrongFocus);
|
2018-07-17 15:57:41 +02:00
|
|
|
}
|
|
|
|
|
2019-05-03 21:18:34 +02:00
|
|
|
static ExtTextInputAttr lcl_MapUndrelineStyle(QTextCharFormat::UnderlineStyle us)
|
|
|
|
{
|
|
|
|
switch (us)
|
|
|
|
{
|
|
|
|
case QTextCharFormat::NoUnderline:
|
|
|
|
return ExtTextInputAttr::NONE;
|
|
|
|
case QTextCharFormat::DotLine:
|
|
|
|
return ExtTextInputAttr::DottedUnderline;
|
|
|
|
case QTextCharFormat::DashDotDotLine:
|
|
|
|
case QTextCharFormat::DashDotLine:
|
|
|
|
return ExtTextInputAttr::DashDotUnderline;
|
|
|
|
case QTextCharFormat::WaveUnderline:
|
|
|
|
return ExtTextInputAttr::GrayWaveline;
|
|
|
|
default:
|
|
|
|
return ExtTextInputAttr::Underline;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-21 17:25:17 +01:00
|
|
|
void Qt5Widget::inputMethodEvent(QInputMethodEvent* pEvent)
|
|
|
|
{
|
Qt5 IM handle (spurious?) all-empty IM events
2nd attempt to fix the bug described in commit 00221089800c ("Qt5
IM allow committing empty strings") and various siblings of it.
This also reverts it.
What I see is calls with "all-empty" events (preedit, commit and
replacementLength() == 0; no QInputMethodEvent::Attribute), some
from QIBusPlatformInputContext::updatePreeditText.
There are various Writer document edit states with (selected)
text, undo, cursor position and focus changes to other windows via
Ctrl+Tab, which will result in inputMethodEvent calls totally in
contrast to the expected text state, all somehow always related to
all-empty events. They currently result in wrongly deleted
selected text, change of selection, cursor movement or general
change of text from old preedit. Most time on focus out / window
change, some times at first meta-key press after focus in.
This patch tries hard not to corrupt Writers edit state with these
all-empty events. No idea if this is some bug on LO's qt5 side or
expected, but KDE kate and VCL gtk3 and gen work fine, so I assume
Qt's behaviour is correct. FWIW gtk3 also does some extended IM
handling with focus, so probably this is the Qt equivalent of it.
But then I couldn't find some eqivalent code in Qt's source code.
I actually expected an even more complex solution (if this really
fixes all cases).
Works for a multitude of tests I tried to come up with, but is
quite probably not the final fix to this, as qt5 current doesn't
handle replacementStart() and replacementLength() at all.
Also never saw a call to Qt5Frame::EndExtTextInput.
Change-Id: I4210e0588041cfb4d80dbdfdb937e430a5f7cbfb
Reviewed-on: https://gerrit.libreoffice.org/71988
Tested-by: Jenkins
Reviewed-by: Jan-Marek Glogowski <glogow@fbihome.de>
2019-05-08 11:51:15 +00:00
|
|
|
if (!pEvent->commitString().isEmpty())
|
2019-05-30 13:39:11 +00:00
|
|
|
commitText(m_rFrame, pEvent->commitString());
|
2018-12-21 17:25:17 +01:00
|
|
|
else
|
|
|
|
{
|
2019-06-01 19:46:16 +00:00
|
|
|
SalExtTextInputEvent aInputEvent;
|
|
|
|
aInputEvent.mpTextAttr = nullptr;
|
|
|
|
aInputEvent.mnCursorFlags = 0;
|
2018-12-21 17:25:17 +01:00
|
|
|
aInputEvent.maText = toOUString(pEvent->preeditString());
|
|
|
|
aInputEvent.mnCursorPos = 0;
|
2019-05-03 21:16:49 +02:00
|
|
|
|
|
|
|
const sal_Int32 nLength = aInputEvent.maText.getLength();
|
|
|
|
const QList<QInputMethodEvent::Attribute>& rAttrList = pEvent->attributes();
|
qt5: Fix build error on 32 bit archs
... as observed in [1]:
> [...]
> In file included from /usr/include/c++/8/algorithm:61,
> from /usr/include/i386-linux-gnu/qt5/QtCore/qglobal.h:142,
> from /usr/include/i386-linux-gnu/qt5/QtGui/qtguiglobal.h:43,
> from /usr/include/i386-linux-gnu/qt5/QtWidgets/qtwidgetsglobal.h:43,
> from /usr/include/i386-linux-gnu/qt5/QtWidgets/qwidget.h:43,
> from /usr/include/i386-linux-gnu/qt5/QtWidgets/QWidget:1,
> from /<<PKGBUILDDIR>>/vcl/inc/qt5/Qt5Widget.hxx:22,
> from /<<PKGBUILDDIR>>/vcl/qt5/Qt5Widget.cxx:20:
> /usr/include/c++/8/bits/stl_algobase.h:265:5: note: candidate: ‘template<class _Tp, class _Compare> constexpr const _Tp& std::max(const _Tp&, const _Tp&, _Compare)’
> max(const _Tp& __a, const _Tp& __b, _Compare __comp)
> ^~~
> /usr/include/c++/8/bits/stl_algobase.h:265:5: note: template argument deduction/substitution failed:
> /<<PKGBUILDDIR>>/vcl/qt5/Qt5Widget.cxx:502:69: note: deduced conflicting types for parameter ‘const _Tp’ (‘int’ and ‘sal_Int32’ {aka ‘long int’})
> std::vector<ExtTextInputAttr> aTextAttrs(std::max(1, nLength), ExtTextInputAttr::NONE);
[1] https://buildd.debian.org/status/fetch.php?pkg=libreoffice&arch=i386&ver=1%3A6.3.0%7Ealpha1-1&stamp=1557890923&raw=0
Change-Id: Ic0e3e0b8bc65d9755b3d62385673f8d1e357b122
Reviewed-on: https://gerrit.libreoffice.org/72352
Tested-by: Jenkins
Reviewed-by: Rene Engelhard <rene@debian.org>
Tested-by: Rene Engelhard <rene@debian.org>
2019-05-15 13:28:15 +02:00
|
|
|
std::vector<ExtTextInputAttr> aTextAttrs(std::max(sal_Int32(1), nLength),
|
|
|
|
ExtTextInputAttr::NONE);
|
2019-05-22 13:49:30 +02:00
|
|
|
aInputEvent.mpTextAttr = aTextAttrs.data();
|
2019-05-03 21:16:49 +02:00
|
|
|
|
|
|
|
for (int i = 0; i < rAttrList.size(); ++i)
|
|
|
|
{
|
|
|
|
const QInputMethodEvent::Attribute& rAttr = rAttrList.at(i);
|
|
|
|
switch (rAttr.type)
|
|
|
|
{
|
2019-05-03 21:18:34 +02:00
|
|
|
case QInputMethodEvent::TextFormat:
|
|
|
|
{
|
|
|
|
QTextCharFormat aCharFormat
|
|
|
|
= qvariant_cast<QTextFormat>(rAttr.value).toCharFormat();
|
|
|
|
if (aCharFormat.isValid())
|
|
|
|
{
|
|
|
|
ExtTextInputAttr aETIP
|
|
|
|
= lcl_MapUndrelineStyle(aCharFormat.underlineStyle());
|
|
|
|
if (aCharFormat.hasProperty(QTextFormat::BackgroundBrush))
|
|
|
|
aETIP |= ExtTextInputAttr::Highlight;
|
|
|
|
if (aCharFormat.fontStrikeOut())
|
|
|
|
aETIP |= ExtTextInputAttr::RedText;
|
|
|
|
for (int j = rAttr.start; j < rAttr.start + rAttr.length; j++)
|
|
|
|
aTextAttrs[j] = aETIP;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2019-05-03 21:16:49 +02:00
|
|
|
case QInputMethodEvent::Cursor:
|
|
|
|
{
|
|
|
|
aInputEvent.mnCursorPos = rAttr.start;
|
|
|
|
if (rAttr.length == 0)
|
|
|
|
aInputEvent.mnCursorFlags |= EXTTEXTINPUT_CURSOR_INVISIBLE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
SAL_WARN("vcl.qt5", "Unhandled QInputMethodEvent attribute: "
|
|
|
|
<< static_cast<int>(rAttr.type));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Qt5 IM handle (spurious?) all-empty IM events
2nd attempt to fix the bug described in commit 00221089800c ("Qt5
IM allow committing empty strings") and various siblings of it.
This also reverts it.
What I see is calls with "all-empty" events (preedit, commit and
replacementLength() == 0; no QInputMethodEvent::Attribute), some
from QIBusPlatformInputContext::updatePreeditText.
There are various Writer document edit states with (selected)
text, undo, cursor position and focus changes to other windows via
Ctrl+Tab, which will result in inputMethodEvent calls totally in
contrast to the expected text state, all somehow always related to
all-empty events. They currently result in wrongly deleted
selected text, change of selection, cursor movement or general
change of text from old preedit. Most time on focus out / window
change, some times at first meta-key press after focus in.
This patch tries hard not to corrupt Writers edit state with these
all-empty events. No idea if this is some bug on LO's qt5 side or
expected, but KDE kate and VCL gtk3 and gen work fine, so I assume
Qt's behaviour is correct. FWIW gtk3 also does some extended IM
handling with focus, so probably this is the Qt equivalent of it.
But then I couldn't find some eqivalent code in Qt's source code.
I actually expected an even more complex solution (if this really
fixes all cases).
Works for a multitude of tests I tried to come up with, but is
quite probably not the final fix to this, as qt5 current doesn't
handle replacementStart() and replacementLength() at all.
Also never saw a call to Qt5Frame::EndExtTextInput.
Change-Id: I4210e0588041cfb4d80dbdfdb937e430a5f7cbfb
Reviewed-on: https://gerrit.libreoffice.org/71988
Tested-by: Jenkins
Reviewed-by: Jan-Marek Glogowski <glogow@fbihome.de>
2019-05-08 11:51:15 +00:00
|
|
|
const bool bIsEmpty = aInputEvent.maText.isEmpty();
|
|
|
|
if (m_bNonEmptyIMPreeditSeen || !bIsEmpty)
|
|
|
|
{
|
2019-06-01 19:46:16 +00:00
|
|
|
SolarMutexGuard aGuard;
|
|
|
|
vcl::DeletionListener aDel(&m_rFrame);
|
|
|
|
m_rFrame.CallCallback(SalEvent::ExtTextInput, &aInputEvent);
|
Qt5 IM handle (spurious?) all-empty IM events
2nd attempt to fix the bug described in commit 00221089800c ("Qt5
IM allow committing empty strings") and various siblings of it.
This also reverts it.
What I see is calls with "all-empty" events (preedit, commit and
replacementLength() == 0; no QInputMethodEvent::Attribute), some
from QIBusPlatformInputContext::updatePreeditText.
There are various Writer document edit states with (selected)
text, undo, cursor position and focus changes to other windows via
Ctrl+Tab, which will result in inputMethodEvent calls totally in
contrast to the expected text state, all somehow always related to
all-empty events. They currently result in wrongly deleted
selected text, change of selection, cursor movement or general
change of text from old preedit. Most time on focus out / window
change, some times at first meta-key press after focus in.
This patch tries hard not to corrupt Writers edit state with these
all-empty events. No idea if this is some bug on LO's qt5 side or
expected, but KDE kate and VCL gtk3 and gen work fine, so I assume
Qt's behaviour is correct. FWIW gtk3 also does some extended IM
handling with focus, so probably this is the Qt equivalent of it.
But then I couldn't find some eqivalent code in Qt's source code.
I actually expected an even more complex solution (if this really
fixes all cases).
Works for a multitude of tests I tried to come up with, but is
quite probably not the final fix to this, as qt5 current doesn't
handle replacementStart() and replacementLength() at all.
Also never saw a call to Qt5Frame::EndExtTextInput.
Change-Id: I4210e0588041cfb4d80dbdfdb937e430a5f7cbfb
Reviewed-on: https://gerrit.libreoffice.org/71988
Tested-by: Jenkins
Reviewed-by: Jan-Marek Glogowski <glogow@fbihome.de>
2019-05-08 11:51:15 +00:00
|
|
|
if (!aDel.isDeleted() && bIsEmpty)
|
|
|
|
m_rFrame.CallCallback(SalEvent::EndExtTextInput, nullptr);
|
|
|
|
m_bNonEmptyIMPreeditSeen = !bIsEmpty;
|
|
|
|
}
|
2018-12-21 17:25:17 +01:00
|
|
|
}
|
Qt5 IM handle (spurious?) all-empty IM events
2nd attempt to fix the bug described in commit 00221089800c ("Qt5
IM allow committing empty strings") and various siblings of it.
This also reverts it.
What I see is calls with "all-empty" events (preedit, commit and
replacementLength() == 0; no QInputMethodEvent::Attribute), some
from QIBusPlatformInputContext::updatePreeditText.
There are various Writer document edit states with (selected)
text, undo, cursor position and focus changes to other windows via
Ctrl+Tab, which will result in inputMethodEvent calls totally in
contrast to the expected text state, all somehow always related to
all-empty events. They currently result in wrongly deleted
selected text, change of selection, cursor movement or general
change of text from old preedit. Most time on focus out / window
change, some times at first meta-key press after focus in.
This patch tries hard not to corrupt Writers edit state with these
all-empty events. No idea if this is some bug on LO's qt5 side or
expected, but KDE kate and VCL gtk3 and gen work fine, so I assume
Qt's behaviour is correct. FWIW gtk3 also does some extended IM
handling with focus, so probably this is the Qt equivalent of it.
But then I couldn't find some eqivalent code in Qt's source code.
I actually expected an even more complex solution (if this really
fixes all cases).
Works for a multitude of tests I tried to come up with, but is
quite probably not the final fix to this, as qt5 current doesn't
handle replacementStart() and replacementLength() at all.
Also never saw a call to Qt5Frame::EndExtTextInput.
Change-Id: I4210e0588041cfb4d80dbdfdb937e430a5f7cbfb
Reviewed-on: https://gerrit.libreoffice.org/71988
Tested-by: Jenkins
Reviewed-by: Jan-Marek Glogowski <glogow@fbihome.de>
2019-05-08 11:51:15 +00:00
|
|
|
|
|
|
|
pEvent->accept();
|
2018-12-21 17:25:17 +01:00
|
|
|
}
|
|
|
|
|
2019-05-04 14:49:43 +00:00
|
|
|
static bool lcl_retrieveSurrounding(sal_Int32& rPosition, sal_Int32& rAnchor, QString* pText,
|
|
|
|
QString* pSelection)
|
2019-05-04 01:04:35 +00:00
|
|
|
{
|
Qt5Widget: lcl_retrieveSurrounding needs SolarMutex
Otherwise
* starting Writer
* "File" -> "New" -> "Spreadsheet"
* switching back to Writer window using Alt+Tab
in Plasma Wayland session results in
Thread 1 "soffice.bin" received signal SIGABRT, Aborted.
__GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50
50 ../sysdeps/unix/sysv/linux/raise.c: No such file or directory.
(gdb) bt
#0 0x00007ffff7b287bb in __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50
#1 0x00007ffff7b13535 in __GI_abort () at abort.c:79
#2 0x00007ffff7b1340f in __assert_fail_base
(fmt=0x7ffff7c75ee0 "%s%s%s:%u: %s%sAssertion `%s' failed.
%n", assertion=0x7ffff14b0410 "ImplGetSVData()->mpDefInst->GetYieldMutex()->IsCurrentThread() && \"SolarMutex not owned!\"", file=0x7ffff14b03d0 "/mnt/data/development/git/libreoffice/vcl/source/app/dbggui.cxx", line=46, function=<optimized out>) at assert.c:92
#3 0x00007ffff7b21102 in __GI___assert_fail
(assertion=0x7ffff14b0410 "ImplGetSVData()->mpDefInst->GetYieldMutex()->IsCurrentThread() && \"SolarMutex not owned!\"", file=0x7ffff14b03d0 "/mnt/data/development/git/libreoffice/vcl/source/app/dbggui.cxx", line=46, function=0x7ffff14b0470 <ImplDbgTestSolarMutex()::__PRETTY_FUNCTION__> "void ImplDbgTestSolarMutex()") at assert.c:101
#4 0x00007ffff0e495db in ImplDbgTestSolarMutex() () at /mnt/data/development/git/libreoffice/vcl/source/app/dbggui.cxx:46
#5 0x00007ffff2026c2f in DbgTestSolarMutex() () at /mnt/data/development/git/libreoffice/tools/source/debug/debug.cxx:91
#6 0x00007ffff4eea765 in SfxBroadcaster::AddListener(SfxListener&) (this=0x555558e61620, rListener=...) at /mnt/data/development/git/libreoffice/svl/source/notify/SfxBroadcaster.cxx:94
#7 0x00007ffff4effe4e in SfxListener::StartListening(SfxBroadcaster&, DuplicateHandling) (this=0x55555a1ebe50, rBroadcaster=..., eDuplicateHanding=DuplicateHandling::Unexpected) at /mnt/data/development/git/libreoffice/svl/source/notify/lstner.cxx:104
#8 0x00007fffbfdff188 in ScTabViewShell::AddAccessibilityObject(SfxListener&) (this=0x555558c27080, rObject=...) at /mnt/data/development/git/libreoffice/sc/source/ui/view/tabvwshh.cxx:215
#9 0x00007fffbf65754a in ScAccessibleDocument::PreInit() (this=0x55555a1ebdb0) at /mnt/data/development/git/libreoffice/sc/source/ui/Accessibility/AccessibleDocument.cxx:1303
#10 0x00007fffbfd12754 in ScGridWindow::CreateAccessible() (this=0x555558c2dd50) at /mnt/data/development/git/libreoffice/sc/source/ui/view/gridwin5.cxx:443
#11 0x00007ffff06ef948 in vcl::Window::GetAccessible(bool) (this=0x555558c2dd50, bCreate=true) at /mnt/data/development/git/libreoffice/vcl/source/window/accessibility.cxx:129
#12 0x00007fffe730f9c6 in lcl_retrieveSurrounding(sal_Int32&, sal_Int32&, QString*, QString*) (rPosition=@0x7fffffffcb90: -12560, rAnchor=@0x7fffffffcba0: -13392, pText=0x0, pSelection=0x0) at /mnt/data/development/git/libreoffice/vcl/qt5/Qt5Widget.cxx:598
#13 0x00007fffe730ff17 in Qt5Widget::inputMethodQuery(Qt::InputMethodQuery) const (this=0x555556c50ec0, property=Qt::ImCursorPosition) at /mnt/data/development/git/libreoffice/vcl/qt5/Qt5Widget.cxx:656
#14 0x00007fffe786c7f7 in QWidget::event(QEvent*) (this=0x555556c50ec0, event=0x7fffffffcef0) at kernel/qwidget.cpp:8993
#15 0x00007fffe730f0d3 in Qt5Widget::event(QEvent*) (this=0x555556c50ec0, pEvent=0x7fffffffcef0) at /mnt/data/development/git/libreoffice/vcl/qt5/Qt5Widget.cxx:466
#16 0x00007fffe782e4b1 in QApplicationPrivate::notify_helper(QObject*, QEvent*) (this=this@entry=0x5555556480f0, receiver=receiver@entry=0x555556c50ec0, e=e@entry=0x7fffffffcef0) at kernel/qapplication.cpp:3726
#17 0x00007fffe7835950 in QApplication::notify(QObject*, QEvent*) (this=0x555555647fe0, receiver=0x555556c50ec0, e=0x7fffffffcef0) at kernel/qapplication.cpp:3485
#18 0x00007fffe851b5a9 in QCoreApplication::notifyInternal2(QObject*, QEvent*) (receiver=0x555556c50ec0, event=event@entry=0x7fffffffcef0) at ../../include/QtCore/5.11.3/QtCore/private/../../../../../src/corelib/thread/qthread_p.h:307
#19 0x00007fffe5bdbf6a in QCoreApplication::sendEvent(QObject*, QEvent*) (event=0x7fffffffcef0, receiver=<optimized out>) at /usr/include/x86_64-linux-gnu/qt5/QtCore/qcoreapplication.h:234
#20 0x00007fffe5bdbf6a in QtWaylandClient::QWaylandTextInput::updateState(QFlags<Qt::InputMethodQuery>, unsigned int) (this=0x555555655ab0, queries=..., flags=2) at qwaylandinputcontext.cpp:134
#21 0x00007fffe5bdc433 in QtWaylandClient::QWaylandTextInput::commit() (this=0x555555655ab0) at qwaylandinputcontext.cpp:98
#22 0x00007fffe5bdc4b9 in QtWaylandClient::QWaylandInputContext::commit() (this=0x555555652300) at qwaylandinputcontext.cpp:544
#23 0x00007fffe5bdc4b9 in QtWaylandClient::QWaylandInputContext::commit() (this=0x555555652300) at qwaylandinputcontext.cpp:410
#24 0x00007fffe7833aa5 in QApplication::setActiveWindow(QWidget*) (act=act@entry=0x555556f29a60) at kernel/qapplication.cpp:2088
#25 0x00007fffe7833b53 in QApplicationPrivate::notifyActiveWindowChange(QWindow*) (this=<optimized out>, previous=<optimized out>) at kernel/qapplication.cpp:2182
#26 0x00007fffe7e42ce5 in QGuiApplicationPrivate::processActivatedEvent(QWindowSystemInterfacePrivate::ActivatedWindowEvent*) (e=<optimized out>) at kernel/qguiapplication.cpp:2292
#27 0x00007fffe7e42f1d in QGuiApplicationPrivate::processWindowSystemEvent(QWindowSystemInterfacePrivate::WindowSystemEvent*) (e=e@entry=0x555559c186d0) at kernel/qguiapplication.cpp:1838
#28 0x00007fffe7e1d06b in QWindowSystemInterface::sendWindowSystemEvents(QFlags<QEventLoop::ProcessEventsFlag>) (flags=...) at kernel/qwindowsysteminterface.cpp:1032
#29 0x00007fffe5bfc6cb in QPAEventDispatcherGlib::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) () at /usr/lib/x86_64-linux-gnu/libQt5WaylandClient.so.5
#30 0x00007fffe72e9d7d in Qt5Instance::ImplYield(bool, bool) (this=0x5555556c46a0, bWait=true, bHandleAllCurrentEvents=false) at /mnt/data/development/git/libreoffice/vcl/qt5/Qt5Instance.cxx:345
#31 0x00007fffe72e9e8d in Qt5Instance::DoYield(bool, bool) (this=0x5555556c46a0, bWait=true, bHandleAllCurrentEvents=false) at /mnt/data/development/git/libreoffice/vcl/qt5/Qt5Instance.cxx:356
#32 0x00007ffff0ec1a66 in ImplYield(bool, bool) (i_bWait=true, i_bAllEvents=false) at /mnt/data/development/git/libreoffice/vcl/source/app/svapp.cxx:457
#33 0x00007ffff0ec1f89 in Application::Yield() () at /mnt/data/development/git/libreoffice/vcl/source/app/svapp.cxx:521
#34 0x00007ffff0ec185e in Application::Execute() () at /mnt/data/development/git/libreoffice/vcl/source/app/svapp.cxx:438
#35 0x00007ffff7d3dfea in desktop::Desktop::Main() (this=0x7fffffffda80) at /mnt/data/development/git/libreoffice/desktop/source/app/app.cxx:1621
#36 0x00007ffff0edc6c9 in ImplSVMain() () at /mnt/data/development/git/libreoffice/vcl/source/app/svmain.cxx:203
#37 0x00007ffff0edc7ee in SVMain() () at /mnt/data/development/git/libreoffice/vcl/source/app/svmain.cxx:237
#38 0x00007ffff7d977a9 in soffice_main() () at /mnt/data/development/git/libreoffice/desktop/source/app/sofficemain.cxx:170
#39 0x000055555555495d in sal_main () at /mnt/data/development/git/libreoffice/desktop/source/app/main.c:48
#40 0x0000555555554943 in main (argc=2, argv=0x7fffffffddd8) at /mnt/data/development/git/libreoffice/desktop/source/app/main.c:47
Change-Id: I40bf501d07dde0c4610496aa1b2bb2a4051fdf2d
Reviewed-on: https://gerrit.libreoffice.org/73696
Reviewed-by: Jan-Marek Glogowski <glogow@fbihome.de>
Tested-by: Jenkins
Reviewed-by: Michael Weghorn <m.weghorn@posteo.de>
2019-06-08 12:34:24 +02:00
|
|
|
SolarMutexGuard aGuard;
|
2019-05-04 01:04:35 +00:00
|
|
|
vcl::Window* pFocusWin = Application::GetFocusWindow();
|
|
|
|
if (!pFocusWin)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
uno::Reference<accessibility::XAccessibleEditableText> xText;
|
|
|
|
try
|
|
|
|
{
|
|
|
|
uno::Reference<accessibility::XAccessible> xAccessible(pFocusWin->GetAccessible());
|
|
|
|
if (xAccessible.is())
|
|
|
|
xText = FindFocusedEditableText(xAccessible->getAccessibleContext());
|
|
|
|
}
|
2019-06-18 09:33:28 +02:00
|
|
|
catch (const uno::Exception&)
|
2019-05-04 01:04:35 +00:00
|
|
|
{
|
2019-06-18 09:33:28 +02:00
|
|
|
TOOLS_WARN_EXCEPTION("vcl.qt5", "Exception in getting input method surrounding text");
|
2019-05-04 01:04:35 +00:00
|
|
|
}
|
2019-05-04 14:49:43 +00:00
|
|
|
|
|
|
|
bool result = false;
|
2019-05-04 01:04:35 +00:00
|
|
|
if (xText.is())
|
|
|
|
{
|
|
|
|
rPosition = xText->getCaretPosition();
|
2019-05-04 14:49:43 +00:00
|
|
|
if (rPosition != -1)
|
2019-05-04 01:04:35 +00:00
|
|
|
{
|
2019-05-04 14:49:43 +00:00
|
|
|
result = true;
|
|
|
|
if (pText)
|
|
|
|
*pText = toQString(xText->getText());
|
|
|
|
|
|
|
|
sal_Int32 nSelStart = xText->getSelectionStart();
|
|
|
|
sal_Int32 nSelEnd = xText->getSelectionEnd();
|
|
|
|
if (nSelStart == nSelEnd)
|
|
|
|
{
|
|
|
|
rAnchor = rPosition;
|
|
|
|
if (pSelection)
|
|
|
|
result = false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (rPosition == nSelStart)
|
|
|
|
rAnchor = nSelEnd;
|
|
|
|
else
|
|
|
|
rAnchor = nSelStart;
|
|
|
|
if (pSelection)
|
|
|
|
*pSelection = toQString(xText->getSelectedText());
|
|
|
|
}
|
2019-05-04 01:04:35 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-04 14:49:43 +00:00
|
|
|
return result;
|
2019-05-04 01:04:35 +00:00
|
|
|
}
|
|
|
|
|
2018-12-21 17:25:17 +01:00
|
|
|
QVariant Qt5Widget::inputMethodQuery(Qt::InputMethodQuery property) const
|
|
|
|
{
|
|
|
|
switch (property)
|
|
|
|
{
|
2019-05-04 01:04:35 +00:00
|
|
|
case Qt::ImSurroundingText:
|
|
|
|
{
|
|
|
|
QString aText;
|
2019-05-04 14:49:43 +00:00
|
|
|
sal_Int32 nCursorPos, nAnchor;
|
|
|
|
if (lcl_retrieveSurrounding(nCursorPos, nAnchor, &aText, nullptr))
|
2019-05-04 01:04:35 +00:00
|
|
|
return QVariant(aText);
|
|
|
|
[[fallthrough]];
|
|
|
|
}
|
|
|
|
case Qt::ImCursorPosition:
|
|
|
|
{
|
2019-05-04 14:49:43 +00:00
|
|
|
sal_Int32 nCursorPos, nAnchor;
|
|
|
|
if (lcl_retrieveSurrounding(nCursorPos, nAnchor, nullptr, nullptr))
|
2019-06-02 20:04:28 +02:00
|
|
|
return QVariant(static_cast<int>(nCursorPos));
|
2019-05-04 01:04:35 +00:00
|
|
|
[[fallthrough]];
|
|
|
|
}
|
2018-12-21 17:25:17 +01:00
|
|
|
case Qt::ImCursorRectangle:
|
|
|
|
{
|
|
|
|
SalExtTextInputPosEvent aPosEvent;
|
2019-05-05 23:22:09 +00:00
|
|
|
m_rFrame.CallCallback(SalEvent::ExtTextInputPos, &aPosEvent);
|
2018-12-21 17:25:17 +01:00
|
|
|
return QVariant(
|
|
|
|
QRect(aPosEvent.mnX, aPosEvent.mnY, aPosEvent.mnWidth, aPosEvent.mnHeight));
|
|
|
|
}
|
2019-05-04 14:49:43 +00:00
|
|
|
case Qt::ImAnchorPosition:
|
|
|
|
{
|
|
|
|
sal_Int32 nCursorPos, nAnchor;
|
|
|
|
if (lcl_retrieveSurrounding(nCursorPos, nAnchor, nullptr, nullptr))
|
2019-06-02 20:04:28 +02:00
|
|
|
return QVariant(static_cast<int>(nAnchor));
|
2019-05-04 14:49:43 +00:00
|
|
|
[[fallthrough]];
|
|
|
|
}
|
|
|
|
case Qt::ImCurrentSelection:
|
|
|
|
{
|
|
|
|
QString aSelection;
|
|
|
|
sal_Int32 nCursorPos, nAnchor;
|
|
|
|
if (lcl_retrieveSurrounding(nCursorPos, nAnchor, nullptr, &aSelection))
|
|
|
|
return QVariant(aSelection);
|
|
|
|
[[fallthrough]];
|
|
|
|
}
|
2018-12-21 17:25:17 +01:00
|
|
|
default:
|
|
|
|
return QWidget::inputMethodQuery(property);
|
|
|
|
}
|
2019-05-04 01:04:35 +00:00
|
|
|
|
|
|
|
return QVariant();
|
2018-12-21 17:25:17 +01:00
|
|
|
}
|
|
|
|
|
Qt5 IM handle (spurious?) all-empty IM events
2nd attempt to fix the bug described in commit 00221089800c ("Qt5
IM allow committing empty strings") and various siblings of it.
This also reverts it.
What I see is calls with "all-empty" events (preedit, commit and
replacementLength() == 0; no QInputMethodEvent::Attribute), some
from QIBusPlatformInputContext::updatePreeditText.
There are various Writer document edit states with (selected)
text, undo, cursor position and focus changes to other windows via
Ctrl+Tab, which will result in inputMethodEvent calls totally in
contrast to the expected text state, all somehow always related to
all-empty events. They currently result in wrongly deleted
selected text, change of selection, cursor movement or general
change of text from old preedit. Most time on focus out / window
change, some times at first meta-key press after focus in.
This patch tries hard not to corrupt Writers edit state with these
all-empty events. No idea if this is some bug on LO's qt5 side or
expected, but KDE kate and VCL gtk3 and gen work fine, so I assume
Qt's behaviour is correct. FWIW gtk3 also does some extended IM
handling with focus, so probably this is the Qt equivalent of it.
But then I couldn't find some eqivalent code in Qt's source code.
I actually expected an even more complex solution (if this really
fixes all cases).
Works for a multitude of tests I tried to come up with, but is
quite probably not the final fix to this, as qt5 current doesn't
handle replacementStart() and replacementLength() at all.
Also never saw a call to Qt5Frame::EndExtTextInput.
Change-Id: I4210e0588041cfb4d80dbdfdb937e430a5f7cbfb
Reviewed-on: https://gerrit.libreoffice.org/71988
Tested-by: Jenkins
Reviewed-by: Jan-Marek Glogowski <glogow@fbihome.de>
2019-05-08 11:51:15 +00:00
|
|
|
void Qt5Widget::endExtTextInput()
|
|
|
|
{
|
|
|
|
if (m_bNonEmptyIMPreeditSeen)
|
|
|
|
{
|
|
|
|
m_rFrame.CallCallback(SalEvent::EndExtTextInput, nullptr);
|
|
|
|
m_bNonEmptyIMPreeditSeen = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-12 14:30:06 +02:00
|
|
|
void Qt5Widget::changeEvent(QEvent* pEvent)
|
|
|
|
{
|
|
|
|
switch (pEvent->type())
|
|
|
|
{
|
|
|
|
case QEvent::FontChange:
|
|
|
|
[[fallthrough]];
|
|
|
|
case QEvent::PaletteChange:
|
|
|
|
[[fallthrough]];
|
|
|
|
case QEvent::StyleChange:
|
|
|
|
{
|
|
|
|
auto* pSalInst(static_cast<Qt5Instance*>(GetSalData()->m_pInstance));
|
|
|
|
assert(pSalInst);
|
|
|
|
pSalInst->UpdateStyle(QEvent::FontChange == pEvent->type());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
QWidget::changeEvent(pEvent);
|
|
|
|
}
|
|
|
|
|
2017-10-27 13:15:37 +02:00
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|