blob: 733878e62324ea52b089ed937d2995a38ade9c52 [file] [log] [blame]
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
/*
* This file is part of the Collabora Office 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 .
*/
#include <QtTools.hxx>
#include <QtFont.hxx>
#include <QtFontFace.hxx>
#include <QtTransferable.hxx>
#include <unx/fontmanager.hxx>
#include <cairo.h>
#include <tools/stream.hxx>
#include <vcl/event.hxx>
#include <vcl/image.hxx>
#include <vcl/filter/PngImageReader.hxx>
#include <vcl/filter/PngImageWriter.hxx>
#include <vcl/qt/QtUtils.hxx>
#include <vcl/stdtext.hxx>
#include <vcl/svapp.hxx>
#include <QtCore/QBuffer>
#include <QtGui/QImage>
void CairoDeleter::operator()(cairo_surface_t* pSurface) const { cairo_surface_destroy(pSurface); }
Qt::CheckState toQtCheckState(TriState eTristate)
{
switch (eTristate)
{
case TRISTATE_FALSE:
return Qt::CheckState::Unchecked;
case TRISTATE_TRUE:
return Qt::CheckState::Checked;
case TRISTATE_INDET:
return Qt::CheckState::PartiallyChecked;
default:
assert(false && "unhandled Tristate value");
return Qt::CheckState::PartiallyChecked;
}
};
TriState toVclTriState(Qt::CheckState eTristate)
{
switch (eTristate)
{
case Qt::CheckState::Unchecked:
return TRISTATE_FALSE;
case Qt::CheckState::Checked:
return TRISTATE_TRUE;
case Qt::CheckState::PartiallyChecked:
return TRISTATE_INDET;
default:
assert(false && "unhandled Qt::CheckState value");
return TRISTATE_INDET;
}
};
sal_uInt16 toVclKeyboardModifiers(Qt::KeyboardModifiers eKeyModifiers)
{
sal_uInt16 nCode = 0;
if (eKeyModifiers & Qt::ShiftModifier)
nCode |= KEY_SHIFT;
if (eKeyModifiers & Qt::ControlModifier)
nCode |= KEY_MOD1;
if (eKeyModifiers & Qt::AltModifier)
nCode |= KEY_MOD2;
if (eKeyModifiers & Qt::MetaModifier)
nCode |= KEY_MOD3;
return nCode;
}
sal_uInt16 toVclKeyCode(int nKeyval, Qt::KeyboardModifiers eModifiers)
{
if (nKeyval >= Qt::Key_0 && nKeyval <= Qt::Key_9)
return KEY_0 + (nKeyval - Qt::Key_0);
if (nKeyval >= Qt::Key_A && nKeyval <= Qt::Key_Z)
return KEY_A + (nKeyval - Qt::Key_A);
if (nKeyval >= Qt::Key_F1 && nKeyval <= Qt::Key_F26)
return KEY_F1 + (nKeyval - Qt::Key_F1);
if (eModifiers.testFlag(Qt::KeypadModifier)
&& (nKeyval == Qt::Key_Period || nKeyval == Qt::Key_Comma))
// Qt doesn't use a special keyval for decimal separator ("," or ".")
// on numerical keypad, but sets Qt::KeypadModifier in addition
return KEY_DECIMAL;
switch (nKeyval)
{
case Qt::Key_Down:
return KEY_DOWN;
case Qt::Key_Up:
return KEY_UP;
case Qt::Key_Left:
return KEY_LEFT;
case Qt::Key_Right:
return KEY_RIGHT;
case Qt::Key_Home:
return KEY_HOME;
case Qt::Key_End:
return KEY_END;
case Qt::Key_PageUp:
return KEY_PAGEUP;
case Qt::Key_PageDown:
return KEY_PAGEDOWN;
case Qt::Key_Return:
case Qt::Key_Enter:
return KEY_RETURN;
case Qt::Key_Escape:
return KEY_ESCAPE;
case Qt::Key_Tab:
// 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:
return KEY_TAB;
case Qt::Key_Backspace:
return KEY_BACKSPACE;
case Qt::Key_Space:
return KEY_SPACE;
case Qt::Key_Insert:
return KEY_INSERT;
case Qt::Key_Delete:
return KEY_DELETE;
case Qt::Key_Plus:
return KEY_ADD;
case Qt::Key_Minus:
return KEY_SUBTRACT;
case Qt::Key_Asterisk:
return KEY_MULTIPLY;
case Qt::Key_Slash:
return KEY_DIVIDE;
case Qt::Key_Period:
return KEY_POINT;
case Qt::Key_Comma:
return KEY_COMMA;
case Qt::Key_Less:
return KEY_LESS;
case Qt::Key_Greater:
return KEY_GREATER;
case Qt::Key_Equal:
return KEY_EQUAL;
case Qt::Key_Find:
return KEY_FIND;
case Qt::Key_Menu:
return KEY_CONTEXTMENU;
case Qt::Key_Help:
return KEY_HELP;
case Qt::Key_Undo:
return KEY_UNDO;
case Qt::Key_Redo:
return KEY_REPEAT;
case Qt::Key_Cancel:
return KEY_F11;
case Qt::Key_AsciiTilde:
return KEY_TILDE;
case Qt::Key_QuoteLeft:
return KEY_QUOTELEFT;
case Qt::Key_BracketLeft:
return KEY_BRACKETLEFT;
case Qt::Key_BracketRight:
return KEY_BRACKETRIGHT;
case Qt::Key_NumberSign:
return KEY_NUMBERSIGN;
case Qt::Key_Forward:
return KEY_XF86FORWARD;
case Qt::Key_Back:
return KEY_XF86BACK;
case Qt::Key_Colon:
return KEY_COLON;
case Qt::Key_Semicolon:
return KEY_SEMICOLON;
case Qt::Key_Copy:
return KEY_COPY;
case Qt::Key_Cut:
return KEY_CUT;
case Qt::Key_Open:
return KEY_OPEN;
case Qt::Key_Paste:
return KEY_PASTE;
default:
return 0;
}
}
KeyEvent toVclKeyEvent(QKeyEvent& rEvent)
{
const sal_uInt16 nKeyCode = toVclKeyCode(rEvent.key(), rEvent.modifiers());
const QString sText = rEvent.text();
const sal_Unicode nChar = sText.isEmpty() ? 0 : sText.at(0).unicode();
vcl::KeyCode aKeyCode(nKeyCode, toVclKeyboardModifiers(rEvent.modifiers()));
return KeyEvent(nChar, aKeyCode, 0);
}
sal_uInt16 toVclMouseButtons(Qt::MouseButtons eButtons)
{
sal_uInt16 nCode = 0;
if (eButtons & Qt::LeftButton)
nCode |= MOUSE_LEFT;
if (eButtons & Qt::MiddleButton)
nCode |= MOUSE_MIDDLE;
if (eButtons & Qt::RightButton)
nCode |= MOUSE_RIGHT;
return nCode;
}
MouseEvent toVclMouseEvent(QMouseEvent& rEvent)
{
const Point aPos = toPoint(rEvent.pos());
const sal_uInt16 nClicks = rEvent.type() == QMouseEvent::MouseButtonDblClick ? 2 : 1;
const sal_uInt16 nButtons = toVclMouseButtons(rEvent.button() | rEvent.buttons());
const sal_uInt16 nModifiers = toVclKeyboardModifiers(rEvent.modifiers());
return MouseEvent(aPos, nClicks, MouseEventModifiers::NONE, nButtons, nModifiers);
}
Qt::DropActions toQtDropActions(sal_Int8 dragOperation)
{
Qt::DropActions eRet = Qt::IgnoreAction;
if (dragOperation & css::datatransfer::dnd::DNDConstants::ACTION_COPY)
eRet |= Qt::CopyAction;
if (dragOperation & css::datatransfer::dnd::DNDConstants::ACTION_MOVE)
eRet |= Qt::MoveAction;
if (dragOperation & css::datatransfer::dnd::DNDConstants::ACTION_LINK)
eRet |= Qt::LinkAction;
return eRet;
}
sal_Int8 toVclDropActions(Qt::DropActions dragOperation)
{
sal_Int8 nRet(0);
if (dragOperation & Qt::CopyAction)
nRet |= css::datatransfer::dnd::DNDConstants::ACTION_COPY;
if (dragOperation & Qt::MoveAction)
nRet |= css::datatransfer::dnd::DNDConstants::ACTION_MOVE;
if (dragOperation & Qt::LinkAction)
nRet |= css::datatransfer::dnd::DNDConstants::ACTION_LINK;
return nRet;
}
sal_Int8 toVclDropAction(Qt::DropAction dragOperation)
{
sal_Int8 nRet(0);
if (dragOperation == Qt::CopyAction)
nRet = css::datatransfer::dnd::DNDConstants::ACTION_COPY;
else if (dragOperation == Qt::MoveAction)
nRet = css::datatransfer::dnd::DNDConstants::ACTION_MOVE;
else if (dragOperation == Qt::LinkAction)
nRet = css::datatransfer::dnd::DNDConstants::ACTION_LINK;
return nRet;
}
QImage toQImage(const Image& rImage)
{
QImage aImage;
if (!!rImage)
{
SvMemoryStream aMemStm;
auto rBitmap = rImage.GetBitmap();
vcl::PngImageWriter aWriter(aMemStm);
aWriter.write(rBitmap);
aImage.loadFromData(static_cast<const uchar*>(aMemStm.GetData()), aMemStm.TellEnd());
}
return aImage;
}
Image toImage(const QImage& rImage)
{
if (rImage.isNull())
return {};
QByteArray aByteArray;
QBuffer aBuffer(&aByteArray);
aBuffer.open(QIODevice::WriteOnly);
rImage.save(&aBuffer, "PNG");
Bitmap aBitmap;
SvMemoryStream aMemoryStream(aByteArray.data(), aByteArray.size(), StreamMode::READ);
vcl::PngImageReader aReader(aMemoryStream);
assert(aReader.read(aBitmap));
return Image(aBitmap);
}
QFont toQtFont(const vcl::Font& rVclFont)
{
QFont aQFont(toQString(rVclFont.GetFamilyName()), rVclFont.GetFontHeight());
QtFont::applyStretch(aQFont, rVclFont.GetWidthType());
QtFont::applyStyle(aQFont, rVclFont.GetItalic());
QtFont::applyWeight(aQFont, rVclFont.GetWeight());
return aQFont;
}
bool toVclFont(const QFont& rQFont, const css::lang::Locale& rLocale, vcl::Font& rVclFont)
{
FontAttributes aFA;
QtFontFace::fillAttributesFromQFont(rQFont, aFA);
bool bFound = psp::PrintFontManager::get().matchFont(aFA, rLocale);
SAL_INFO("vcl.qt",
"font match result for '"
<< rQFont.family() << "': "
<< (bFound ? OUString::Concat("'") + aFA.GetFamilyName() + "'" : u"failed"_ustr));
if (!bFound)
return false;
QFontInfo qFontInfo(rQFont);
int nPointHeight = qFontInfo.pointSize();
if (nPointHeight <= 0)
nPointHeight = rQFont.pointSize();
vcl::Font aFont(aFA.GetFamilyName(), Size(0, nPointHeight));
if (aFA.GetWeight() != WEIGHT_DONTKNOW)
aFont.SetWeight(aFA.GetWeight());
if (aFA.GetWidthType() != WIDTH_DONTKNOW)
aFont.SetWidthType(aFA.GetWidthType());
if (aFA.GetItalic() != ITALIC_DONTKNOW)
aFont.SetItalic(aFA.GetItalic());
if (aFA.GetPitch() != PITCH_DONTKNOW)
aFont.SetPitch(aFA.GetPitch());
rVclFont = aFont;
return true;
}
QMessageBox::Icon vclMessageTypeToQtIcon(VclMessageType eType)
{
QMessageBox::Icon eRet = QMessageBox::Information;
switch (eType)
{
case VclMessageType::Info:
eRet = QMessageBox::Information;
break;
case VclMessageType::Warning:
eRet = QMessageBox::Warning;
break;
case VclMessageType::Question:
eRet = QMessageBox::Question;
break;
case VclMessageType::Error:
eRet = QMessageBox::Critical;
break;
case VclMessageType::Other:
eRet = QMessageBox::Information;
break;
}
return eRet;
}
QString vclMessageTypeToQtTitle(VclMessageType eType)
{
QString title;
switch (eType)
{
case VclMessageType::Info:
title = toQString(GetStandardInfoBoxText());
break;
case VclMessageType::Warning:
title = toQString(GetStandardWarningBoxText());
break;
case VclMessageType::Question:
title = toQString(GetStandardQueryBoxText());
break;
case VclMessageType::Error:
title = toQString(GetStandardErrorBoxText());
break;
case VclMessageType::Other:
title = toQString(Application::GetDisplayName());
break;
}
return title;
}
QString vclToQtStringWithAccelerator(const OUString& rText)
{
// preserve literal '&'s and use '&' instead of '~' for the accelerator
return toQString(rText.replaceAll("&", "&&").replace('~', '&'));
}
OUString qtToVclStringWithAccelerator(const QString& rText)
{
if (rText.isEmpty())
return OUString();
// find and replace single "&" used for accelerator
qsizetype nIndex = 0;
while (nIndex < rText.size())
{
nIndex = rText.indexOf('&', nIndex);
// skip "&&", i.e. escaped '&'
if (nIndex < rText.length() - 1 && rText.at(nIndex + 1) == '&')
nIndex += 2;
else
break;
}
QString sModified = rText;
if (nIndex >= 0)
sModified.replace(nIndex, 1, '~');
// replace escaped "&&" with plain "&"
return toOUString(sModified.replace("&&", "&"));
}
QString toRichTextTooltip(const OUString& rText)
{
if (rText.isEmpty())
return QString();
return QStringLiteral(u"<html>") + toQString(rText).toHtmlEscaped()
+ QStringLiteral(u"</html>");
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */