diff --git a/MSVC.md b/MSVC.md
index b5e1e4fad..dc6db0c6d 100644
--- a/MSVC.md
+++ b/MSVC.md
@@ -142,7 +142,7 @@ There go to Qt directory
and after that run configure
- configure -debug-and-release -opensource -confirm-license -static -I "D:\TBuild\Libraries\OpenSSL-Win32\include" -L "C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\Lib" -l Gdi32 -opengl desktop -openssl-linked OPENSSL_LIBS_DEBUG="D:\TBuild\Libraries\OpenSSL-Win32\lib\VC\static\ssleay32MTd.lib D:\TBuild\Libraries\OpenSSL-Win32\lib\VC\static\libeay32MTd.lib" OPENSSL_LIBS_RELEASE="D:\TBuild\Libraries\OpenSSL-Win32\lib\VC\static\ssleay32MT.lib D:\TBuild\Libraries\OpenSSL-Win32\lib\VC\static\libeay32MT.lib" -mp -nomake examples -nomake tests -platform win32-msvc2013
+ configure -debug-and-release -opensource -confirm-license -static -I "D:\TBuild\Libraries\OpenSSL-Win32\include" -L "C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\Lib" -l Gdi32 -no-opengl -openssl-linked OPENSSL_LIBS_DEBUG="D:\TBuild\Libraries\OpenSSL-Win32\lib\VC\static\ssleay32MTd.lib D:\TBuild\Libraries\OpenSSL-Win32\lib\VC\static\libeay32MTd.lib" OPENSSL_LIBS_RELEASE="D:\TBuild\Libraries\OpenSSL-Win32\lib\VC\static\ssleay32MT.lib D:\TBuild\Libraries\OpenSSL-Win32\lib\VC\static\libeay32MT.lib" -mp -nomake examples -nomake tests -platform win32-msvc2013
to configure Qt build. After configuration is complete run
diff --git a/Telegram.sln b/Telegram.sln
index a59887f8a..d33570a0c 100644
--- a/Telegram.sln
+++ b/Telegram.sln
@@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2013
-VisualStudioVersion = 12.0.30110.0
+VisualStudioVersion = 12.0.30501.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Telegram", "Telegram\Telegram.vcxproj", "{B12702AD-ABFB-343A-A199-8E24837244A3}"
ProjectSection(ProjectDependencies) = postProject
@@ -16,8 +16,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MetaEmoji", "Telegram\MetaE
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Updater", "Telegram\Updater.vcxproj", "{6B4BA3BE-7B15-4B4C-B200-81ABFDEF2C76}"
EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Prepare", "Telegram\Prepare.vcxproj", "{88AB1138-143A-4CFB-A0E6-79B646B5E1B0}"
-EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MetaLang", "Telegram\MetaLang.vcxproj", "{E417CAA4-259B-4C99-88E3-805F1300E8EB}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{2F863EAD-33C9-4014-A573-93F085BA9CB1}"
@@ -71,15 +69,6 @@ Global
{6B4BA3BE-7B15-4B4C-B200-81ABFDEF2C76}.Release|Win32.ActiveCfg = Release|Win32
{6B4BA3BE-7B15-4B4C-B200-81ABFDEF2C76}.Release|Win32.Build.0 = Release|Win32
{6B4BA3BE-7B15-4B4C-B200-81ABFDEF2C76}.Release|x64.ActiveCfg = Release|Win32
- {88AB1138-143A-4CFB-A0E6-79B646B5E1B0}.Debug|Win32.ActiveCfg = Debug|Win32
- {88AB1138-143A-4CFB-A0E6-79B646B5E1B0}.Debug|Win32.Build.0 = Debug|Win32
- {88AB1138-143A-4CFB-A0E6-79B646B5E1B0}.Debug|x64.ActiveCfg = Debug|Win32
- {88AB1138-143A-4CFB-A0E6-79B646B5E1B0}.Deploy|Win32.ActiveCfg = Deploy|Win32
- {88AB1138-143A-4CFB-A0E6-79B646B5E1B0}.Deploy|Win32.Build.0 = Deploy|Win32
- {88AB1138-143A-4CFB-A0E6-79B646B5E1B0}.Deploy|x64.ActiveCfg = Release|Win32
- {88AB1138-143A-4CFB-A0E6-79B646B5E1B0}.Release|Win32.ActiveCfg = Release|Win32
- {88AB1138-143A-4CFB-A0E6-79B646B5E1B0}.Release|Win32.Build.0 = Release|Win32
- {88AB1138-143A-4CFB-A0E6-79B646B5E1B0}.Release|x64.ActiveCfg = Release|Win32
{E417CAA4-259B-4C99-88E3-805F1300E8EB}.Debug|Win32.ActiveCfg = Debug|Win32
{E417CAA4-259B-4C99-88E3-805F1300E8EB}.Debug|Win32.Build.0 = Debug|Win32
{E417CAA4-259B-4C99-88E3-805F1300E8EB}.Debug|x64.ActiveCfg = Debug|Win32
diff --git a/Telegram/Prepare.vcxproj b/Telegram/Prepare.vcxproj
deleted file mode 100644
index 7dc896256..000000000
--- a/Telegram/Prepare.vcxproj
+++ /dev/null
@@ -1,126 +0,0 @@
-
-
-
-
- Debug
- Win32
-
-
- Deploy
- Win32
-
-
- Release
- Win32
-
-
-
-
-
-
-
-
-
- {88AB1138-143A-4CFB-A0E6-79B646B5E1B0}
- Qt4VSv1.0
-
-
-
- Application
- v120_xp
-
-
- Application
- v120_xp
-
-
- Application
- v120_xp
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- <_ProjectFileVersion>11.0.61030.0
-
-
- $(SolutionDir)$(Platform)\$(Configuration)\
- $(SolutionDir)$(Platform)\$(Configuration)IntermediatePrepare\
-
-
- $(SolutionDir)$(Platform)\$(Configuration)\
- $(SolutionDir)$(Platform)\$(Configuration)IntermediatePrepare\
-
-
- $(SolutionDir)$(Platform)\$(Configuration)\
- $(SolutionDir)$(Platform)\$(Configuration)IntermediatePrepare\
-
-
-
- UNICODE;WIN32;WIN64;QT_CORE_LIB;%(PreprocessorDefinitions)
- Disabled
- ProgramDatabase
- MultiThreadedDebug
- .;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName);$(QTDIR)\include\QtCore;%(AdditionalIncludeDirectories)
- false
-
-
- Console
- $(OutDir)\$(ProjectName).exe
- $(QTDIR)\lib;%(AdditionalLibraryDirectories)
- kernel32.lib;user32.lib;shell32.lib;uuid.lib;ole32.lib;advapi32.lib;ws2_32.lib;qtmaind.lib;Qt5Cored.lib;%(AdditionalDependencies)
- true
-
-
-
-
- UNICODE;WIN32;WIN64;QT_NO_DEBUG;NDEBUG;QT_CORE_LIB;%(PreprocessorDefinitions)
-
- MultiThreaded
- .;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName);$(QTDIR)\include\QtCore;%(AdditionalIncludeDirectories)
- false
-
-
- Console
- $(OutDir)\$(ProjectName).exe
- $(QTDIR)\lib;%(AdditionalLibraryDirectories)
- kernel32.lib;user32.lib;shell32.lib;uuid.lib;ole32.lib;advapi32.lib;ws2_32.lib;qtmain.lib;Qt5Core.lib;%(AdditionalDependencies)
- false
-
-
-
-
- UNICODE;WIN32;WIN64;QT_NO_DEBUG;NDEBUG;QT_CORE_LIB;%(PreprocessorDefinitions)
-
-
- MultiThreaded
- .;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName);$(QTDIR)\include\QtCore;%(AdditionalIncludeDirectories)
- false
-
-
- Console
- $(OutDir)\$(ProjectName).exe
- $(QTDIR)\lib;%(AdditionalLibraryDirectories)
- kernel32.lib;user32.lib;shell32.lib;uuid.lib;ole32.lib;advapi32.lib;ws2_32.lib;qtmain.lib;Qt5Core.lib;%(AdditionalDependencies)
- false
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/Telegram/Prepare.vcxproj.filters b/Telegram/Prepare.vcxproj.filters
deleted file mode 100644
index c4a106555..000000000
--- a/Telegram/Prepare.vcxproj.filters
+++ /dev/null
@@ -1,33 +0,0 @@
-
-
-
-
- {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
- cpp;cxx;c;def
-
-
- {93995380-89BD-4b04-88EB-625FBE52EBFB}
- h
-
-
- {D9D6E242-F8AF-46E4-B9FD-80ECBC20BA3E}
- qrc;*
- false
-
-
- {71ED8ED8-ACB9-4CE9-BBE1-E00B30144E11}
- moc;h;cpp
- False
-
-
-
-
- Source Files
-
-
-
-
- Header Files
-
-
-
\ No newline at end of file
diff --git a/Telegram/PrepareWin.bat b/Telegram/PrepareWin.bat
index 8a019fba8..23c286dc6 100644
--- a/Telegram/PrepareWin.bat
+++ b/Telegram/PrepareWin.bat
@@ -1,8 +1,9 @@
@echo OFF
-set "AppVersionStrSmall=0.7.16"
-set "AppVersionStr=0.7.16"
-set "AppVersionStrFull=0.7.16.0"
+set "AppVersion=7017"
+set "AppVersionStrSmall=0.7.17"
+set "AppVersionStr=0.7.17"
+set "AppVersionStrFull=0.7.17.0"
set "DevChannel=0"
if %DevChannel% neq 0 goto preparedev
@@ -22,6 +23,10 @@ echo.
echo Preparing version %AppVersionStr%%DevPostfix%..
echo.
+if exist ..\Win32\Deploy\deploy\%AppVersionStr%\ goto error_exist1
+if exist ..\Win32\Deploy\deploy\%AppVersionStr%.dev\ goto error_exist2
+if exist ..\Win32\Deploy\tupdate%AppVersion% goto error_exist3
+
set "PATH=%PATH%;C:\Program Files\7-Zip;C:\Program Files (x86)\Inno Setup 5"
cd ..\Win32\Deploy
@@ -37,12 +42,21 @@ if %errorlevel% neq 0 goto error1
call ..\..\..\TelegramPrivate\Sign.bat tsetup.%AppVersionStr%%DevPostfix%.exe
if %errorlevel% neq 0 goto error1
-call Prepare.exe -path Telegram.exe -path Updater.exe %DevParam%
+call Packer.exe -version %AppVersion% -path Telegram.exe -path Updater.exe %DevParam%
if %errorlevel% neq 0 goto error1
+if not exist deploy mkdir deploy
+mkdir deploy\%AppVersionStr%%DevPostfix%
+mkdir deploy\%AppVersionStr%%DevPostfix%\Telegram
+
+move Telegram.exe deploy\%AppVersionStr%%DevPostfix%\Telegram\
+move Updater.exe deploy\%AppVersionStr%%DevPostfix%\
+move Telegram.pdb deploy\%AppVersionStr%%DevPostfix%\
+move Updater.pdb deploy\%AppVersionStr%%DevPostfix%\
+move tsetup.%AppVersionStr%%DevPostfix%.exe deploy\%AppVersionStr%%DevPostfix%\
+move tupdate%AppVersion% deploy\%AppVersionStr%%DevPostfix%\
+
cd deploy\%AppVersionStr%%DevPostfix%
-mkdir Telegram
-move Telegram.exe Telegram\
7z a -mx9 tportable.%AppVersionStr%%DevPostfix%.zip Telegram\
if %errorlevel% neq 0 goto error2
@@ -60,4 +74,16 @@ cd ..\..\Telegram
echo ERROR occured!
exit /b %errorlevel%
+:error_exist1
+echo Deploy folder for version %AppVersionStr% already exists!
+exit /b 1
+
+:error_exist2
+echo Deploy folder for version %AppVersionStr%.dev already exists!
+exit /b 1
+
+:error_exist3
+echo Update file for version %AppVersion% already exists!
+exit /b 1
+
:eof
diff --git a/Telegram/SourceFiles/_other/packer.cpp b/Telegram/SourceFiles/_other/packer.cpp
index deb0ed572..bd1a7df10 100644
--- a/Telegram/SourceFiles/_other/packer.cpp
+++ b/Telegram/SourceFiles/_other/packer.cpp
@@ -253,7 +253,12 @@ int main(int argc, char *argv[])
size_t compressedLen = compressed.size() - hSize;
size_t outPropsSize = LZMA_PROPS_SIZE;
- int res = LzmaCompress((uchar*)(compressed.data() + hSize), &compressedLen, (const uchar*)(result.constData()), result.size(), (uchar*)(compressed.data() + hSigLen + hShaLen), &outPropsSize, 9, 64 * 1024 * 1024, 0, 0, 0, 0, 0);
+ uchar *_dest = (uchar*)(compressed.data() + hSize);
+ size_t *_destLen = &compressedLen;
+ const uchar *_src = (const uchar*)(result.constData());
+ size_t _srcLen = result.size();
+ uchar *_outProps = (uchar*)(compressed.data() + hSigLen + hShaLen);
+ int res = LzmaCompress(_dest, _destLen, _src, _srcLen, _outProps, &outPropsSize, 9, 64 * 1024 * 1024, 4, 0, 2, 273, 2);
if (res != SZ_OK) {
cout << "Error in compression: " << res << "\n";
return -1;
diff --git a/Telegram/SourceFiles/_other/prepare.cpp b/Telegram/SourceFiles/_other/prepare.cpp
deleted file mode 100644
index 4c05acb57..000000000
--- a/Telegram/SourceFiles/_other/prepare.cpp
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
-This file is part of Telegram Desktop,
-the official desktop version of Telegram messaging app, see https://telegram.org
-
-Telegram Desktop is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-It is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
-Copyright (c) 2014 John Preston, https://desktop.telegram.org
-*/
-#include "prepare.h"
-
-bool DevChannel = false;
-
-int prepare(QFileInfo f, QStringList paths) {
- if (paths.isEmpty()) {
- cout << "No -path args were passed :(\n";
- return -1;
- }
-
- int lastVersion = 0;
- QString lastVersionStr;
- QFileInfo last;
- QFileInfoList l = f.absoluteDir().entryInfoList(QDir::Files);
- for (QFileInfoList::iterator i = l.begin(), e = l.end(); i != e; ++i) {
- QRegularExpressionMatch m = QRegularExpression("/tsetup\\.((\\d+)\\.(\\d+)\\.(\\d+))(?:\\.dev)?\\.exe$").match(i->absoluteFilePath());
- if (!m.hasMatch()) continue;
-
- int version = m.captured(2).toInt() * 1000000 + m.captured(3).toInt() * 1000 + m.captured(4).toInt();
- if (version > lastVersion) {
- lastVersion = version;
- lastVersionStr = m.captured(1);
- last = *i;
- }
- }
-
- if (!lastVersion) {
- cout << "No tsetup.X.Y.Z.exe or tsetup.X.Y.Z.dev.exe found :(\n";
- return -1;
- }
-
- cout << "Last version: " << (lastVersionStr + (DevChannel ? ".dev" : "")).toUtf8().constData() << " (" << lastVersion << "), executing packer..\n";
-
- QDir dir("deploy/" + lastVersionStr + (DevChannel ? ".dev" : ""));
- if (dir.exists()) {
- cout << "Version " << (lastVersionStr + (DevChannel ? ".dev" : "")).toUtf8().constData() << " already exists in /deploy..\n";
- return -1;
- } else if (QDir("deploy/" + lastVersionStr + (DevChannel ? "" : ".dev")).exists()) {
- cout << "Version " << (lastVersionStr + (DevChannel ? "" : ".dev")).toUtf8().constData() << " already exists in /deploy..\n";
- return -1;
- }
-
- QString packer = QString("Packer.exe -version %1").arg(lastVersion);
- for (QStringList::iterator i = paths.begin(), e = paths.end(); i != e; ++i) {
- packer += " -path " + *i;
- }
- if (DevChannel) packer += " -dev";
-
- int res = system(packer.toUtf8().constData());
-
- if (res) return res;
-
- dir.mkpath(".");
-
- paths.push_back("Telegram.pdb");
- paths.push_back("Updater.pdb");
- paths.push_back("tsetup." + lastVersionStr + (DevChannel ? ".dev" : "") + ".exe");
- paths.push_back(QString("tupdate%1").arg(lastVersion));
- for (QStringList::iterator i = paths.begin(), e = paths.end(); i != e; ++i) {
- if (!QFile::copy(*i, "deploy/" + lastVersionStr + (DevChannel ? ".dev" : "") + "/" + *i)) {
- cout << "Could not copy " << i->toUtf8().constData() << " to deploy/" << (lastVersionStr + (DevChannel ? ".dev" : "")).toUtf8().constData() << "\n";
- return -1;
- }
- cout << "Copied " << i->toUtf8().constData() << "..\n";
- }
- for (QStringList::iterator i = paths.begin(), e = paths.end(); i != e; ++i) {
- QFile::remove(*i);
- }
-
- cout << "Update created in deploy/" << (lastVersionStr + (DevChannel ? ".dev" : "")).toUtf8().constData() << "\n";
-
- return 0;
-}
-
-int main(int argc, char *argv[])
-{
- QFileInfo f(argv[0]);
-
- QStringList paths;
- for (int i = 1; i < argc; ++i) {
- if (string(argv[i]) == "-path" && i + 1 < argc) {
- paths.push_back(QString(argv[i + 1]));
- } else if (string(argv[i]) == "-dev") {
- DevChannel = true;
- }
- }
- int res = prepare(f, paths);
- return res;
-}
diff --git a/Telegram/SourceFiles/_other/prepare.h b/Telegram/SourceFiles/_other/prepare.h
deleted file mode 100644
index b12c1a645..000000000
--- a/Telegram/SourceFiles/_other/prepare.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
-This file is part of Telegram Desktop,
-the official desktop version of Telegram messaging app, see https://telegram.org
-
-Telegram Desktop is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-It is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
-Copyright (c) 2014 John Preston, https://desktop.telegram.org
-*/
-#pragma once
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include
-#include
-
-#include
-#include
-#include
-using std::string;
-using std::wstring;
-using std::cout;
\ No newline at end of file
diff --git a/Telegram/SourceFiles/application.cpp b/Telegram/SourceFiles/application.cpp
index be5f8cc57..b894d74ca 100644
--- a/Telegram/SourceFiles/application.cpp
+++ b/Telegram/SourceFiles/application.cpp
@@ -22,7 +22,6 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
#include "pspecific.h"
#include "fileuploader.h"
#include "mainwidget.h"
-#include "supporttl.h"
#include "lang.h"
#include "boxes/confirmbox.h"
@@ -674,7 +673,6 @@ void Application::startApp() {
window->init();
DEBUG_LOG(("Application Info: window created.."));
- readSupportTemplates();
MTP::start();
@@ -706,8 +704,8 @@ void Application::startApp() {
QString versionFeatures;
if (DevChannel && Local::oldMapVersion() < 7015) {
versionFeatures = QString::fromUtf8("\xe2\x80\x94 Some translation keys added\n\xe2\x80\x94 Linux: fixed semibold font\n\xe2\x80\x94 Linux: tray icon should work better in non-Unity environments");
- } else if (!DevChannel && Local::oldMapVersion() < 7016) {
- versionFeatures = lang(lng_new_version7016).trimmed();
+ } else if (!DevChannel && Local::oldMapVersion() < 7017) {
+ versionFeatures = lang(lng_new_version_minor).trimmed();
}
if (!versionFeatures.isEmpty()) {
versionFeatures = lng_new_version_wrap(lt_version, QString::fromStdWString(AppVersionStr), lt_changes, versionFeatures, lt_link, qsl("https://desktop.telegram.org/#changelog"));
diff --git a/Telegram/SourceFiles/config.h b/Telegram/SourceFiles/config.h
index b42b299a4..410f8824a 100644
--- a/Telegram/SourceFiles/config.h
+++ b/Telegram/SourceFiles/config.h
@@ -17,8 +17,8 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
*/
#pragma once
-static const int32 AppVersion = 7016;
-static const wchar_t *AppVersionStr = L"0.7.16";
+static const int32 AppVersion = 7017;
+static const wchar_t *AppVersionStr = L"0.7.17";
static const bool DevChannel = false;
static const wchar_t *AppNameOld = L"Telegram Win (Unofficial)";
diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp
index bcc7f40e3..dc169aa8a 100644
--- a/Telegram/SourceFiles/historywidget.cpp
+++ b/Telegram/SourceFiles/historywidget.cpp
@@ -26,7 +26,6 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
#include "mainwidget.h"
#include "window.h"
#include "fileuploader.h"
-#include "supporttl.h"
#include "localstorage.h"
@@ -1585,7 +1584,6 @@ HistoryWidget::HistoryWidget(QWidget *parent) : QWidget(parent)
connect(&_attachPhoto, SIGNAL(clicked()), this, SLOT(onPhotoSelect()));
connect(&_field, SIGNAL(submitted(bool)), this, SLOT(onSend(bool)));
connect(&_field, SIGNAL(cancelled()), this, SLOT(onCancel()));
- connect(&_field, SIGNAL(tabbed()), this, SLOT(onFieldTabbed()));
connect(&_field, SIGNAL(resized()), this, SLOT(onFieldResize()));
connect(&_field, SIGNAL(focused()), this, SLOT(onFieldFocused()));
connect(&imageLoader, SIGNAL(imageReady()), this, SLOT(onPhotoReady()));
@@ -3429,49 +3427,6 @@ void HistoryWidget::keyPressEvent(QKeyEvent *e) {
}
}
-void HistoryWidget::onFieldTabbed() {
- QString v = _field.getText(), t = supportTemplate(v);
- if (!t.isEmpty()) {
- bool isImg = t.startsWith(qsl("img:")), isFile = t.startsWith(qsl("file:")), isContact = t.startsWith(qsl("contact:"));
- if (isImg || isFile) {
- QString fname = t.mid(isImg ? 4 : 5).trimmed(), text;
- int32 lineEnd = fname.indexOf(QChar('\n'));
- if (lineEnd > 0) {
- text = fname.mid(lineEnd + 1).trimmed();
- fname = fname.mid(0, lineEnd).trimmed();
- }
- if (isImg) {
- QImage img(cWorkingDir() + fname);
- if (!img.isNull()) {
- setFieldText(text);
- uploadImage(img, !text.isEmpty());
- }
- } else {
- setFieldText(text);
- uploadFile(cWorkingDir() + fname, !text.isEmpty());
- }
- } else if (isContact) {
- QString contact = t.mid(8).trimmed(), text;
- int32 lineEnd = contact.indexOf(QChar('\n'));
- if (lineEnd > 0) {
- text = contact.mid(lineEnd + 1).trimmed();
- contact = contact.mid(0, lineEnd).trimmed();
- }
- QStringList data = contact.split(QChar(' '));
- if (data.size() > 1) {
- setFieldText(text);
- QString phone = data.at(0).trimmed(), fname = data.at(1).trimmed(), lname = (data.size() > 2) ? static_cast(data.mid(2)).join(QChar(' ')).trimmed() : QString();
- shareContactConfirmation(phone, fname, lname, !text.isEmpty());
- }
- } else {
- setFieldText(t);
- QTextCursor c = _field.textCursor();
- c.movePosition(QTextCursor::End);
- _field.setTextCursor(c);
- }
- }
-}
-
void HistoryWidget::onStickerSend(DocumentData *sticker) {
if (!hist || !sticker) return;
diff --git a/Telegram/SourceFiles/historywidget.h b/Telegram/SourceFiles/historywidget.h
index c0215fe9d..951fea0a7 100644
--- a/Telegram/SourceFiles/historywidget.h
+++ b/Telegram/SourceFiles/historywidget.h
@@ -382,7 +382,6 @@ public slots:
void activate();
void onTextChange();
- void onFieldTabbed();
void onStickerSend(DocumentData *sticker);
void onVisibleChanged();
diff --git a/Telegram/SourceFiles/mediaview.cpp b/Telegram/SourceFiles/mediaview.cpp
index 02b3303d4..8128abe63 100644
--- a/Telegram/SourceFiles/mediaview.cpp
+++ b/Telegram/SourceFiles/mediaview.cpp
@@ -69,8 +69,10 @@ _saveMsgStarted(0), _saveMsgOpacity(0)
setAttribute(Qt::WA_NoSystemBackground, true);
setAttribute(Qt::WA_TranslucentBackground, true);
setMouseTracking(true);
- hide();
+ hide();
+ createWinId();
+
connect(&_close, SIGNAL(clicked()), this, SLOT(onClose()));
connect(&_save, SIGNAL(clicked()), this, SLOT(onDownload()));
connect(&_forward, SIGNAL(clicked()), this, SLOT(onForward()));
@@ -528,7 +530,7 @@ void MediaView::showPhoto(PhotoData *photo) {
void MediaView::paintEvent(QPaintEvent *e) {
QPainter p(this);
QRect r(e->rect());
-
+
QPainter::CompositionMode m = p.compositionMode();
p.setCompositionMode(QPainter::CompositionMode_Source);
diff --git a/Telegram/SourceFiles/mtproto/mtpCoreTypes.h b/Telegram/SourceFiles/mtproto/mtpCoreTypes.h
index 451f220ec..a9d1916dc 100644
--- a/Telegram/SourceFiles/mtproto/mtpCoreTypes.h
+++ b/Telegram/SourceFiles/mtproto/mtpCoreTypes.h
@@ -20,6 +20,9 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
#include "types.h"
#include
+#undef min
+#undef max
+
//#define DEBUG_MTPPRIME
#ifdef DEBUG_MTPPRIME
diff --git a/Telegram/SourceFiles/pspecific_wnd.cpp b/Telegram/SourceFiles/pspecific_wnd.cpp
index ba86a81ee..03f8bd0fc 100644
--- a/Telegram/SourceFiles/pspecific_wnd.cpp
+++ b/Telegram/SourceFiles/pspecific_wnd.cpp
@@ -1786,7 +1786,7 @@ bool psSkipAudioNotify() {
bool psSkipDesktopNotify() {
QUERY_USER_NOTIFICATION_STATE state;
if (useShellapi && SUCCEEDED(shQueryUserNotificationState(&state))) {
- if (state == QUNS_PRESENTATION_MODE || state == QUNS_RUNNING_D3D_FULL_SCREEN || state == QUNS_BUSY) return true;
+ if (state == QUNS_PRESENTATION_MODE || state == QUNS_RUNNING_D3D_FULL_SCREEN/* || state == QUNS_BUSY*/) return true;
}
return false;
}
diff --git a/Telegram/SourceFiles/supporttl.cpp b/Telegram/SourceFiles/supporttl.cpp
deleted file mode 100644
index bea949fd8..000000000
--- a/Telegram/SourceFiles/supporttl.cpp
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
-This file is part of Telegram Desktop,
-the official desktop version of Telegram messaging app, see https://telegram.org
-
-Telegram Desktop is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-It is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
-Copyright (c) 2014 John Preston, https://desktop.telegram.org
-*/
-#include "stdafx.h"
-#include "supporttl.h"
-
-namespace {
- typedef QMap SupportTemplates;
- SupportTemplates _supportTemplates;
-
- void saveTemplate(QStringList &keys, QString &value) {
- if (!keys.isEmpty() && !value.isEmpty()) {
- if (value.at(value.size() - 1) == '\n') {
- value = value.mid(0, value.size() - 1);
- }
- for (QStringList::const_iterator i = keys.cbegin(), e = keys.cend(); i != e; ++i) {
- _supportTemplates[textSearchKey(*i)] = value;
- }
- }
- value = QString();
- }
-}
-
-void readSupportTemplates() {
- QStringList files(cWorkingDir() + qsl("support_tl.txt"));
- QDir supp(cWorkingDir() + qsl("tsupport"));
- if (supp.exists()) {
- QStringList all = supp.entryList(QDir::Files);
- for (QStringList::const_iterator i = all.cbegin(), e = all.cend(); i != e; ++i) {
- if (i->startsWith(qsl("tl_"))) {
- files.push_back(cWorkingDir() + qsl("tsupport/") + *i);
- }
- }
- }
-
- typedef QList TemplatesLines;
- enum ReadingState {
- ReadingNone = 0,
- ReadingKeys = 1,
- ReadingValue = 2,
- ReadingMoreValue = 3,
- };
-
- for (QStringList::const_iterator i = files.cbegin(), e = files.cend(); i != e; ++i) {
- QFile f(*i);
- if (!f.open(QIODevice::ReadOnly)) continue;
-
- TemplatesLines lines = f.readAll().split('\n');
-
- f.close();
-
- ReadingState state = ReadingNone;
- QStringList keys;
- QString value;
- for (TemplatesLines::const_iterator i = lines.cbegin(), e = lines.cend(); i != e; ++i) {
- QString line = QString::fromUtf8(*i).trimmed();
- QRegularExpressionMatch m = QRegularExpression(qsl("^\\{([A-Z_]+)\\}$")).match(line);
- if (m.hasMatch()) {
- saveTemplate(keys, value);
-
- QString token = m.captured(1);
- if (token == qsl("KEYS")) {
- keys.clear();
- state = ReadingKeys;
- } else if (token == qsl("VALUE")) {
- state = ReadingValue;
- } else {
- keys.clear();
- state = ReadingNone;
- }
- continue;
- }
-
- switch (state) {
- case ReadingKeys:
- if (!line.isEmpty()) {
- keys.push_back(line);
- }
- break;
-
- case ReadingMoreValue:
- value += '\n';
- case ReadingValue:
- value += line;
- state = ReadingMoreValue;
- break;
- }
- }
- saveTemplate(keys, value);
- }
-}
-
-const QString &supportTemplate(const QString &key) {
- SupportTemplates::const_iterator i = _supportTemplates.constFind(textSearchKey(key));
- if (i != _supportTemplates.cend()) {
- return *i;
- }
-
- static const QString _tmp;
- return _tmp;
-}
diff --git a/Telegram/SourceFiles/supporttl.h b/Telegram/SourceFiles/supporttl.h
deleted file mode 100644
index 974a82595..000000000
--- a/Telegram/SourceFiles/supporttl.h
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
-This file is part of Telegram Desktop,
-the official desktop version of Telegram messaging app, see https://telegram.org
-
-Telegram Desktop is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-It is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
-Copyright (c) 2014 John Preston, https://desktop.telegram.org
-*/
-#pragma once
-
-void readSupportTemplates();
-const QString &supportTemplate(const QString &key);
diff --git a/Telegram/SourceFiles/window.cpp b/Telegram/SourceFiles/window.cpp
index 622b09c6e..f9099a1f6 100644
--- a/Telegram/SourceFiles/window.cpp
+++ b/Telegram/SourceFiles/window.cpp
@@ -329,7 +329,7 @@ NotifyWindow::~NotifyWindow() {
if (App::wnd()) App::wnd()->notifyShowNext(this);
}
-Window::Window(QWidget *parent) : PsMainWindow(parent), _serviceHistoryRequest(0),
+Window::Window(QWidget *parent) : PsMainWindow(parent), _serviceHistoryRequest(0), title(0),
intro(0), main(0), settings(0), layerBG(0), _isActive(false), _topWidget(0),
_connecting(0), _clearManager(0), dragging(false), _inactivePress(false), _mediaView(0) {
@@ -1034,6 +1034,8 @@ TitleWidget *Window::getTitle() {
}
void Window::resizeEvent(QResizeEvent *e) {
+ if (!title) return;
+
bool wideMode = (width() >= st::wideModeWidth);
if (wideMode != cWideMode()) {
cSetWideMode(wideMode);
diff --git a/Telegram/Telegram.plist b/Telegram/Telegram.plist
index 15c3decb9..c293b73f9 100644
--- a/Telegram/Telegram.plist
+++ b/Telegram/Telegram.plist
@@ -11,7 +11,7 @@
CFBundlePackageType
APPL
CFBundleShortVersionString
- 0.7.16
+ 0.7.17
CFBundleSignature
????
CFBundleURLTypes
diff --git a/Telegram/Telegram.pro b/Telegram/Telegram.pro
index e63f92455..43dd72f21 100644
--- a/Telegram/Telegram.pro
+++ b/Telegram/Telegram.pro
@@ -99,7 +99,6 @@ SOURCES += \
./SourceFiles/mainwidget.cpp \
./SourceFiles/settings.cpp \
./SourceFiles/settingswidget.cpp \
- ./SourceFiles/supporttl.cpp \
./SourceFiles/sysbuttons.cpp \
./SourceFiles/title.cpp \
./SourceFiles/types.cpp \
@@ -179,7 +178,6 @@ HEADERS += \
./SourceFiles/settings.h \
./SourceFiles/settingswidget.h \
./SourceFiles/style.h \
- ./SourceFiles/supporttl.h \
./SourceFiles/sysbuttons.h \
./SourceFiles/title.h \
./SourceFiles/types.h \
diff --git a/Telegram/Telegram.rc b/Telegram/Telegram.rc
index c0e5eac93..9720c09f6 100644
Binary files a/Telegram/Telegram.rc and b/Telegram/Telegram.rc differ
diff --git a/Telegram/Telegram.vcxproj b/Telegram/Telegram.vcxproj
index b9b00a9a0..e58af3357 100644
--- a/Telegram/Telegram.vcxproj
+++ b/Telegram/Telegram.vcxproj
@@ -914,7 +914,6 @@
Create
Create
-
@@ -1749,7 +1748,6 @@
-
Moc%27ing sysbuttons.h...
.\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp
@@ -1819,4 +1817,4 @@
-
\ No newline at end of file
+
diff --git a/Telegram/Telegram.vcxproj.filters b/Telegram/Telegram.vcxproj.filters
index 2a3ce6228..9960516b1 100644
--- a/Telegram/Telegram.vcxproj.filters
+++ b/Telegram/Telegram.vcxproj.filters
@@ -150,9 +150,6 @@
gui
-
- Source Files
-
gui
@@ -839,9 +836,6 @@
gui
-
- Source Files
-
gui
@@ -1079,4 +1073,4 @@
langs
-
\ No newline at end of file
+
diff --git a/Telegram/Telegram.xcodeproj/project.pbxproj b/Telegram/Telegram.xcodeproj/project.pbxproj
index 443f232df..50876c8d9 100644
--- a/Telegram/Telegram.xcodeproj/project.pbxproj
+++ b/Telegram/Telegram.xcodeproj/project.pbxproj
@@ -127,7 +127,6 @@
830CB6F547B8C80A569A0271 /* addparticipantbox.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 40E88FBA282991C8CBDFE94D /* addparticipantbox.cpp */; settings = {ATTRIBUTES = (); }; };
832C50BFD7D09AF042A51D4F /* flatbutton.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = F1A04BDB750C2AE652797B04 /* flatbutton.cpp */; settings = {ATTRIBUTES = (); }; };
8771A8C96E9C391044035D99 /* OpenGL.framework in Link Binary With Libraries */ = {isa = PBXBuildFile; fileRef = D4B32C2222F82AC56BADEB21 /* OpenGL.framework */; };
- 878DCAF0C5B0DB2EB4EDFD3D /* supporttl.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 0B2F770F53FBB0DBF738D89B /* supporttl.cpp */; settings = {ATTRIBUTES = (); }; };
8883FF366F2623E89D90A9E6 /* qgenericbearer in Link Binary With Libraries */ = {isa = PBXBuildFile; fileRef = 9A55B8F7C143D66AD9EAE304 /* qgenericbearer */; };
89ADB41E48A3B5E24ABB626C /* profilewidget.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = CF32DF59C7823E4F3397EF3C /* profilewidget.cpp */; settings = {ATTRIBUTES = (); }; };
8B22E794EFF0EAFF964A3043 /* introsteps.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = C34459FA465B57DF4DB80D12 /* introsteps.cpp */; settings = {ATTRIBUTES = (); }; };
@@ -311,7 +310,6 @@
098EA7CE256AAFAE4A17EB77 /* introcode.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = introcode.h; path = SourceFiles/intro/introcode.h; sourceTree = ""; };
09FD01F2BD652EB838A296D8 /* application.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = application.h; path = SourceFiles/application.h; sourceTree = ""; };
0ABCEA8D0DD45589040B0AF2 /* /usr/local/Qt-5.4.0/mkspecs/common/unix.conf */ = {isa = PBXFileReference; lastKnownFileType = text; path = "/usr/local/Qt-5.4.0/mkspecs/common/unix.conf"; sourceTree = ""; };
- 0B2F770F53FBB0DBF738D89B /* supporttl.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = supporttl.cpp; path = SourceFiles/supporttl.cpp; sourceTree = ""; };
0BDE09020E45EFA57DCB2E25 /* photosendbox.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = photosendbox.h; path = SourceFiles/boxes/photosendbox.h; sourceTree = ""; };
0C0DC15EB416789673526AA5 /* moc_emojibox.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = moc_emojibox.cpp; path = GeneratedFiles/Debug/moc_emojibox.cpp; sourceTree = ""; };
0CAA815FFFEDCD84808E11F5 /* logs.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = logs.h; path = SourceFiles/logs.h; sourceTree = ""; };
@@ -387,7 +385,6 @@
3F08D430CEC8D2117735CCB4 /* /usr/local/Qt-5.4.0/mkspecs/modules/qt_plugin_qmldbg_tcp_qtdeclarative.pri */ = {isa = PBXFileReference; lastKnownFileType = text; path = "/usr/local/Qt-5.4.0/mkspecs/modules/qt_plugin_qmldbg_tcp_qtdeclarative.pri"; sourceTree = ""; };
40E88FBA282991C8CBDFE94D /* addparticipantbox.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = addparticipantbox.cpp; path = SourceFiles/boxes/addparticipantbox.cpp; sourceTree = ""; };
420A06A32B66D250142B4B6D /* style_core.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = style_core.cpp; path = SourceFiles/gui/style_core.cpp; sourceTree = ""; };
- 42FA90460B1A91C414157EAC /* supporttl.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = supporttl.h; path = SourceFiles/supporttl.h; sourceTree = ""; };
45B95DB3B70B47A910FC847B /* /usr/local/Qt-5.4.0/mkspecs/common/gcc-base.conf */ = {isa = PBXFileReference; lastKnownFileType = text; path = "/usr/local/Qt-5.4.0/mkspecs/common/gcc-base.conf"; sourceTree = ""; };
45DB132B756499D4DF38430E /* /usr/local/Qt-5.4.0/mkspecs/modules/qt_plugin_qjp2.pri */ = {isa = PBXFileReference; lastKnownFileType = text; path = "/usr/local/Qt-5.4.0/mkspecs/modules/qt_plugin_qjp2.pri"; sourceTree = ""; };
4604687EBA85611C9E8A9CDF /* button.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = button.h; path = SourceFiles/gui/button.h; sourceTree = ""; };
@@ -891,7 +888,6 @@
047DAFB0A7DE92C63033A43C /* mainwidget.cpp */,
8A28F7789408AA839F48A5F2 /* settings.cpp */,
8CCCACE96535180FEB557712 /* settingswidget.cpp */,
- 0B2F770F53FBB0DBF738D89B /* supporttl.cpp */,
6B90F69947805586A6FAE80E /* sysbuttons.cpp */,
2BB2A1BB8DB0993F78F4E3C7 /* title.cpp */,
7D075A915E8739C1B6BC5F43 /* types.cpp */,
@@ -927,7 +923,6 @@
2EA58EF6CDF368B0132BAEB9 /* settings.h */,
F80095A026AF9453E9C2B8BD /* settingswidget.h */,
C913E6A1001E07EE7C13CE93 /* style.h */,
- 42FA90460B1A91C414157EAC /* supporttl.h */,
3BE70E2A82DC2BF402165ED5 /* sysbuttons.h */,
4D504A849F15EB58E53A4E5F /* title.h */,
1DC02F674A7192FF8BE391A7 /* types.h */,
@@ -1476,7 +1471,6 @@
E3194392BD6D0726F75FA72E /* mainwidget.cpp in Compile Sources */,
DF36EA42D67ED39E58CB7DF9 /* settings.cpp in Compile Sources */,
B99CCE43EEFCD3E18F6D16D1 /* settingswidget.cpp in Compile Sources */,
- 878DCAF0C5B0DB2EB4EDFD3D /* supporttl.cpp in Compile Sources */,
B8DA82DA1B195A933A0805E7 /* sysbuttons.cpp in Compile Sources */,
5CE57D44510AB2A11886AB52 /* title.cpp in Compile Sources */,
4078D5D614EB3ECF7F1848C7 /* types.cpp in Compile Sources */,
@@ -1633,7 +1627,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
COPY_PHASE_STRIP = NO;
- CURRENT_PROJECT_VERSION = 0.7.16;
+ CURRENT_PROJECT_VERSION = 0.7.17;
DEBUG_INFORMATION_FORMAT = dwarf;
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
@@ -1651,7 +1645,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
COPY_PHASE_STRIP = YES;
- CURRENT_PROJECT_VERSION = 0.7.16;
+ CURRENT_PROJECT_VERSION = 0.7.17;
GCC_GENERATE_DEBUGGING_SYMBOLS = NO;
GCC_OPTIMIZATION_LEVEL = fast;
GCC_PREFIX_HEADER = ./SourceFiles/stdafx.h;
@@ -1677,10 +1671,10 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "";
COPY_PHASE_STRIP = NO;
- CURRENT_PROJECT_VERSION = 0.7.16;
+ CURRENT_PROJECT_VERSION = 0.7.17;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DYLIB_COMPATIBILITY_VERSION = 0.7;
- DYLIB_CURRENT_VERSION = 0.7.16;
+ DYLIB_CURRENT_VERSION = 0.7.17;
ENABLE_STRICT_OBJC_MSGSEND = YES;
FRAMEWORK_SEARCH_PATHS = "";
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
@@ -1818,10 +1812,10 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "";
COPY_PHASE_STRIP = NO;
- CURRENT_PROJECT_VERSION = 0.7.16;
+ CURRENT_PROJECT_VERSION = 0.7.17;
DEBUG_INFORMATION_FORMAT = dwarf;
DYLIB_COMPATIBILITY_VERSION = 0.7;
- DYLIB_CURRENT_VERSION = 0.7.16;
+ DYLIB_CURRENT_VERSION = 0.7.17;
ENABLE_STRICT_OBJC_MSGSEND = YES;
FRAMEWORK_SEARCH_PATHS = "";
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
diff --git a/Telegram/Version.sh b/Telegram/Version.sh
index c71253925..44d172e2f 100755
--- a/Telegram/Version.sh
+++ b/Telegram/Version.sh
@@ -1,2 +1,2 @@
-echo 7016 0.7.16 0
+echo 7017 0.7.17 0
# AppVersion AppVersionStr DevChannel
diff --git a/Telegram/_qt_5_4_0_patch.diff b/Telegram/_qt_5_4_0_patch.diff
index 3f09d4b51..3d7210e5f 100644
--- a/Telegram/_qt_5_4_0_patch.diff
+++ b/Telegram/_qt_5_4_0_patch.diff
@@ -137,6 +137,31 @@ index 312320c..5e82318 100644
}
// Make sure we're inside the viewport.
+diff --git a/qtbase/src/gui/text/qtextlayout.cpp b/qtbase/src/gui/text/qtextlayout.cpp
+index 1ac50d3..3c88caa 100644
+--- a/qtbase/src/gui/text/qtextlayout.cpp
++++ b/qtbase/src/gui/text/qtextlayout.cpp
+@@ -643,7 +643,10 @@ int QTextLayout::nextCursorPosition(int oldPos, CursorMode mode) const
+ while (oldPos < len && !attributes[oldPos].graphemeBoundary)
+ oldPos++;
+ } else {
+- if (oldPos < len && d->atWordSeparator(oldPos)) {
++ while (oldPos < len && d->atSpace(oldPos))
++ oldPos++;
++
++ if (oldPos < len && d->atWordSeparator(oldPos)) {
+ oldPos++;
+ while (oldPos < len && d->atWordSeparator(oldPos))
+ oldPos++;
+@@ -651,8 +654,6 @@ int QTextLayout::nextCursorPosition(int oldPos, CursorMode mode) const
+ while (oldPos < len && !d->atSpace(oldPos) && !d->atWordSeparator(oldPos))
+ oldPos++;
+ }
+- while (oldPos < len && d->atSpace(oldPos))
+- oldPos++;
+ }
+
+ return oldPos;
diff --git a/qtbase/src/gui/text/qtextlayout.h b/qtbase/src/gui/text/qtextlayout.h
index 1e0ab9b..47972d3 100644
--- a/qtbase/src/gui/text/qtextlayout.h
@@ -647,7 +672,7 @@ index f1f472b..97819dd 100644
{
m_data.setSelectedNameFilter(f); // Dialog cannot be updated at run-time.
diff --git a/qtbase/src/plugins/platforms/windows/qwindowskeymapper.cpp b/qtbase/src/plugins/platforms/windows/qwindowskeymapper.cpp
-index ff9ad18..ba423b4 100644
+index ff9ad18..3fd0848 100644
--- a/qtbase/src/plugins/platforms/windows/qwindowskeymapper.cpp
+++ b/qtbase/src/plugins/platforms/windows/qwindowskeymapper.cpp
@@ -537,17 +537,16 @@ static inline int toKeyOrUnicode(int vk, int scancode, unsigned char *kbdBuffer,
@@ -678,6 +703,18 @@ index ff9ad18..ba423b4 100644
code = unicodeBuffer[0].toUpper().unicode();
// Qt::Key_*'s are not encoded below 0x20, so try again, and DEL keys (0x7f) is encoded with a
+@@ -833,7 +832,10 @@ bool QWindowsKeyMapper::translateMultimediaKeyEventInternal(QWindow *window, con
+
+ const int qtKey = CmdTbl[cmd];
+ sendExtendedPressRelease(receiver, qtKey, Qt::KeyboardModifier(state), 0, 0, 0);
+- return true;
++ // QTBUG-43343: Make sure to return false if Qt does not handle the key, otherwise,
++ // the keys are not passed to the active media player.
++ const QKeySequence sequence(Qt::Modifier(state) + qtKey);
++ return QGuiApplicationPrivate::instance()->shortcutMap.hasShortcutForKeySequence(sequence);
+ #else
+ Q_UNREACHABLE();
+ return false;
diff --git a/qtbase/src/plugins/platforms/windows/qwindowswindow.cpp b/qtbase/src/plugins/platforms/windows/qwindowswindow.cpp
index 8a80729..16fda26 100644
--- a/qtbase/src/plugins/platforms/windows/qwindowswindow.cpp
diff --git a/Telegram/_qt_5_4_0_patch/qtbase/src/gui/text/qtextlayout.cpp b/Telegram/_qt_5_4_0_patch/qtbase/src/gui/text/qtextlayout.cpp
new file mode 100644
index 000000000..3c88caa39
--- /dev/null
+++ b/Telegram/_qt_5_4_0_patch/qtbase/src/gui/text/qtextlayout.cpp
@@ -0,0 +1,2905 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qtextlayout.h"
+#include "qtextengine_p.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "qtextdocument_p.h"
+#include "qtextformat_p.h"
+#include "qpainterpath.h"
+#include "qglyphrun.h"
+#include "qglyphrun_p.h"
+#include "qrawfont.h"
+#include "qrawfont_p.h"
+#include
+
+#include
+
+#include "qfontengine_p.h"
+#include
+
+QT_BEGIN_NAMESPACE
+
+#define ObjectSelectionBrush (QTextFormat::ForegroundBrush + 1)
+#define SuppressText 0x5012
+#define SuppressBackground 0x513
+
+/*!
+ \class QTextLayout::FormatRange
+ \reentrant
+
+ \brief The QTextLayout::FormatRange structure is used to apply extra formatting information
+ for a specified area in the text layout's content.
+ \inmodule QtGui
+
+ \sa QTextLayout::setAdditionalFormats(), QTextLayout::draw()
+*/
+
+/*!
+ \variable QTextLayout::FormatRange::start
+ Specifies the beginning of the format range within the text layout's text.
+*/
+
+/*!
+ \variable QTextLayout::FormatRange::length
+ Specifies the numer of characters the format range spans.
+*/
+
+/*!
+ \variable QTextLayout::FormatRange::format
+ Specifies the format to apply.
+*/
+
+/*!
+ \class QTextInlineObject
+ \reentrant
+
+ \brief The QTextInlineObject class represents an inline object in
+ a QAbstractTextDocumentLayout and its implementations.
+ \inmodule QtGui
+
+ \ingroup richtext-processing
+
+ Normally, you do not need to create a QTextInlineObject. It is
+ used by QAbstractTextDocumentLayout to handle inline objects when
+ implementing a custom layout.
+
+ The inline object has various attributes that can be set, for
+ example using, setWidth(), setAscent(), and setDescent(). The
+ rectangle it occupies is given by rect(), and its direction by
+ textDirection(). Its position in the text layout is given by
+ textPosition(), and its format is given by format().
+*/
+
+/*!
+ \fn QTextInlineObject::QTextInlineObject(int i, QTextEngine *e)
+ \internal
+
+ Creates a new inline object for the item at position \a i in the
+ text engine \a e.
+*/
+
+/*!
+ \fn QTextInlineObject::QTextInlineObject()
+
+ \internal
+*/
+
+/*!
+ \fn bool QTextInlineObject::isValid() const
+
+ Returns \c true if this inline object is valid; otherwise returns
+ false.
+*/
+
+/*!
+ Returns the inline object's rectangle.
+
+ \sa ascent(), descent(), width()
+*/
+QRectF QTextInlineObject::rect() const
+{
+ QScriptItem& si = eng->layoutData->items[itm];
+ return QRectF(0, -si.ascent.toReal(), si.width.toReal(), si.height().toReal());
+}
+
+/*!
+ Returns the inline object's width.
+
+ \sa ascent(), descent(), rect()
+*/
+qreal QTextInlineObject::width() const
+{
+ return eng->layoutData->items[itm].width.toReal();
+}
+
+/*!
+ Returns the inline object's ascent.
+
+ \sa descent(), width(), rect()
+*/
+qreal QTextInlineObject::ascent() const
+{
+ return eng->layoutData->items[itm].ascent.toReal();
+}
+
+/*!
+ Returns the inline object's descent.
+
+ \sa ascent(), width(), rect()
+*/
+qreal QTextInlineObject::descent() const
+{
+ return eng->layoutData->items[itm].descent.toReal();
+}
+
+/*!
+ Returns the inline object's total height. This is equal to
+ ascent() + descent() + 1.
+
+ \sa ascent(), descent(), width(), rect()
+*/
+qreal QTextInlineObject::height() const
+{
+ return eng->layoutData->items[itm].height().toReal();
+}
+
+/*!
+ Sets the inline object's width to \a w.
+
+ \sa width(), ascent(), descent(), rect()
+*/
+void QTextInlineObject::setWidth(qreal w)
+{
+ eng->layoutData->items[itm].width = QFixed::fromReal(w);
+}
+
+/*!
+ Sets the inline object's ascent to \a a.
+
+ \sa ascent(), setDescent(), width(), rect()
+*/
+void QTextInlineObject::setAscent(qreal a)
+{
+ eng->layoutData->items[itm].ascent = QFixed::fromReal(a);
+}
+
+/*!
+ Sets the inline object's descent to \a d.
+
+ \sa descent(), setAscent(), width(), rect()
+*/
+void QTextInlineObject::setDescent(qreal d)
+{
+ eng->layoutData->items[itm].descent = QFixed::fromReal(d);
+}
+
+/*!
+ The position of the inline object within the text layout.
+*/
+int QTextInlineObject::textPosition() const
+{
+ return eng->layoutData->items[itm].position;
+}
+
+/*!
+ Returns an integer describing the format of the inline object
+ within the text layout.
+*/
+int QTextInlineObject::formatIndex() const
+{
+ return eng->formatIndex(&eng->layoutData->items[itm]);
+}
+
+/*!
+ Returns format of the inline object within the text layout.
+*/
+QTextFormat QTextInlineObject::format() const
+{
+ return eng->format(&eng->layoutData->items[itm]);
+}
+
+/*!
+ Returns if the object should be laid out right-to-left or left-to-right.
+*/
+Qt::LayoutDirection QTextInlineObject::textDirection() const
+{
+ return (eng->layoutData->items[itm].analysis.bidiLevel % 2 ? Qt::RightToLeft : Qt::LeftToRight);
+}
+
+/*!
+ \class QTextLayout
+ \reentrant
+
+ \brief The QTextLayout class is used to lay out and render text.
+ \inmodule QtGui
+
+ \ingroup richtext-processing
+
+ It offers many features expected from a modern text layout
+ engine, including Unicode compliant rendering, line breaking and
+ handling of cursor positioning. It can also produce and render
+ device independent layout, something that is important for WYSIWYG
+ applications.
+
+ The class has a rather low level API and unless you intend to
+ implement your own text rendering for some specialized widget, you
+ probably won't need to use it directly.
+
+ QTextLayout can be used with both plain and rich text.
+
+ QTextLayout can be used to create a sequence of QTextLine
+ instances with given widths and can position them independently
+ on the screen. Once the layout is done, these lines can be drawn
+ on a paint device.
+
+ The text to be laid out can be provided in the constructor or set with
+ setText().
+
+ The layout can be seen as a sequence of QTextLine objects; use createLine()
+ to create a QTextLine instance, and lineAt() or lineForTextPosition() to retrieve
+ created lines.
+
+ Here is a code snippet that demonstrates the layout phase:
+ \snippet code/src_gui_text_qtextlayout.cpp 0
+
+ The text can then be rendered by calling the layout's draw() function:
+ \snippet code/src_gui_text_qtextlayout.cpp 1
+
+ For a given position in the text you can find a valid cursor position with
+ isValidCursorPosition(), nextCursorPosition(), and previousCursorPosition().
+
+ The QTextLayout itself can be positioned with setPosition(); it has a
+ boundingRect(), and a minimumWidth() and a maximumWidth().
+
+ \sa QStaticText
+*/
+
+/*!
+ \enum QTextLayout::CursorMode
+
+ \value SkipCharacters
+ \value SkipWords
+*/
+
+/*!
+ \fn QTextEngine *QTextLayout::engine() const
+ \internal
+
+ Returns the text engine used to render the text layout.
+*/
+
+/*!
+ Constructs an empty text layout.
+
+ \sa setText()
+*/
+QTextLayout::QTextLayout()
+{ d = new QTextEngine(); }
+
+/*!
+ Constructs a text layout to lay out the given \a text.
+*/
+QTextLayout::QTextLayout(const QString& text)
+{
+ d = new QTextEngine();
+ d->text = text;
+}
+
+/*!
+ Constructs a text layout to lay out the given \a text with the specified
+ \a font.
+
+ All the metric and layout calculations will be done in terms of
+ the paint device, \a paintdevice. If \a paintdevice is 0 the
+ calculations will be done in screen metrics.
+*/
+QTextLayout::QTextLayout(const QString& text, const QFont &font, QPaintDevice *paintdevice)
+{
+ QFont f(font);
+ if (paintdevice)
+ f = QFont(font, paintdevice);
+ d = new QTextEngine((text.isNull() ? (const QString&)QString::fromLatin1("") : text), f);
+}
+
+/*!
+ \internal
+ Constructs a text layout to lay out the given \a block.
+*/
+QTextLayout::QTextLayout(const QTextBlock &block)
+{
+ d = new QTextEngine();
+ d->block = block;
+}
+
+/*!
+ Destructs the layout.
+*/
+QTextLayout::~QTextLayout()
+{
+ if (!d->stackEngine)
+ delete d;
+}
+
+#ifndef QT_NO_RAWFONT
+/*!
+ \internal
+ Sets a raw font, to be used with QTextLayout::glyphRuns.
+ Note that this only supports the needs of WebKit.
+ Use of this function with e.g. QTextLayout::draw will result
+ in undefined behaviour.
+*/
+void QTextLayout::setRawFont(const QRawFont &rawFont)
+{
+ d->rawFont = rawFont;
+ d->useRawFont = true;
+ d->resetFontEngineCache();
+}
+#endif
+
+/*!
+ Sets the layout's font to the given \a font. The layout is
+ invalidated and must be laid out again.
+
+ \sa font()
+*/
+void QTextLayout::setFont(const QFont &font)
+{
+ d->fnt = font;
+#ifndef QT_NO_RAWFONT
+ d->useRawFont = false;
+#endif
+ d->resetFontEngineCache();
+}
+
+/*!
+ Returns the current font that is used for the layout, or a default
+ font if none is set.
+
+ \sa setFont()
+*/
+QFont QTextLayout::font() const
+{
+ return d->font();
+}
+
+/*!
+ Sets the layout's text to the given \a string. The layout is
+ invalidated and must be laid out again.
+
+ Notice that when using this QTextLayout as part of a QTextDocument this
+ method will have no effect.
+
+ \sa text()
+*/
+void QTextLayout::setText(const QString& string)
+{
+ d->invalidate();
+ d->clearLineData();
+ d->text = string;
+}
+
+/*!
+ Returns the layout's text.
+
+ \sa setText()
+*/
+QString QTextLayout::text() const
+{
+ return d->text;
+}
+
+/*!
+ Sets the text option structure that controls the layout process to the
+ given \a option.
+
+ \sa textOption()
+*/
+void QTextLayout::setTextOption(const QTextOption &option)
+{
+ d->option = option;
+}
+
+/*!
+ Returns the current text option used to control the layout process.
+
+ \sa setTextOption()
+*/
+const QTextOption &QTextLayout::textOption() const
+{
+ return d->option;
+}
+
+/*!
+ Sets the \a position and \a text of the area in the layout that is
+ processed before editing occurs. The layout is
+ invalidated and must be laid out again.
+
+ \sa preeditAreaPosition(), preeditAreaText()
+*/
+void QTextLayout::setPreeditArea(int position, const QString &text)
+{
+ if (d->preeditAreaPosition() == position && d->preeditAreaText() == text)
+ return;
+ d->setPreeditArea(position, text);
+
+ if (d->block.docHandle())
+ d->block.docHandle()->documentChange(d->block.position(), d->block.length());
+}
+
+/*!
+ Returns the position of the area in the text layout that will be
+ processed before editing occurs.
+
+ \sa preeditAreaText()
+*/
+int QTextLayout::preeditAreaPosition() const
+{
+ return d->preeditAreaPosition();
+}
+
+/*!
+ Returns the text that is inserted in the layout before editing occurs.
+
+ \sa preeditAreaPosition()
+*/
+QString QTextLayout::preeditAreaText() const
+{
+ return d->preeditAreaText();
+}
+
+
+/*!
+ Sets the additional formats supported by the text layout to \a formatList.
+ The formats are applied with preedit area text in place.
+
+ \sa additionalFormats(), clearAdditionalFormats()
+*/
+void QTextLayout::setAdditionalFormats(const QList &formatList)
+{
+ d->setFormats(formatList);
+
+ if (d->block.docHandle())
+ d->block.docHandle()->documentChange(d->block.position(), d->block.length());
+}
+
+/*!
+ Returns the list of additional formats supported by the text layout.
+
+ \sa setAdditionalFormats(), clearAdditionalFormats()
+*/
+QList QTextLayout::additionalFormats() const
+{
+ return d->formats();
+}
+
+/*!
+ Clears the list of additional formats supported by the text layout.
+
+ \sa additionalFormats(), setAdditionalFormats()
+*/
+void QTextLayout::clearAdditionalFormats()
+{
+ setAdditionalFormats(QList());
+}
+
+/*!
+ Enables caching of the complete layout information if \a enable is
+ true; otherwise disables layout caching. Usually
+ QTextLayout throws most of the layouting information away after a
+ call to endLayout() to reduce memory consumption. If you however
+ want to draw the laid out text directly afterwards enabling caching
+ might speed up drawing significantly.
+
+ \sa cacheEnabled()
+*/
+void QTextLayout::setCacheEnabled(bool enable)
+{
+ d->cacheGlyphs = enable;
+}
+
+/*!
+ Returns \c true if the complete layout information is cached; otherwise
+ returns \c false.
+
+ \sa setCacheEnabled()
+*/
+bool QTextLayout::cacheEnabled() const
+{
+ return d->cacheGlyphs;
+}
+
+/*!
+ Sets the visual cursor movement style to the given \a style. If the
+ QTextLayout is backed by a document, you can ignore this and use the option
+ in QTextDocument, this option is for widgets like QLineEdit or custom
+ widgets without a QTextDocument. Default value is Qt::LogicalMoveStyle.
+
+ \sa cursorMoveStyle()
+*/
+void QTextLayout::setCursorMoveStyle(Qt::CursorMoveStyle style)
+{
+ d->visualMovement = style == Qt::VisualMoveStyle;
+}
+
+/*!
+ The cursor movement style of this QTextLayout. The default is
+ Qt::LogicalMoveStyle.
+
+ \sa setCursorMoveStyle()
+*/
+Qt::CursorMoveStyle QTextLayout::cursorMoveStyle() const
+{
+ return d->visualMovement ? Qt::VisualMoveStyle : Qt::LogicalMoveStyle;
+}
+
+/*!
+ Begins the layout process.
+
+ \sa endLayout()
+*/
+void QTextLayout::beginLayout()
+{
+#ifndef QT_NO_DEBUG
+ if (d->layoutData && d->layoutData->layoutState == QTextEngine::InLayout) {
+ qWarning("QTextLayout::beginLayout: Called while already doing layout");
+ return;
+ }
+#endif
+ d->invalidate();
+ d->clearLineData();
+ d->itemize();
+ d->layoutData->layoutState = QTextEngine::InLayout;
+}
+
+/*!
+ Ends the layout process.
+
+ \sa beginLayout()
+*/
+void QTextLayout::endLayout()
+{
+#ifndef QT_NO_DEBUG
+ if (!d->layoutData || d->layoutData->layoutState == QTextEngine::LayoutEmpty) {
+ qWarning("QTextLayout::endLayout: Called without beginLayout()");
+ return;
+ }
+#endif
+ int l = d->lines.size();
+ if (l && d->lines.at(l-1).length < 0) {
+ QTextLine(l-1, d).setNumColumns(INT_MAX);
+ }
+ d->layoutData->layoutState = QTextEngine::LayoutEmpty;
+ if (!d->cacheGlyphs)
+ d->freeMemory();
+}
+
+/*!
+ \since 4.4
+
+ Clears the line information in the layout. After having called
+ this function, lineCount() returns 0.
+*/
+void QTextLayout::clearLayout()
+{
+ d->clearLineData();
+}
+
+/*!
+ Returns the next valid cursor position after \a oldPos that
+ respects the given cursor \a mode.
+ Returns value of \a oldPos, if \a oldPos is not a valid cursor position.
+
+ \sa isValidCursorPosition(), previousCursorPosition()
+*/
+int QTextLayout::nextCursorPosition(int oldPos, CursorMode mode) const
+{
+ const QCharAttributes *attributes = d->attributes();
+ int len = d->block.isValid() ? d->block.length() - 1
+ : d->layoutData->string.length();
+ Q_ASSERT(len <= d->layoutData->string.length());
+ if (!attributes || oldPos < 0 || oldPos >= len)
+ return oldPos;
+
+ if (mode == SkipCharacters) {
+ oldPos++;
+ while (oldPos < len && !attributes[oldPos].graphemeBoundary)
+ oldPos++;
+ } else {
+ while (oldPos < len && d->atSpace(oldPos))
+ oldPos++;
+
+ if (oldPos < len && d->atWordSeparator(oldPos)) {
+ oldPos++;
+ while (oldPos < len && d->atWordSeparator(oldPos))
+ oldPos++;
+ } else {
+ while (oldPos < len && !d->atSpace(oldPos) && !d->atWordSeparator(oldPos))
+ oldPos++;
+ }
+ }
+
+ return oldPos;
+}
+
+/*!
+ Returns the first valid cursor position before \a oldPos that
+ respects the given cursor \a mode.
+ Returns value of \a oldPos, if \a oldPos is not a valid cursor position.
+
+ \sa isValidCursorPosition(), nextCursorPosition()
+*/
+int QTextLayout::previousCursorPosition(int oldPos, CursorMode mode) const
+{
+ const QCharAttributes *attributes = d->attributes();
+ int len = d->block.isValid() ? d->block.length() - 1
+ : d->layoutData->string.length();
+ Q_ASSERT(len <= d->layoutData->string.length());
+ if (!attributes || oldPos <= 0 || oldPos > len)
+ return oldPos;
+
+ if (mode == SkipCharacters) {
+ oldPos--;
+ while (oldPos && !attributes[oldPos].graphemeBoundary)
+ oldPos--;
+ } else {
+ while (oldPos && d->atSpace(oldPos-1))
+ oldPos--;
+
+ if (oldPos && d->atWordSeparator(oldPos-1)) {
+ oldPos--;
+ while (oldPos && d->atWordSeparator(oldPos-1))
+ oldPos--;
+ } else {
+ while (oldPos && !d->atSpace(oldPos-1) && !d->atWordSeparator(oldPos-1))
+ oldPos--;
+ }
+ }
+
+ return oldPos;
+}
+
+/*!
+ Returns the cursor position to the right of \a oldPos, next to it.
+ It's dependent on the visual position of characters, after bi-directional
+ reordering.
+
+ \sa leftCursorPosition(), nextCursorPosition()
+*/
+int QTextLayout::rightCursorPosition(int oldPos) const
+{
+ int newPos = d->positionAfterVisualMovement(oldPos, QTextCursor::Right);
+// qDebug("%d -> %d", oldPos, newPos);
+ return newPos;
+}
+
+/*!
+ Returns the cursor position to the left of \a oldPos, next to it.
+ It's dependent on the visual position of characters, after bi-directional
+ reordering.
+
+ \sa rightCursorPosition(), previousCursorPosition()
+*/
+int QTextLayout::leftCursorPosition(int oldPos) const
+{
+ int newPos = d->positionAfterVisualMovement(oldPos, QTextCursor::Left);
+// qDebug("%d -> %d", oldPos, newPos);
+ return newPos;
+}
+
+/*!/
+ Returns \c true if position \a pos is a valid cursor position.
+
+ In a Unicode context some positions in the text are not valid
+ cursor positions, because the position is inside a Unicode
+ surrogate or a grapheme cluster.
+
+ A grapheme cluster is a sequence of two or more Unicode characters
+ that form one indivisible entity on the screen. For example the
+ latin character `\unicode{0xC4}' can be represented in Unicode by two
+ characters, `A' (0x41), and the combining diaresis (0x308). A text
+ cursor can only validly be positioned before or after these two
+ characters, never between them since that wouldn't make sense. In
+ indic languages every syllable forms a grapheme cluster.
+*/
+bool QTextLayout::isValidCursorPosition(int pos) const
+{
+ const QCharAttributes *attributes = d->attributes();
+ if (!attributes || pos < 0 || pos > (int)d->layoutData->string.length())
+ return false;
+ return attributes[pos].graphemeBoundary;
+}
+
+/*!
+ Returns a new text line to be laid out if there is text to be
+ inserted into the layout; otherwise returns an invalid text line.
+
+ The text layout creates a new line object that starts after the
+ last line in the layout, or at the beginning if the layout is empty.
+ The layout maintains an internal cursor, and each line is filled
+ with text from the cursor position onwards when the
+ QTextLine::setLineWidth() function is called.
+
+ Once QTextLine::setLineWidth() is called, a new line can be created and
+ filled with text. Repeating this process will lay out the whole block
+ of text contained in the QTextLayout. If there is no text left to be
+ inserted into the layout, the QTextLine returned will not be valid
+ (isValid() will return false).
+*/
+QTextLine QTextLayout::createLine()
+{
+#ifndef QT_NO_DEBUG
+ if (!d->layoutData || d->layoutData->layoutState == QTextEngine::LayoutEmpty) {
+ qWarning("QTextLayout::createLine: Called without layouting");
+ return QTextLine();
+ }
+#endif
+ if (d->layoutData->layoutState == QTextEngine::LayoutFailed)
+ return QTextLine();
+
+ int l = d->lines.size();
+ if (l && d->lines.at(l-1).length < 0) {
+ QTextLine(l-1, d).setNumColumns(INT_MAX);
+ }
+ int from = l > 0 ? d->lines.at(l-1).from + d->lines.at(l-1).length + d->lines.at(l-1).trailingSpaces : 0;
+ int strlen = d->layoutData->string.length();
+ if (l && from >= strlen) {
+ if (!d->lines.at(l-1).length || d->layoutData->string.at(strlen - 1) != QChar::LineSeparator)
+ return QTextLine();
+ }
+
+ QScriptLine line;
+ line.from = from;
+ line.length = -1;
+ line.justified = false;
+ line.gridfitted = false;
+
+ d->lines.append(line);
+ return QTextLine(l, d);
+}
+
+/*!
+ Returns the number of lines in this text layout.
+
+ \sa lineAt()
+*/
+int QTextLayout::lineCount() const
+{
+ return d->lines.size();
+}
+
+/*!
+ Returns the \a{i}-th line of text in this text layout.
+
+ \sa lineCount(), lineForTextPosition()
+*/
+QTextLine QTextLayout::lineAt(int i) const
+{
+ return i < lineCount() ? QTextLine(i, d) : QTextLine();
+}
+
+/*!
+ Returns the line that contains the cursor position specified by \a pos.
+
+ \sa isValidCursorPosition(), lineAt()
+*/
+QTextLine QTextLayout::lineForTextPosition(int pos) const
+{
+ int lineNum = d->lineNumberForTextPosition(pos);
+ return lineNum >= 0 ? lineAt(lineNum) : QTextLine();
+}
+
+/*!
+ \since 4.2
+
+ The global position of the layout. This is independent of the
+ bounding rectangle and of the layout process.
+
+ \sa setPosition()
+*/
+QPointF QTextLayout::position() const
+{
+ return d->position;
+}
+
+/*!
+ Moves the text layout to point \a p.
+
+ \sa position()
+*/
+void QTextLayout::setPosition(const QPointF &p)
+{
+ d->position = p;
+}
+
+/*!
+ The smallest rectangle that contains all the lines in the layout.
+*/
+QRectF QTextLayout::boundingRect() const
+{
+ if (d->lines.isEmpty())
+ return QRectF();
+
+ QFixed xmax, ymax;
+ QFixed xmin = d->lines.at(0).x;
+ QFixed ymin = d->lines.at(0).y;
+
+ for (int i = 0; i < d->lines.size(); ++i) {
+ const QScriptLine &si = d->lines[i];
+ xmin = qMin(xmin, si.x);
+ ymin = qMin(ymin, si.y);
+ QFixed lineWidth = si.width < QFIXED_MAX ? qMax(si.width, si.textWidth) : si.textWidth;
+ xmax = qMax(xmax, si.x+lineWidth);
+ // ### shouldn't the ascent be used in ymin???
+ ymax = qMax(ymax, si.y+si.height().ceil());
+ }
+ return QRectF(xmin.toReal(), ymin.toReal(), (xmax-xmin).toReal(), (ymax-ymin).toReal());
+}
+
+/*!
+ The minimum width the layout needs. This is the width of the
+ layout's smallest non-breakable substring.
+
+ \warning This function only returns a valid value after the layout
+ has been done.
+
+ \sa maximumWidth()
+*/
+qreal QTextLayout::minimumWidth() const
+{
+ return d->minWidth.toReal();
+}
+
+/*!
+ The maximum width the layout could expand to; this is essentially
+ the width of the entire text.
+
+ \warning This function only returns a valid value after the layout
+ has been done.
+
+ \sa minimumWidth()
+*/
+qreal QTextLayout::maximumWidth() const
+{
+ return d->maxWidth.toReal();
+}
+
+
+/*!
+ \internal
+*/
+void QTextLayout::setFlags(int flags)
+{
+ if (flags & Qt::TextJustificationForced) {
+ d->option.setAlignment(Qt::AlignJustify);
+ d->forceJustification = true;
+ }
+
+ if (flags & (Qt::TextForceLeftToRight|Qt::TextForceRightToLeft)) {
+ d->ignoreBidi = true;
+ d->option.setTextDirection((flags & Qt::TextForceLeftToRight) ? Qt::LeftToRight : Qt::RightToLeft);
+ }
+}
+
+static void addSelectedRegionsToPath(QTextEngine *eng, int lineNumber, const QPointF &pos, QTextLayout::FormatRange *selection,
+ QPainterPath *region, QRectF boundingRect)
+{
+ const QScriptLine &line = eng->lines[lineNumber];
+
+ QTextLineItemIterator iterator(eng, lineNumber, pos, selection);
+
+
+
+ const qreal selectionY = pos.y() + line.y.toReal();
+ const qreal lineHeight = line.height().toReal();
+
+ QFixed lastSelectionX = iterator.x;
+ QFixed lastSelectionWidth;
+
+ while (!iterator.atEnd()) {
+ iterator.next();
+
+ QFixed selectionX, selectionWidth;
+ if (iterator.getSelectionBounds(&selectionX, &selectionWidth)) {
+ if (selectionX == lastSelectionX + lastSelectionWidth) {
+ lastSelectionWidth += selectionWidth;
+ continue;
+ }
+
+ if (lastSelectionWidth > 0) {
+ QRectF rect = boundingRect & QRectF(lastSelectionX.toReal(), selectionY, lastSelectionWidth.toReal(), lineHeight);
+ rect.moveLeft(qFloor(rect.left()));
+ rect.moveTop(qFloor(rect.top()));
+ region->addRect(rect);
+ }
+
+ lastSelectionX = selectionX;
+ lastSelectionWidth = selectionWidth;
+ }
+ }
+ if (lastSelectionWidth > 0) {
+ QRectF rect = boundingRect & QRectF(lastSelectionX.toReal(), selectionY, lastSelectionWidth.toReal(), lineHeight);
+ rect.moveLeft(qFloor(rect.left()));
+ rect.moveTop(qFloor(rect.top()));
+ region->addRect(rect);
+ }
+}
+
+static inline QRectF clipIfValid(const QRectF &rect, const QRectF &clip)
+{
+ return clip.isValid() ? (rect & clip) : rect;
+}
+
+
+/*!
+ Returns the glyph indexes and positions for all glyphs corresponding to the \a length characters
+ starting at the position \a from in this QTextLayout. This is an expensive function, and should
+ not be called in a time sensitive context.
+
+ If \a from is less than zero, then the glyph run will begin at the first character in the
+ layout. If \a length is less than zero, it will span the entire string from the start position.
+
+ \since 4.8
+
+ \sa draw(), QPainter::drawGlyphRun()
+*/
+#if !defined(QT_NO_RAWFONT)
+QList QTextLayout::glyphRuns(int from, int length) const
+{
+ if (from < 0)
+ from = 0;
+ if (length < 0)
+ length = text().length();
+
+ QHash, QGlyphRun> glyphRunHash;
+ for (int i=0; ilines.size(); ++i) {
+ if (d->lines[i].from > from + length)
+ break;
+ else if (d->lines[i].from + d->lines[i].length >= from) {
+ QList glyphRuns = QTextLine(i, d).glyphRuns(from, length);
+
+ for (int j = 0; j < glyphRuns.size(); j++) {
+ const QGlyphRun &glyphRun = glyphRuns.at(j);
+ QRawFont rawFont = glyphRun.rawFont();
+
+ QFontEngine *fontEngine = rawFont.d->fontEngine;
+ QGlyphRun::GlyphRunFlags flags = glyphRun.flags();
+ QPair key(fontEngine, int(flags));
+ // merge the glyph runs using the same font
+ if (glyphRunHash.contains(key)) {
+ QGlyphRun &oldGlyphRun = glyphRunHash[key];
+
+ QVector indexes = oldGlyphRun.glyphIndexes();
+ QVector positions = oldGlyphRun.positions();
+
+ indexes += glyphRun.glyphIndexes();
+ positions += glyphRun.positions();
+
+ oldGlyphRun.setGlyphIndexes(indexes);
+ oldGlyphRun.setPositions(positions);
+ } else {
+ glyphRunHash[key] = glyphRun;
+ }
+ }
+ }
+ }
+
+ return glyphRunHash.values();
+}
+#endif // QT_NO_RAWFONT
+
+/*!
+ Draws the whole layout on the painter \a p at the position specified by \a pos.
+ The rendered layout includes the given \a selections and is clipped within
+ the rectangle specified by \a clip.
+*/
+void QTextLayout::draw(QPainter *p, const QPointF &pos, const QVector &selections, const QRectF &clip) const
+{
+ if (d->lines.isEmpty())
+ return;
+
+ if (!d->layoutData)
+ d->itemize();
+
+ QPointF position = pos + d->position;
+
+ QFixed clipy = (INT_MIN/256);
+ QFixed clipe = (INT_MAX/256);
+ if (clip.isValid()) {
+ clipy = QFixed::fromReal(clip.y() - position.y());
+ clipe = clipy + QFixed::fromReal(clip.height());
+ }
+
+ int firstLine = 0;
+ int lastLine = d->lines.size();
+ for (int i = 0; i < d->lines.size(); ++i) {
+ QTextLine l(i, d);
+ const QScriptLine &sl = d->lines[i];
+
+ if (sl.y > clipe) {
+ lastLine = i;
+ break;
+ }
+ if ((sl.y + sl.height()) < clipy) {
+ firstLine = i;
+ continue;
+ }
+ }
+
+ QPainterPath excludedRegion;
+ QPainterPath textDoneRegion;
+ for (int i = 0; i < selections.size(); ++i) {
+ FormatRange selection = selections.at(i);
+ const QBrush bg = selection.format.background();
+
+ QPainterPath region;
+ region.setFillRule(Qt::WindingFill);
+
+ for (int line = firstLine; line < lastLine; ++line) {
+ const QScriptLine &sl = d->lines[line];
+ QTextLine tl(line, d);
+
+ QRectF lineRect(tl.naturalTextRect());
+ lineRect.translate(position);
+ lineRect.adjust(0, 0, d->leadingSpaceWidth(sl).toReal(), 0);
+
+ bool isLastLineInBlock = (line == d->lines.size()-1);
+ int sl_length = sl.length + (isLastLineInBlock? 1 : 0); // the infamous newline
+
+
+ if (sl.from > selection.start + selection.length || sl.from + sl_length <= selection.start)
+ continue; // no actual intersection
+
+ const bool selectionStartInLine = sl.from <= selection.start;
+ const bool selectionEndInLine = selection.start + selection.length < sl.from + sl_length;
+
+ if (sl.length && (selectionStartInLine || selectionEndInLine)) {
+ addSelectedRegionsToPath(d, line, position, &selection, ®ion, clipIfValid(lineRect, clip));
+ } else {
+ region.addRect(clipIfValid(lineRect, clip));
+ }
+
+ if (selection.format.boolProperty(QTextFormat::FullWidthSelection)) {
+ QRectF fullLineRect(tl.rect());
+ fullLineRect.translate(position);
+ fullLineRect.setRight(QFIXED_MAX);
+ if (!selectionEndInLine)
+ region.addRect(clipIfValid(QRectF(lineRect.topRight(), fullLineRect.bottomRight()), clip));
+ if (!selectionStartInLine)
+ region.addRect(clipIfValid(QRectF(fullLineRect.topLeft(), lineRect.bottomLeft()), clip));
+ } else if (!selectionEndInLine
+ && isLastLineInBlock
+ &&!(d->option.flags() & QTextOption::ShowLineAndParagraphSeparators)) {
+ region.addRect(clipIfValid(QRectF(lineRect.right(), lineRect.top(),
+ lineRect.height()/4, lineRect.height()), clip));
+ }
+
+ }
+ {
+ const QPen oldPen = p->pen();
+ const QBrush oldBrush = p->brush();
+
+ p->setPen(selection.format.penProperty(QTextFormat::OutlinePen));
+ p->setBrush(selection.format.brushProperty(QTextFormat::BackgroundBrush));
+ p->drawPath(region);
+
+ p->setPen(oldPen);
+ p->setBrush(oldBrush);
+ }
+
+
+
+ bool hasText = (selection.format.foreground().style() != Qt::NoBrush);
+ bool hasBackground= (selection.format.background().style() != Qt::NoBrush);
+
+ if (hasBackground) {
+ selection.format.setProperty(ObjectSelectionBrush, selection.format.property(QTextFormat::BackgroundBrush));
+ // don't just clear the property, set an empty brush that overrides a potential
+ // background brush specified in the text
+ selection.format.setProperty(QTextFormat::BackgroundBrush, QBrush());
+ selection.format.clearProperty(QTextFormat::OutlinePen);
+ }
+
+ selection.format.setProperty(SuppressText, !hasText);
+
+ if (hasText && !hasBackground && !(textDoneRegion & region).isEmpty())
+ continue;
+
+ p->save();
+ p->setClipPath(region, Qt::IntersectClip);
+
+ for (int line = firstLine; line < lastLine; ++line) {
+ QTextLine l(line, d);
+ l.draw(p, position, &selection);
+ }
+ p->restore();
+
+ if (hasText) {
+ textDoneRegion += region;
+ } else {
+ if (hasBackground)
+ textDoneRegion -= region;
+ }
+
+ excludedRegion += region;
+ }
+
+ QPainterPath needsTextButNoBackground = excludedRegion - textDoneRegion;
+ if (!needsTextButNoBackground.isEmpty()){
+ p->save();
+ p->setClipPath(needsTextButNoBackground, Qt::IntersectClip);
+ FormatRange selection;
+ selection.start = 0;
+ selection.length = INT_MAX;
+ selection.format.setProperty(SuppressBackground, true);
+ for (int line = firstLine; line < lastLine; ++line) {
+ QTextLine l(line, d);
+ l.draw(p, position, &selection);
+ }
+ p->restore();
+ }
+
+ if (!excludedRegion.isEmpty()) {
+ p->save();
+ QPainterPath path;
+ QRectF br = boundingRect().translated(position);
+ br.setRight(QFIXED_MAX);
+ if (!clip.isNull())
+ br = br.intersected(clip);
+ path.addRect(br);
+ path -= excludedRegion;
+ p->setClipPath(path, Qt::IntersectClip);
+ }
+
+ for (int i = firstLine; i < lastLine; ++i) {
+ QTextLine l(i, d);
+ l.draw(p, position);
+ }
+ if (!excludedRegion.isEmpty())
+ p->restore();
+
+
+ if (!d->cacheGlyphs)
+ d->freeMemory();
+}
+
+/*!
+ \fn void QTextLayout::drawCursor(QPainter *painter, const QPointF &position, int cursorPosition) const
+ \overload
+
+ Draws a text cursor with the current pen at the given \a position using the
+ \a painter specified.
+ The corresponding position within the text is specified by \a cursorPosition.
+*/
+void QTextLayout::drawCursor(QPainter *p, const QPointF &pos, int cursorPosition) const
+{
+ drawCursor(p, pos, cursorPosition, 1);
+}
+
+/*!
+ \fn void QTextLayout::drawCursor(QPainter *painter, const QPointF &position, int cursorPosition, int width) const
+
+ Draws a text cursor with the current pen and the specified \a width at the given \a position using the
+ \a painter specified.
+ The corresponding position within the text is specified by \a cursorPosition.
+*/
+void QTextLayout::drawCursor(QPainter *p, const QPointF &pos, int cursorPosition, int width) const
+{
+ if (d->lines.isEmpty())
+ return;
+
+ if (!d->layoutData)
+ d->itemize();
+
+ QPointF position = pos + d->position;
+
+ cursorPosition = qBound(0, cursorPosition, d->layoutData->string.length());
+ int line = d->lineNumberForTextPosition(cursorPosition);
+ if (line < 0)
+ line = 0;
+ if (line >= d->lines.size())
+ return;
+
+ QTextLine l(line, d);
+ const QScriptLine &sl = d->lines[line];
+
+ qreal x = position.x() + l.cursorToX(cursorPosition);
+
+ int itm;
+
+ if (d->visualCursorMovement()) {
+ if (cursorPosition == sl.from + sl.length)
+ cursorPosition--;
+ itm = d->findItem(cursorPosition);
+ } else
+ itm = d->findItem(cursorPosition - 1);
+
+ QFixed base = sl.base();
+ QFixed descent = sl.descent;
+ bool rightToLeft = d->isRightToLeft();
+ if (itm >= 0) {
+ const QScriptItem &si = d->layoutData->items.at(itm);
+ if (si.ascent > 0)
+ base = si.ascent;
+ if (si.descent > 0)
+ descent = si.descent;
+ rightToLeft = si.analysis.bidiLevel % 2;
+ }
+ qreal y = position.y() + (sl.y + sl.base() - base).toReal();
+ bool toggleAntialiasing = !(p->renderHints() & QPainter::Antialiasing)
+ && (p->transform().type() > QTransform::TxTranslate);
+ if (toggleAntialiasing)
+ p->setRenderHint(QPainter::Antialiasing);
+ p->fillRect(QRectF(x, y, qreal(width), (base + descent).toReal()), p->pen().brush());
+ if (toggleAntialiasing)
+ p->setRenderHint(QPainter::Antialiasing, false);
+ if (d->layoutData->hasBidi) {
+ const int arrow_extent = 4;
+ int sign = rightToLeft ? -1 : 1;
+ p->drawLine(QLineF(x, y, x + (sign * arrow_extent/2), y + arrow_extent/2));
+ p->drawLine(QLineF(x, y+arrow_extent, x + (sign * arrow_extent/2), y + arrow_extent/2));
+ }
+ return;
+}
+
+/*!
+ \class QTextLine
+ \reentrant
+
+ \brief The QTextLine class represents a line of text inside a QTextLayout.
+ \inmodule QtGui
+
+ \ingroup richtext-processing
+
+ A text line is usually created by QTextLayout::createLine().
+
+ After being created, the line can be filled using the setLineWidth()
+ or setNumColumns() functions. A line has a number of attributes including the
+ rectangle it occupies, rect(), its coordinates, x() and y(), its
+ textLength(), width() and naturalTextWidth(), and its ascent() and descent()
+ relative to the text. The position of the cursor in terms of the
+ line is available from cursorToX() and its inverse from
+ xToCursor(). A line can be moved with setPosition().
+*/
+
+/*!
+ \enum QTextLine::Edge
+
+ \value Leading
+ \value Trailing
+*/
+
+/*!
+ \enum QTextLine::CursorPosition
+
+ \value CursorBetweenCharacters
+ \value CursorOnCharacter
+*/
+
+/*!
+ \fn QTextLine::QTextLine(int line, QTextEngine *e)
+ \internal
+
+ Constructs a new text line using the line at position \a line in
+ the text engine \a e.
+*/
+
+/*!
+ \fn QTextLine::QTextLine()
+
+ Creates an invalid line.
+*/
+
+/*!
+ \fn bool QTextLine::isValid() const
+
+ Returns \c true if this text line is valid; otherwise returns \c false.
+*/
+
+/*!
+ \fn int QTextLine::lineNumber() const
+
+ Returns the position of the line in the text engine.
+*/
+
+
+/*!
+ Returns the line's bounding rectangle.
+
+ \sa x(), y(), textLength(), width()
+*/
+QRectF QTextLine::rect() const
+{
+ const QScriptLine& sl = eng->lines[index];
+ return QRectF(sl.x.toReal(), sl.y.toReal(), sl.width.toReal(), sl.height().toReal());
+}
+
+/*!
+ Returns the rectangle covered by the line.
+*/
+QRectF QTextLine::naturalTextRect() const
+{
+ const QScriptLine& sl = eng->lines[index];
+ QFixed x = sl.x + eng->alignLine(sl);
+
+ QFixed width = sl.textWidth;
+ if (sl.justified)
+ width = sl.width;
+
+ return QRectF(x.toReal(), sl.y.toReal(), width.toReal(), sl.height().toReal());
+}
+
+/*!
+ Returns the line's x position.
+
+ \sa rect(), y(), textLength(), width()
+*/
+qreal QTextLine::x() const
+{
+ return eng->lines[index].x.toReal();
+}
+
+/*!
+ Returns the line's y position.
+
+ \sa x(), rect(), textLength(), width()
+*/
+qreal QTextLine::y() const
+{
+ return eng->lines[index].y.toReal();
+}
+
+/*!
+ Returns the line's width as specified by the layout() function.
+
+ \sa naturalTextWidth(), x(), y(), textLength(), rect()
+*/
+qreal QTextLine::width() const
+{
+ return eng->lines[index].width.toReal();
+}
+
+
+/*!
+ Returns the line's ascent.
+
+ \sa descent(), height()
+*/
+qreal QTextLine::ascent() const
+{
+ return eng->lines[index].ascent.toReal();
+}
+
+/*!
+ Returns the line's descent.
+
+ \sa ascent(), height()
+*/
+qreal QTextLine::descent() const
+{
+ return eng->lines[index].descent.toReal();
+}
+
+/*!
+ Returns the line's height. This is equal to ascent() + descent()
+ if leading is not included. If leading is included, this equals to
+ ascent() + descent() + leading().
+
+ \sa ascent(), descent(), leading(), setLeadingIncluded()
+*/
+qreal QTextLine::height() const
+{
+ return eng->lines[index].height().ceil().toReal();
+}
+
+/*!
+ \since 4.6
+
+ Returns the line's leading.
+
+ \sa ascent(), descent(), height()
+*/
+qreal QTextLine::leading() const
+{
+ return eng->lines[index].leading.toReal();
+}
+
+/*!
+ \since 4.6
+
+ Includes positive leading into the line's height if \a included is true;
+ otherwise does not include leading.
+
+ By default, leading is not included.
+
+ Note that negative leading is ignored, it must be handled
+ in the code using the text lines by letting the lines overlap.
+
+ \sa leadingIncluded()
+
+*/
+void QTextLine::setLeadingIncluded(bool included)
+{
+ eng->lines[index].leadingIncluded= included;
+
+}
+
+/*!
+ \since 4.6
+
+ Returns \c true if positive leading is included into the line's height;
+ otherwise returns \c false.
+
+ By default, leading is not included.
+
+ \sa setLeadingIncluded()
+*/
+bool QTextLine::leadingIncluded() const
+{
+ return eng->lines[index].leadingIncluded;
+}
+
+/*!
+ Returns the width of the line that is occupied by text. This is
+ always \<= to width(), and is the minimum width that could be used
+ by layout() without changing the line break position.
+*/
+qreal QTextLine::naturalTextWidth() const
+{
+ return eng->lines[index].textWidth.toReal();
+}
+
+/*!
+ \since 4.7
+ Returns the horizontal advance of the text. The advance of the text
+ is the distance from its position to the next position at which
+ text would naturally be drawn.
+
+ By adding the advance to the position of the text line and using this
+ as the position of a second text line, you will be able to position
+ the two lines side-by-side without gaps in-between.
+*/
+qreal QTextLine::horizontalAdvance() const
+{
+ return eng->lines[index].textAdvance.toReal();
+}
+
+/*!
+ Lays out the line with the given \a width. The line is filled from
+ its starting position with as many characters as will fit into
+ the line. In case the text cannot be split at the end of the line,
+ it will be filled with additional characters to the next whitespace
+ or end of the text.
+*/
+void QTextLine::setLineWidth(qreal width)
+{
+ QScriptLine &line = eng->lines[index];
+ if (!eng->layoutData) {
+ qWarning("QTextLine: Can't set a line width while not layouting.");
+ return;
+ }
+
+ if (width > QFIXED_MAX)
+ width = QFIXED_MAX;
+
+ line.width = QFixed::fromReal(width);
+ if (line.length
+ && line.textWidth <= line.width
+ && line.from + line.length == eng->layoutData->string.length())
+ // no need to do anything if the line is already layouted and the last one. This optimization helps
+ // when using things in a single line layout.
+ return;
+ line.length = 0;
+ line.textWidth = 0;
+
+ layout_helper(INT_MAX);
+}
+
+/*!
+ Lays out the line. The line is filled from its starting position
+ with as many characters as are specified by \a numColumns. In case
+ the text cannot be split until \a numColumns characters, the line
+ will be filled with as many characters to the next whitespace or
+ end of the text.
+*/
+void QTextLine::setNumColumns(int numColumns)
+{
+ QScriptLine &line = eng->lines[index];
+ line.width = QFIXED_MAX;
+ line.length = 0;
+ line.textWidth = 0;
+ layout_helper(numColumns);
+}
+
+/*!
+ Lays out the line. The line is filled from its starting position
+ with as many characters as are specified by \a numColumns. In case
+ the text cannot be split until \a numColumns characters, the line
+ will be filled with as many characters to the next whitespace or
+ end of the text. The provided \a alignmentWidth is used as reference
+ width for alignment.
+*/
+void QTextLine::setNumColumns(int numColumns, qreal alignmentWidth)
+{
+ QScriptLine &line = eng->lines[index];
+ line.width = QFixed::fromReal(alignmentWidth);
+ line.length = 0;
+ line.textWidth = 0;
+ layout_helper(numColumns);
+}
+
+#if 0
+#define LB_DEBUG qDebug
+#else
+#define LB_DEBUG if (0) qDebug
+#endif
+
+namespace {
+
+ struct LineBreakHelper
+ {
+ LineBreakHelper()
+ : glyphCount(0), maxGlyphs(0), currentPosition(0), fontEngine(0), logClusters(0),
+ manualWrap(false), whiteSpaceOrObject(true)
+ {
+ }
+
+
+ QScriptLine tmpData;
+ QScriptLine spaceData;
+
+ QGlyphLayout glyphs;
+
+ int glyphCount;
+ int maxGlyphs;
+ int currentPosition;
+ glyph_t previousGlyph;
+
+ QFixed minw;
+ QFixed softHyphenWidth;
+ QFixed rightBearing;
+ QFixed minimumRightBearing;
+
+ QFontEngine *fontEngine;
+ const unsigned short *logClusters;
+
+ bool manualWrap;
+ bool whiteSpaceOrObject;
+
+ bool checkFullOtherwiseExtend(QScriptLine &line);
+
+ QFixed calculateNewWidth(const QScriptLine &line) const {
+ return line.textWidth + tmpData.textWidth + spaceData.textWidth + softHyphenWidth
+ - qMin(rightBearing, QFixed());
+ }
+
+ inline glyph_t currentGlyph() const
+ {
+ Q_ASSERT(currentPosition > 0);
+ Q_ASSERT(logClusters[currentPosition - 1] < glyphs.numGlyphs);
+
+ return glyphs.glyphs[logClusters[currentPosition - 1]];
+ }
+
+ inline void saveCurrentGlyph()
+ {
+ previousGlyph = 0;
+ if (currentPosition > 0 &&
+ logClusters[currentPosition - 1] < glyphs.numGlyphs) {
+ previousGlyph = currentGlyph(); // needed to calculate right bearing later
+ }
+ }
+
+ inline void adjustRightBearing(glyph_t glyph)
+ {
+ qreal rb;
+ fontEngine->getGlyphBearings(glyph, 0, &rb);
+ rightBearing = qMin(QFixed(), QFixed::fromReal(rb));
+ }
+
+ inline void adjustRightBearing()
+ {
+ if (currentPosition <= 0)
+ return;
+ adjustRightBearing(currentGlyph());
+ }
+
+ inline void adjustPreviousRightBearing()
+ {
+ if (previousGlyph > 0)
+ adjustRightBearing(previousGlyph);
+ }
+
+ inline void resetRightBearing()
+ {
+ rightBearing = QFixed(1); // Any positive number is defined as invalid since only
+ // negative right bearings are interesting to us.
+ }
+ };
+
+inline bool LineBreakHelper::checkFullOtherwiseExtend(QScriptLine &line)
+{
+ LB_DEBUG("possible break width %f, spacew=%f", tmpData.textWidth.toReal(), spaceData.textWidth.toReal());
+
+ QFixed newWidth = calculateNewWidth(line);
+ if (line.length && !manualWrap && (newWidth > line.width || glyphCount > maxGlyphs))
+ return true;
+
+ minw = qMax(minw, tmpData.textWidth);
+ line += tmpData;
+ line.textWidth += spaceData.textWidth;
+
+ line.length += spaceData.length;
+ tmpData.textWidth = 0;
+ tmpData.length = 0;
+ spaceData.textWidth = 0;
+ spaceData.length = 0;
+
+ return false;
+}
+
+} // anonymous namespace
+
+
+static inline void addNextCluster(int &pos, int end, QScriptLine &line, int &glyphCount,
+ const QScriptItem ¤t, const unsigned short *logClusters,
+ const QGlyphLayout &glyphs)
+{
+ int glyphPosition = logClusters[pos];
+ do { // got to the first next cluster
+ ++pos;
+ ++line.length;
+ } while (pos < end && logClusters[pos] == glyphPosition);
+ do { // calculate the textWidth for the rest of the current cluster.
+ if (!glyphs.attributes[glyphPosition].dontPrint)
+ line.textWidth += glyphs.advances[glyphPosition];
+ ++glyphPosition;
+ } while (glyphPosition < current.num_glyphs && !glyphs.attributes[glyphPosition].clusterStart);
+
+ Q_ASSERT((pos == end && glyphPosition == current.num_glyphs) || logClusters[pos] == glyphPosition);
+
+ ++glyphCount;
+}
+
+
+// fill QScriptLine
+void QTextLine::layout_helper(int maxGlyphs)
+{
+ QScriptLine &line = eng->lines[index];
+ line.length = 0;
+ line.trailingSpaces = 0;
+ line.textWidth = 0;
+ line.hasTrailingSpaces = false;
+
+ if (!eng->layoutData->items.size() || line.from >= eng->layoutData->string.length()) {
+ line.setDefaultHeight(eng);
+ return;
+ }
+
+ Q_ASSERT(line.from < eng->layoutData->string.length());
+
+ LineBreakHelper lbh;
+
+ lbh.maxGlyphs = maxGlyphs;
+
+ QTextOption::WrapMode wrapMode = eng->option.wrapMode();
+ bool breakany = (wrapMode == QTextOption::WrapAnywhere);
+ lbh.manualWrap = (wrapMode == QTextOption::ManualWrap || wrapMode == QTextOption::NoWrap);
+
+ int item = -1;
+ int newItem = eng->findItem(line.from);
+ Q_ASSERT(newItem >= 0);
+
+ LB_DEBUG("from: %d: item=%d, total %d, width available %f", line.from, newItem, eng->layoutData->items.size(), line.width.toReal());
+
+ Qt::Alignment alignment = eng->option.alignment();
+
+ const QCharAttributes *attributes = eng->attributes();
+ if (!attributes)
+ return;
+ lbh.currentPosition = line.from;
+ int end = 0;
+ lbh.logClusters = eng->layoutData->logClustersPtr;
+ lbh.previousGlyph = 0;
+
+ while (newItem < eng->layoutData->items.size()) {
+ lbh.resetRightBearing();
+ lbh.softHyphenWidth = 0;
+ if (newItem != item) {
+ item = newItem;
+ const QScriptItem ¤t = eng->layoutData->items[item];
+ if (!current.num_glyphs) {
+ eng->shape(item);
+ attributes = eng->attributes();
+ if (!attributes)
+ return;
+ lbh.logClusters = eng->layoutData->logClustersPtr;
+ }
+ lbh.currentPosition = qMax(line.from, current.position);
+ end = current.position + eng->length(item);
+ lbh.glyphs = eng->shapedGlyphs(¤t);
+ QFontEngine *fontEngine = eng->fontEngine(current);
+ if (lbh.fontEngine != fontEngine) {
+ lbh.fontEngine = fontEngine;
+ lbh.minimumRightBearing = qMin(QFixed(),
+ QFixed::fromReal(fontEngine->minRightBearing()));
+ }
+ }
+ const QScriptItem ¤t = eng->layoutData->items[item];
+
+ lbh.tmpData.leading = qMax(lbh.tmpData.leading + lbh.tmpData.ascent,
+ current.leading + current.ascent) - qMax(lbh.tmpData.ascent,
+ current.ascent);
+ lbh.tmpData.ascent = qMax(lbh.tmpData.ascent, current.ascent);
+ lbh.tmpData.descent = qMax(lbh.tmpData.descent, current.descent);
+
+ if (current.analysis.flags == QScriptAnalysis::Tab && (alignment & (Qt::AlignLeft | Qt::AlignRight | Qt::AlignCenter | Qt::AlignJustify))) {
+ lbh.whiteSpaceOrObject = true;
+ if (lbh.checkFullOtherwiseExtend(line))
+ goto found;
+
+ QFixed x = line.x + line.textWidth + lbh.tmpData.textWidth + lbh.spaceData.textWidth;
+ QFixed tabWidth = eng->calculateTabWidth(item, x);
+
+ lbh.spaceData.textWidth += tabWidth;
+ lbh.spaceData.length++;
+ newItem = item + 1;
+
+ QFixed averageCharWidth = eng->fontEngine(current)->averageCharWidth();
+ lbh.glyphCount += qRound(tabWidth / averageCharWidth);
+
+ if (lbh.checkFullOtherwiseExtend(line))
+ goto found;
+ } else if (current.analysis.flags == QScriptAnalysis::LineOrParagraphSeparator) {
+ lbh.whiteSpaceOrObject = true;
+ // if the line consists only of the line separator make sure
+ // we have a sane height
+ if (!line.length && !lbh.tmpData.length)
+ line.setDefaultHeight(eng);
+ if (eng->option.flags() & QTextOption::ShowLineAndParagraphSeparators) {
+ if (lbh.checkFullOtherwiseExtend(line))
+ goto found;
+
+ addNextCluster(lbh.currentPosition, end, lbh.tmpData, lbh.glyphCount,
+ current, lbh.logClusters, lbh.glyphs);
+ } else {
+ lbh.tmpData.length++;
+ lbh.adjustPreviousRightBearing();
+ }
+ line += lbh.tmpData;
+ goto found;
+ } else if (current.analysis.flags == QScriptAnalysis::Object) {
+ lbh.whiteSpaceOrObject = true;
+ lbh.tmpData.length++;
+
+ if (eng->block.docHandle()) {
+ QTextInlineObject inlineObject(item, eng);
+ eng->docLayout()->positionInlineObject(inlineObject, eng->block.position() + current.position, inlineObject.format());
+ }
+
+ lbh.tmpData.textWidth += current.width;
+
+ newItem = item + 1;
+ ++lbh.glyphCount;
+ if (lbh.checkFullOtherwiseExtend(line))
+ goto found;
+ } else if (attributes[lbh.currentPosition].whiteSpace) {
+ lbh.whiteSpaceOrObject = true;
+ while (lbh.currentPosition < end && attributes[lbh.currentPosition].whiteSpace)
+ addNextCluster(lbh.currentPosition, end, lbh.spaceData, lbh.glyphCount,
+ current, lbh.logClusters, lbh.glyphs);
+
+ if (!lbh.manualWrap && lbh.spaceData.textWidth > line.width) {
+ lbh.spaceData.textWidth = line.width; // ignore spaces that fall out of the line.
+ goto found;
+ }
+ } else {
+ lbh.whiteSpaceOrObject = false;
+ bool sb_or_ws = false;
+ lbh.saveCurrentGlyph();
+ do {
+ addNextCluster(lbh.currentPosition, end, lbh.tmpData, lbh.glyphCount,
+ current, lbh.logClusters, lbh.glyphs);
+
+ // This is a hack to fix a regression caused by the introduction of the
+ // whitespace flag to non-breakable spaces and will cause the non-breakable
+ // spaces to behave as in previous Qt versions in the line breaking algorithm.
+ // The line breaks do not currently follow the Unicode specs, but fixing this would
+ // require refactoring the code and would cause behavioral regressions.
+ bool isBreakableSpace = lbh.currentPosition < eng->layoutData->string.length()
+ && attributes[lbh.currentPosition].whiteSpace
+ && eng->layoutData->string.at(lbh.currentPosition).decompositionTag() != QChar::NoBreak;
+
+ if (lbh.currentPosition >= eng->layoutData->string.length()
+ || isBreakableSpace
+ || attributes[lbh.currentPosition].lineBreak) {
+ sb_or_ws = true;
+ break;
+ } else if (breakany && attributes[lbh.currentPosition].graphemeBoundary) {
+ break;
+ }
+ } while (lbh.currentPosition < end);
+ lbh.minw = qMax(lbh.tmpData.textWidth, lbh.minw);
+
+ if (lbh.currentPosition > 0 && lbh.currentPosition < end
+ && attributes[lbh.currentPosition].lineBreak
+ && eng->layoutData->string.at(lbh.currentPosition - 1).unicode() == QChar::SoftHyphen) {
+ // if we are splitting up a word because of
+ // a soft hyphen then we ...
+ //
+ // a) have to take the width of the soft hyphen into
+ // account to see if the first syllable(s) /and/
+ // the soft hyphen fit into the line
+ //
+ // b) if we are so short of available width that the
+ // soft hyphen is the first breakable position, then
+ // we don't want to show it. However we initially
+ // have to take the width for it into account so that
+ // the text document layout sees the overflow and
+ // switch to break-anywhere mode, in which we
+ // want the soft-hyphen to slip into the next line
+ // and thus become invisible again.
+ //
+ if (line.length)
+ lbh.softHyphenWidth = lbh.glyphs.advances[lbh.logClusters[lbh.currentPosition - 1]];
+ else if (breakany)
+ lbh.tmpData.textWidth += lbh.glyphs.advances[lbh.logClusters[lbh.currentPosition - 1]];
+ }
+
+ // The actual width of the text needs to take the right bearing into account. The
+ // right bearing is left-ward, which means that if the rightmost pixel is to the right
+ // of the advance of the glyph, the bearing will be negative. We flip the sign
+ // for the code to be more readable. Logic borrowed from qfontmetrics.cpp.
+ // We ignore the right bearing if the minimum negative bearing is too little to
+ // expand the text beyond the edge.
+ if (sb_or_ws|breakany) {
+ QFixed rightBearing = lbh.rightBearing; // store previous right bearing
+ if (lbh.calculateNewWidth(line) - lbh.minimumRightBearing > line.width)
+ lbh.adjustRightBearing();
+ if (lbh.checkFullOtherwiseExtend(line)) {
+ // we are too wide, fix right bearing
+ if (rightBearing <= 0)
+ lbh.rightBearing = rightBearing; // take from cache
+ else
+ lbh.adjustPreviousRightBearing();
+
+ if (!breakany) {
+ line.textWidth += lbh.softHyphenWidth;
+ }
+
+ goto found;
+ }
+ }
+ lbh.saveCurrentGlyph();
+ }
+ if (lbh.currentPosition == end)
+ newItem = item + 1;
+ }
+ LB_DEBUG("reached end of line");
+ lbh.checkFullOtherwiseExtend(line);
+found:
+ if (lbh.rightBearing > 0 && !lbh.whiteSpaceOrObject) // If right bearing has not yet been adjusted
+ lbh.adjustRightBearing();
+ line.textAdvance = line.textWidth;
+ line.textWidth -= qMin(QFixed(), lbh.rightBearing);
+
+ if (line.length == 0) {
+ LB_DEBUG("no break available in line, adding temp: length %d, width %f, space: length %d, width %f",
+ lbh.tmpData.length, lbh.tmpData.textWidth.toReal(),
+ lbh.spaceData.length, lbh.spaceData.textWidth.toReal());
+ line += lbh.tmpData;
+ }
+
+ LB_DEBUG("line length = %d, ascent=%f, descent=%f, textWidth=%f (spacew=%f)", line.length, line.ascent.toReal(),
+ line.descent.toReal(), line.textWidth.toReal(), lbh.spaceData.width.toReal());
+ LB_DEBUG(" : '%s'", eng->layoutData->string.mid(line.from, line.length).toUtf8().data());
+
+ if (lbh.manualWrap) {
+ eng->minWidth = qMax(eng->minWidth, line.textWidth);
+ eng->maxWidth = qMax(eng->maxWidth, line.textWidth);
+ } else {
+ eng->minWidth = qMax(eng->minWidth, lbh.minw);
+ eng->maxWidth += line.textWidth;
+ }
+
+ if (line.textWidth > 0 && item < eng->layoutData->items.size())
+ eng->maxWidth += lbh.spaceData.textWidth;
+ if (eng->option.flags() & QTextOption::IncludeTrailingSpaces)
+ line.textWidth += lbh.spaceData.textWidth;
+ if (lbh.spaceData.length) {
+ line.trailingSpaces = lbh.spaceData.length;
+ line.hasTrailingSpaces = true;
+ }
+
+ line.justified = false;
+ line.gridfitted = false;
+
+ if (eng->option.wrapMode() == QTextOption::WrapAtWordBoundaryOrAnywhere) {
+ if ((lbh.maxGlyphs != INT_MAX && lbh.glyphCount > lbh.maxGlyphs)
+ || (lbh.maxGlyphs == INT_MAX && line.textWidth > line.width)) {
+
+ eng->option.setWrapMode(QTextOption::WrapAnywhere);
+ line.length = 0;
+ line.textWidth = 0;
+ layout_helper(lbh.maxGlyphs);
+ eng->option.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
+ }
+ }
+}
+
+/*!
+ Moves the line to position \a pos.
+*/
+void QTextLine::setPosition(const QPointF &pos)
+{
+ eng->lines[index].x = QFixed::fromReal(pos.x());
+ eng->lines[index].y = QFixed::fromReal(pos.y());
+}
+
+/*!
+ Returns the line's position relative to the text layout's position.
+*/
+QPointF QTextLine::position() const
+{
+ return QPointF(eng->lines[index].x.toReal(), eng->lines[index].y.toReal());
+}
+
+// ### DOC: I have no idea what this means/does.
+// You create a text layout with a string of text. Once you laid
+// it out, it contains a number of QTextLines. from() returns the position
+// inside the text string where this line starts. If you e.g. has a
+// text of "This is a string", laid out into two lines (the second
+// starting at the word 'a'), layout.lineAt(0).from() == 0 and
+// layout.lineAt(1).from() == 8.
+/*!
+ Returns the start of the line from the beginning of the string
+ passed to the QTextLayout.
+*/
+int QTextLine::textStart() const
+{
+ return eng->lines[index].from;
+}
+
+/*!
+ Returns the length of the text in the line.
+
+ \sa naturalTextWidth()
+*/
+int QTextLine::textLength() const
+{
+ if (eng->option.flags() & QTextOption::ShowLineAndParagraphSeparators
+ && eng->block.isValid() && index == eng->lines.count()-1) {
+ return eng->lines[index].length - 1;
+ }
+ return eng->lines[index].length + eng->lines[index].trailingSpaces;
+}
+
+static void setPenAndDrawBackground(QPainter *p, const QPen &defaultPen, const QTextCharFormat &chf, const QRectF &r)
+{
+ QBrush c = chf.foreground();
+ if (c.style() == Qt::NoBrush) {
+ p->setPen(defaultPen);
+ }
+
+ QBrush bg = chf.background();
+ if (bg.style() != Qt::NoBrush && !chf.property(SuppressBackground).toBool())
+ p->fillRect(QRectF(qFloor(r.x()), qFloor(r.y()), r.width(), r.height()), bg);
+ if (c.style() != Qt::NoBrush) {
+ p->setPen(QPen(c, 0));
+ }
+
+}
+
+#if !defined(QT_NO_RAWFONT)
+static QGlyphRun glyphRunWithInfo(QFontEngine *fontEngine,
+ const QGlyphLayout &glyphLayout,
+ const QPointF &pos,
+ const QGlyphRun::GlyphRunFlags &flags,
+ const QFixed &selectionX,
+ const QFixed &selectionWidth,
+ int glyphsStart,
+ int glyphsEnd,
+ unsigned short *logClusters,
+ int textPosition,
+ int textLength)
+{
+ Q_ASSERT(logClusters != 0);
+
+ QGlyphRun glyphRun;
+
+ QGlyphRunPrivate *d = QGlyphRunPrivate::get(glyphRun);
+
+ int rangeStart = textPosition;
+ while (*logClusters != glyphsStart && rangeStart < textPosition + textLength) {
+ ++logClusters;
+ ++rangeStart;
+ }
+
+ int rangeEnd = rangeStart;
+ while (*logClusters != glyphsEnd && rangeEnd < textPosition + textLength) {
+ ++logClusters;
+ ++rangeEnd;
+ }
+
+ d->textRangeStart = rangeStart;
+ d->textRangeEnd = rangeEnd;
+
+ // Make a font for this particular engine
+ QRawFont font;
+ QRawFontPrivate *fontD = QRawFontPrivate::get(font);
+ fontD->fontEngine = fontEngine;
+ fontD->thread = QThread::currentThread();
+ fontD->fontEngine->ref.ref();
+ QVarLengthArray glyphsArray;
+ QVarLengthArray positionsArray;
+
+ QTextItem::RenderFlags renderFlags;
+ if (flags.testFlag(QGlyphRun::Overline))
+ renderFlags |= QTextItem::Overline;
+ if (flags.testFlag(QGlyphRun::Underline))
+ renderFlags |= QTextItem::Underline;
+ if (flags.testFlag(QGlyphRun::StrikeOut))
+ renderFlags |= QTextItem::StrikeOut;
+ if (flags.testFlag(QGlyphRun::RightToLeft))
+ renderFlags |= QTextItem::RightToLeft;
+
+ fontEngine->getGlyphPositions(glyphLayout, QTransform(), renderFlags, glyphsArray,
+ positionsArray);
+ Q_ASSERT(glyphsArray.size() == positionsArray.size());
+
+ qreal fontHeight = font.ascent() + font.descent();
+ qreal minY = 0;
+ qreal maxY = 0;
+ QVector glyphs;
+ QVector positions;
+ for (int i=0; i QTextLine::glyphRuns(int from, int length) const
+{
+ const QScriptLine &line = eng->lines[index];
+
+ if (line.length == 0)
+ return QList();
+
+ if (from < 0)
+ from = textStart();
+
+ if (length < 0)
+ length = textLength();
+
+ if (length == 0)
+ return QList();
+
+ QTextLayout::FormatRange selection;
+ selection.start = from;
+ selection.length = length;
+
+ QTextLineItemIterator iterator(eng, index, QPointF(), &selection);
+ qreal y = line.y.toReal() + line.base().toReal();
+ QList glyphRuns;
+ while (!iterator.atEnd()) {
+ QScriptItem &si = iterator.next();
+ if (si.analysis.flags >= QScriptAnalysis::TabOrObject)
+ continue;
+
+ if (from >= 0 && length >= 0 && (from >= iterator.itemEnd || from + length <= iterator.itemStart))
+ continue;
+
+ QPointF pos(iterator.x.toReal(), y);
+
+ QFont font;
+ QGlyphRun::GlyphRunFlags flags;
+ if (!eng->useRawFont) {
+ font = eng->font(si);
+ if (font.overline())
+ flags |= QGlyphRun::Overline;
+ if (font.underline())
+ flags |= QGlyphRun::Underline;
+ if (font.strikeOut())
+ flags |= QGlyphRun::StrikeOut;
+ }
+
+ bool rtl = false;
+ if (si.analysis.bidiLevel % 2) {
+ flags |= QGlyphRun::RightToLeft;
+ rtl = true;
+ }
+
+ int relativeFrom = qMax(iterator.itemStart, from) - si.position;
+ int relativeTo = qMin(iterator.itemEnd, from + length) - 1 - si.position;
+
+ unsigned short *logClusters = eng->logClusters(&si);
+ int glyphsStart = logClusters[relativeFrom];
+ int glyphsEnd = (relativeTo == iterator.itemLength) ? si.num_glyphs - 1 : logClusters[relativeTo];
+ // the glyph index right next to the requested range
+ int nextGlyphIndex = (relativeTo < iterator.itemLength - 1) ? logClusters[relativeTo + 1] : si.num_glyphs;
+ if (nextGlyphIndex - 1 > glyphsEnd)
+ glyphsEnd = nextGlyphIndex - 1;
+ bool startsInsideLigature = relativeFrom > 0 && logClusters[relativeFrom - 1] == glyphsStart;
+ bool endsInsideLigature = nextGlyphIndex == glyphsEnd;
+
+ int itemGlyphsStart = logClusters[iterator.itemStart - si.position];
+ int itemGlyphsEnd = logClusters[iterator.itemEnd - 1 - si.position];
+
+ QGlyphLayout glyphLayout = eng->shapedGlyphs(&si);
+
+ // Calculate new x position of glyph layout for a subset. This becomes somewhat complex
+ // when we're breaking a RTL script item, since the expected position passed into
+ // getGlyphPositions() is the left-most edge of the left-most glyph in an RTL run.
+ if (relativeFrom != (iterator.itemStart - si.position) && !rtl) {
+ for (int i=itemGlyphsStart; iglyphsEnd; --i) {
+ QFixed justification = QFixed::fromFixed(glyphLayout.justifications[i].space_18d6);
+ pos.rx() += (glyphLayout.advances[i] + justification).toReal();
+ }
+ }
+
+ glyphLayout = glyphLayout.mid(glyphsStart, glyphsEnd - glyphsStart + 1);
+
+ QFixed x;
+ QFixed width;
+ iterator.getSelectionBounds(&x, &width);
+
+ if (glyphLayout.numGlyphs > 0) {
+ QFontEngine *mainFontEngine;
+#ifndef QT_NO_RAWFONT
+ if (eng->useRawFont && eng->rawFont.isValid())
+ mainFontEngine= eng->fontEngine(si);
+ else
+#endif
+ mainFontEngine = font.d->engineForScript(si.analysis.script);
+
+ if (mainFontEngine->type() == QFontEngine::Multi) {
+ QFontEngineMulti *multiFontEngine = static_cast(mainFontEngine);
+ int end = rtl ? glyphLayout.numGlyphs : 0;
+ int start = rtl ? end : 0;
+ int which = glyphLayout.glyphs[rtl ? start - 1 : end] >> 24;
+ for (; (rtl && start > 0) || (!rtl && end < glyphLayout.numGlyphs);
+ rtl ? --start : ++end) {
+ const int e = glyphLayout.glyphs[rtl ? start - 1 : end] >> 24;
+ if (e == which)
+ continue;
+
+ QGlyphLayout subLayout = glyphLayout.mid(start, end - start);
+ multiFontEngine->ensureEngineAt(which);
+
+ QGlyphRun::GlyphRunFlags subFlags = flags;
+ if (start == 0 && startsInsideLigature)
+ subFlags |= QGlyphRun::SplitLigature;
+
+ glyphRuns.append(glyphRunWithInfo(multiFontEngine->engine(which),
+ subLayout,
+ pos,
+ subFlags,
+ x,
+ width,
+ glyphsStart + start,
+ glyphsStart + end,
+ logClusters,
+ iterator.itemStart,
+ iterator.itemLength));
+ for (int i = 0; i < subLayout.numGlyphs; ++i) {
+ QFixed justification = QFixed::fromFixed(subLayout.justifications[i].space_18d6);
+ pos.rx() += (subLayout.advances[i] + justification).toReal();
+ }
+
+ if (rtl)
+ end = start;
+ else
+ start = end;
+ which = e;
+ }
+
+ QGlyphLayout subLayout = glyphLayout.mid(start, end - start);
+ multiFontEngine->ensureEngineAt(which);
+
+ QGlyphRun::GlyphRunFlags subFlags = flags;
+ if ((start == 0 && startsInsideLigature) || endsInsideLigature)
+ subFlags |= QGlyphRun::SplitLigature;
+
+ QGlyphRun glyphRun = glyphRunWithInfo(multiFontEngine->engine(which),
+ subLayout,
+ pos,
+ subFlags,
+ x,
+ width,
+ glyphsStart + start,
+ glyphsStart + end,
+ logClusters,
+ iterator.itemStart,
+ iterator.itemLength);
+ if (!glyphRun.isEmpty())
+ glyphRuns.append(glyphRun);
+ } else {
+ if (startsInsideLigature || endsInsideLigature)
+ flags |= QGlyphRun::SplitLigature;
+ QGlyphRun glyphRun = glyphRunWithInfo(mainFontEngine,
+ glyphLayout,
+ pos,
+ flags,
+ x,
+ width,
+ glyphsStart,
+ glyphsEnd,
+ logClusters,
+ iterator.itemStart,
+ iterator.itemLength);
+ if (!glyphRun.isEmpty())
+ glyphRuns.append(glyphRun);
+ }
+ }
+ }
+
+ return glyphRuns;
+}
+#endif // QT_NO_RAWFONT
+
+/*!
+ \fn void QTextLine::draw(QPainter *painter, const QPointF &position, const QTextLayout::FormatRange *selection) const
+
+ Draws a line on the given \a painter at the specified \a position.
+ The \a selection is reserved for internal use.
+*/
+void QTextLine::draw(QPainter *p, const QPointF &pos, const QTextLayout::FormatRange *selection) const
+{
+#ifndef QT_NO_RAWFONT
+ // Not intended to work with rawfont
+ Q_ASSERT(!eng->useRawFont);
+#endif
+ const QScriptLine &line = eng->lines[index];
+ QPen pen = p->pen();
+
+ bool noText = (selection && selection->format.property(SuppressText).toBool());
+
+ if (!line.length) {
+ if (selection
+ && selection->start <= line.from
+ && selection->start + selection->length > line.from) {
+
+ const qreal lineHeight = line.height().toReal();
+ QRectF r(pos.x() + line.x.toReal(), pos.y() + line.y.toReal(),
+ lineHeight / 2, QFontMetrics(eng->font()).width(QLatin1Char(' ')));
+ setPenAndDrawBackground(p, QPen(), selection->format, r);
+ p->setPen(pen);
+ }
+ return;
+ }
+
+
+ QTextLineItemIterator iterator(eng, index, pos, selection);
+ QFixed lineBase = line.base();
+ eng->clearDecorations();
+ eng->enableDelayDecorations();
+
+ const QFixed y = QFixed::fromReal(pos.y()) + line.y + lineBase;
+
+ bool suppressColors = (eng->option.flags() & QTextOption::SuppressColors);
+ while (!iterator.atEnd()) {
+ QScriptItem &si = iterator.next();
+
+ if (selection && selection->start >= 0 && iterator.isOutsideSelection())
+ continue;
+
+ if (si.analysis.flags == QScriptAnalysis::LineOrParagraphSeparator
+ && !(eng->option.flags() & QTextOption::ShowLineAndParagraphSeparators))
+ continue;
+
+ QFixed itemBaseLine = y;
+ QFont f = eng->font(si);
+ QTextCharFormat format;
+
+ if (eng->hasFormats() || selection) {
+ format = eng->format(&si);
+ if (suppressColors) {
+ format.clearForeground();
+ format.clearBackground();
+ format.clearProperty(QTextFormat::TextUnderlineColor);
+ }
+ if (selection)
+ format.merge(selection->format);
+
+ setPenAndDrawBackground(p, pen, format, QRectF(iterator.x.toReal(), (y - lineBase).toReal(),
+ iterator.itemWidth.toReal(), line.height().toReal()));
+
+ QTextCharFormat::VerticalAlignment valign = format.verticalAlignment();
+ if (valign == QTextCharFormat::AlignSuperScript || valign == QTextCharFormat::AlignSubScript) {
+ QFontEngine *fe = f.d->engineForScript(si.analysis.script);
+ QFixed height = fe->ascent() + fe->descent();
+ if (valign == QTextCharFormat::AlignSubScript)
+ itemBaseLine += height / 6;
+ else if (valign == QTextCharFormat::AlignSuperScript)
+ itemBaseLine -= height / 2;
+ }
+ }
+
+ if (si.analysis.flags >= QScriptAnalysis::TabOrObject) {
+
+ if (eng->hasFormats()) {
+ p->save();
+ if (si.analysis.flags == QScriptAnalysis::Object && eng->block.docHandle()) {
+ QFixed itemY = y - si.ascent;
+ if (format.verticalAlignment() == QTextCharFormat::AlignTop) {
+ itemY = y - lineBase;
+ }
+
+ QRectF itemRect(iterator.x.toReal(), itemY.toReal(), iterator.itemWidth.toReal(), si.height().toReal());
+
+ eng->docLayout()->drawInlineObject(p, itemRect,
+ QTextInlineObject(iterator.item, eng),
+ si.position + eng->block.position(),
+ format);
+ if (selection) {
+ QBrush bg = format.brushProperty(ObjectSelectionBrush);
+ if (bg.style() != Qt::NoBrush) {
+ QColor c = bg.color();
+ c.setAlpha(128);
+ p->fillRect(itemRect, c);
+ }
+ }
+ } else { // si.isTab
+ QFont f = eng->font(si);
+ QTextItemInt gf(si, &f, format);
+ gf.chars = 0;
+ gf.num_chars = 0;
+ gf.width = iterator.itemWidth;
+ QPainterPrivate::get(p)->drawTextItem(QPointF(iterator.x.toReal(), y.toReal()), gf, eng);
+ if (eng->option.flags() & QTextOption::ShowTabsAndSpaces) {
+ QChar visualTab(0x2192);
+ int w = QFontMetrics(f).width(visualTab);
+ qreal x = iterator.itemWidth.toReal() - w; // Right-aligned
+ if (x < 0)
+ p->setClipRect(QRectF(iterator.x.toReal(), line.y.toReal(),
+ iterator.itemWidth.toReal(), line.height().toReal()),
+ Qt::IntersectClip);
+ else
+ x /= 2; // Centered
+ p->drawText(QPointF(iterator.x.toReal() + x,
+ y.toReal()), visualTab);
+ }
+
+ }
+ p->restore();
+ }
+
+ continue;
+ }
+
+ unsigned short *logClusters = eng->logClusters(&si);
+ QGlyphLayout glyphs = eng->shapedGlyphs(&si);
+
+ QTextItemInt gf(glyphs.mid(iterator.glyphsStart, iterator.glyphsEnd - iterator.glyphsStart),
+ &f, eng->layoutData->string.unicode() + iterator.itemStart,
+ iterator.itemEnd - iterator.itemStart, eng->fontEngine(si), format);
+ gf.logClusters = logClusters + iterator.itemStart - si.position;
+ gf.width = iterator.itemWidth;
+ gf.justified = line.justified;
+ gf.initWithScriptItem(si);
+
+ Q_ASSERT(gf.fontEngine);
+
+ QPointF pos(iterator.x.toReal(), itemBaseLine.toReal());
+ if (format.penProperty(QTextFormat::TextOutline).style() != Qt::NoPen) {
+ QPainterPath path;
+ path.setFillRule(Qt::WindingFill);
+
+ if (gf.glyphs.numGlyphs)
+ gf.fontEngine->addOutlineToPath(pos.x(), pos.y(), gf.glyphs, &path, gf.flags);
+ if (gf.flags) {
+ const QFontEngine *fe = gf.fontEngine;
+ const qreal lw = fe->lineThickness().toReal();
+ if (gf.flags & QTextItem::Underline) {
+ qreal offs = fe->underlinePosition().toReal();
+ path.addRect(pos.x(), pos.y() + offs, gf.width.toReal(), lw);
+ }
+ if (gf.flags & QTextItem::Overline) {
+ qreal offs = fe->ascent().toReal() + 1;
+ path.addRect(pos.x(), pos.y() - offs, gf.width.toReal(), lw);
+ }
+ if (gf.flags & QTextItem::StrikeOut) {
+ qreal offs = fe->ascent().toReal() / 3;
+ path.addRect(pos.x(), pos.y() - offs, gf.width.toReal(), lw);
+ }
+ }
+
+ p->save();
+ p->setRenderHint(QPainter::Antialiasing);
+ //Currently QPen with a Qt::NoPen style still returns a default
+ //QBrush which != Qt::NoBrush so we need this specialcase to reset it
+ if (p->pen().style() == Qt::NoPen)
+ p->setBrush(Qt::NoBrush);
+ else
+ p->setBrush(p->pen().brush());
+
+ p->setPen(format.textOutline());
+ p->drawPath(path);
+ p->restore();
+ } else {
+ if (noText)
+ gf.glyphs.numGlyphs = 0; // slightly less elegant than it should be
+ QPainterPrivate::get(p)->drawTextItem(pos, gf, eng);
+ }
+
+ if (si.analysis.flags == QScriptAnalysis::Space
+ && (eng->option.flags() & QTextOption::ShowTabsAndSpaces)) {
+ QBrush c = format.foreground();
+ if (c.style() != Qt::NoBrush)
+ p->setPen(c.color());
+ QChar visualSpace((ushort)0xb7);
+ p->drawText(QPointF(iterator.x.toReal(), itemBaseLine.toReal()), visualSpace);
+ p->setPen(pen);
+ }
+ }
+ eng->drawDecorations(p);
+
+ if (eng->hasFormats())
+ p->setPen(pen);
+}
+
+/*!
+ \fn int QTextLine::cursorToX(int cursorPos, Edge edge) const
+
+ \overload
+*/
+
+/*!
+ Converts the cursor position \a cursorPos to the corresponding x position
+ inside the line, taking account of the \a edge.
+
+ If \a cursorPos is not a valid cursor position, the nearest valid
+ cursor position will be used instead, and cpos will be modified to
+ point to this valid cursor position.
+
+ \sa xToCursor()
+*/
+qreal QTextLine::cursorToX(int *cursorPos, Edge edge) const
+{
+ if (!eng->layoutData)
+ eng->itemize();
+
+ const QScriptLine &line = eng->lines[index];
+ bool lastLine = index >= eng->lines.size() - 1;
+
+ QFixed x = line.x;
+ x += eng->alignLine(line) - eng->leadingSpaceWidth(line);
+
+ if (!index && !eng->layoutData->items.size()) {
+ *cursorPos = 0;
+ return x.toReal();
+ }
+
+ int lineEnd = line.from + line.length + line.trailingSpaces;
+ int pos = qBound(0, *cursorPos, lineEnd);
+ int itm;
+ const QCharAttributes *attributes = eng->attributes();
+ if (!attributes) {
+ *cursorPos = 0;
+ return x.toReal();
+ }
+ while (pos < lineEnd && !attributes[pos].graphemeBoundary)
+ pos++;
+ if (pos == lineEnd) {
+ // end of line ensure we have the last item on the line
+ itm = eng->findItem(pos-1);
+ }
+ else
+ itm = eng->findItem(pos);
+ if (itm < 0) {
+ *cursorPos = 0;
+ return x.toReal();
+ }
+ eng->shapeLine(line);
+
+ const QScriptItem *si = &eng->layoutData->items[itm];
+ if (!si->num_glyphs)
+ eng->shape(itm);
+ pos -= si->position;
+
+ QGlyphLayout glyphs = eng->shapedGlyphs(si);
+ unsigned short *logClusters = eng->logClusters(si);
+ Q_ASSERT(logClusters);
+
+ int l = eng->length(itm);
+ if (pos > l)
+ pos = l;
+ if (pos < 0)
+ pos = 0;
+
+ int glyph_pos = pos == l ? si->num_glyphs : logClusters[pos];
+ if (edge == Trailing && glyph_pos < si->num_glyphs) {
+ // trailing edge is leading edge of next cluster
+ glyph_pos++;
+ while (glyph_pos < si->num_glyphs && !glyphs.attributes[glyph_pos].clusterStart)
+ glyph_pos++;
+ }
+
+ bool reverse = eng->layoutData->items[itm].analysis.bidiLevel % 2;
+
+
+ // add the items left of the cursor
+
+ int firstItem = eng->findItem(line.from);
+ int lastItem = eng->findItem(lineEnd - 1);
+ int nItems = (firstItem >= 0 && lastItem >= firstItem)? (lastItem-firstItem+1) : 0;
+
+ QVarLengthArray visualOrder(nItems);
+ QVarLengthArray levels(nItems);
+ for (int i = 0; i < nItems; ++i)
+ levels[i] = eng->layoutData->items[i+firstItem].analysis.bidiLevel;
+ QTextEngine::bidiReorder(nItems, levels.data(), visualOrder.data());
+
+ for (int i = 0; i < nItems; ++i) {
+ int item = visualOrder[i]+firstItem;
+ if (item == itm)
+ break;
+ QScriptItem &si = eng->layoutData->items[item];
+ if (!si.num_glyphs)
+ eng->shape(item);
+
+ if (si.analysis.flags >= QScriptAnalysis::TabOrObject) {
+ x += si.width;
+ continue;
+ }
+ int start = qMax(line.from, si.position);
+ int end = qMin(lineEnd, si.position + eng->length(item));
+
+ logClusters = eng->logClusters(&si);
+
+ int gs = logClusters[start-si.position];
+ int ge = (end == si.position + eng->length(item)) ? si.num_glyphs-1 : logClusters[end-si.position-1];
+
+ QGlyphLayout glyphs = eng->shapedGlyphs(&si);
+
+ while (gs <= ge) {
+ x += glyphs.effectiveAdvance(gs);
+ ++gs;
+ }
+ }
+
+ logClusters = eng->logClusters(si);
+ glyphs = eng->shapedGlyphs(si);
+ if (si->analysis.flags >= QScriptAnalysis::TabOrObject) {
+ if (pos == (reverse ? 0 : l))
+ x += si->width;
+ } else {
+ bool rtl = eng->isRightToLeft();
+ bool visual = eng->visualCursorMovement();
+ int end = qMin(lineEnd, si->position + l) - si->position;
+ if (reverse) {
+ int glyph_end = end == l ? si->num_glyphs : logClusters[end];
+ int glyph_start = glyph_pos;
+ if (visual && !rtl && !(lastLine && itm == (visualOrder[nItems - 1] + firstItem)))
+ glyph_start++;
+ for (int i = glyph_end - 1; i >= glyph_start; i--)
+ x += glyphs.effectiveAdvance(i);
+ } else {
+ int start = qMax(line.from - si->position, 0);
+ int glyph_start = logClusters[start];
+ int glyph_end = glyph_pos;
+ if (!visual || !rtl || (lastLine && itm == visualOrder[0] + firstItem))
+ glyph_end--;
+ for (int i = glyph_start; i <= glyph_end; i++)
+ x += glyphs.effectiveAdvance(i);
+ }
+ x += eng->offsetInLigature(si, pos, end, glyph_pos);
+ }
+
+ if (eng->option.wrapMode() != QTextOption::NoWrap && x > line.x + line.width)
+ x = line.x + line.width;
+ if (eng->option.wrapMode() != QTextOption::NoWrap && x < 0)
+ x = 0;
+
+ *cursorPos = pos + si->position;
+ return x.toReal();
+}
+
+/*!
+ \fn int QTextLine::xToCursor(qreal x, CursorPosition cpos) const
+
+ Converts the x-coordinate \a x, to the nearest matching cursor
+ position, depending on the cursor position type, \a cpos.
+ Note that result cursor position includes possible preedit area text.
+
+ \sa cursorToX()
+*/
+int QTextLine::xToCursor(qreal _x, CursorPosition cpos) const
+{
+ QFixed x = QFixed::fromReal(_x);
+ const QScriptLine &line = eng->lines[index];
+ bool lastLine = index >= eng->lines.size() - 1;
+ int lineNum = index;
+
+ if (!eng->layoutData)
+ eng->itemize();
+
+ int line_length = textLength();
+
+ if (!line_length)
+ return line.from;
+
+ int firstItem = eng->findItem(line.from);
+ int lastItem = eng->findItem(line.from + line_length - 1);
+ int nItems = (firstItem >= 0 && lastItem >= firstItem)? (lastItem-firstItem+1) : 0;
+
+ if (!nItems)
+ return 0;
+
+ x -= line.x;
+ x -= eng->alignLine(line);
+// qDebug("xToCursor: x=%f, cpos=%d", x.toReal(), cpos);
+
+ QVarLengthArray visualOrder(nItems);
+ QVarLengthArray levels(nItems);
+ for (int i = 0; i < nItems; ++i)
+ levels[i] = eng->layoutData->items[i+firstItem].analysis.bidiLevel;
+ QTextEngine::bidiReorder(nItems, levels.data(), visualOrder.data());
+
+ bool visual = eng->visualCursorMovement();
+ if (x <= 0) {
+ // left of first item
+ int item = visualOrder[0]+firstItem;
+ QScriptItem &si = eng->layoutData->items[item];
+ if (!si.num_glyphs)
+ eng->shape(item);
+ int pos = si.position;
+ if (si.analysis.bidiLevel % 2)
+ pos += eng->length(item);
+ pos = qMax(line.from, pos);
+ pos = qMin(line.from + line_length, pos);
+ return pos;
+ } else if (x < line.textWidth
+ || (line.justified && x < line.width)) {
+ // has to be in one of the runs
+ QFixed pos;
+ bool rtl = eng->isRightToLeft();
+
+ eng->shapeLine(line);
+ QVector insertionPoints;
+ if (visual && rtl)
+ eng->insertionPointsForLine(lineNum, insertionPoints);
+ int nchars = 0;
+ for (int i = 0; i < nItems; ++i) {
+ int item = visualOrder[i]+firstItem;
+ QScriptItem &si = eng->layoutData->items[item];
+ int item_length = eng->length(item);
+// qDebug(" item %d, visual %d x_remain=%f", i, item, x.toReal());
+
+ int start = qMax(line.from - si.position, 0);
+ int end = qMin(line.from + line_length - si.position, item_length);
+
+ unsigned short *logClusters = eng->logClusters(&si);
+
+ int gs = logClusters[start];
+ int ge = (end == item_length ? si.num_glyphs : logClusters[end]) - 1;
+ QGlyphLayout glyphs = eng->shapedGlyphs(&si);
+
+ QFixed item_width = 0;
+ if (si.analysis.flags >= QScriptAnalysis::TabOrObject) {
+ item_width = si.width;
+ } else {
+ int g = gs;
+ while (g <= ge) {
+ item_width += glyphs.effectiveAdvance(g);
+ ++g;
+ }
+ }
+// qDebug(" start=%d, end=%d, gs=%d, ge=%d item_width=%f", start, end, gs, ge, item_width.toReal());
+
+ if (pos + item_width < x) {
+ pos += item_width;
+ nchars += end;
+ continue;
+ }
+// qDebug(" inside run");
+ if (si.analysis.flags >= QScriptAnalysis::TabOrObject) {
+ if (cpos == QTextLine::CursorOnCharacter)
+ return si.position;
+ bool left_half = (x - pos) < item_width/2;
+
+ if (bool(si.analysis.bidiLevel % 2) != left_half)
+ return si.position;
+ return si.position + 1;
+ }
+
+ int glyph_pos = -1;
+ QFixed edge;
+ // has to be inside run
+ if (cpos == QTextLine::CursorOnCharacter) {
+ if (si.analysis.bidiLevel % 2) {
+ pos += item_width;
+ glyph_pos = gs;
+ while (gs <= ge) {
+ if (glyphs.attributes[gs].clusterStart) {
+ if (pos < x)
+ break;
+ glyph_pos = gs;
+ edge = pos;
+ }
+ pos -= glyphs.effectiveAdvance(gs);
+ ++gs;
+ }
+ } else {
+ glyph_pos = gs;
+ while (gs <= ge) {
+ if (glyphs.attributes[gs].clusterStart) {
+ if (pos > x)
+ break;
+ glyph_pos = gs;
+ edge = pos;
+ }
+ pos += glyphs.effectiveAdvance(gs);
+ ++gs;
+ }
+ }
+ } else {
+ QFixed dist = INT_MAX/256;
+ if (si.analysis.bidiLevel % 2) {
+ if (!visual || rtl || (lastLine && i == nItems - 1)) {
+ pos += item_width;
+ while (gs <= ge) {
+ if (glyphs.attributes[gs].clusterStart && qAbs(x-pos) < dist) {
+ glyph_pos = gs;
+ edge = pos;
+ dist = qAbs(x-pos);
+ }
+ pos -= glyphs.effectiveAdvance(gs);
+ ++gs;
+ }
+ } else {
+ while (ge >= gs) {
+ if (glyphs.attributes[ge].clusterStart && qAbs(x-pos) < dist) {
+ glyph_pos = ge;
+ edge = pos;
+ dist = qAbs(x-pos);
+ }
+ pos += glyphs.effectiveAdvance(ge);
+ --ge;
+ }
+ }
+ } else {
+ if (!visual || !rtl || (lastLine && i == 0)) {
+ while (gs <= ge) {
+ if (glyphs.attributes[gs].clusterStart && qAbs(x-pos) < dist) {
+ glyph_pos = gs;
+ edge = pos;
+ dist = qAbs(x-pos);
+ }
+ pos += glyphs.effectiveAdvance(gs);
+ ++gs;
+ }
+ } else {
+ QFixed oldPos = pos;
+ while (gs <= ge) {
+ pos += glyphs.effectiveAdvance(gs);
+ if (glyphs.attributes[gs].clusterStart && qAbs(x-pos) < dist) {
+ glyph_pos = gs;
+ edge = pos;
+ dist = qAbs(x-pos);
+ }
+ ++gs;
+ }
+ pos = oldPos;
+ }
+ }
+ if (qAbs(x-pos) < dist) {
+ if (visual) {
+ if (!rtl && i < nItems - 1) {
+ nchars += end;
+ continue;
+ }
+ if (rtl && nchars > 0)
+ return insertionPoints[lastLine ? nchars : nchars - 1];
+ }
+ return eng->positionInLigature(&si, end, x, pos, -1,
+ cpos == QTextLine::CursorOnCharacter);
+ }
+ }
+ Q_ASSERT(glyph_pos != -1);
+ return eng->positionInLigature(&si, end, x, edge, glyph_pos,
+ cpos == QTextLine::CursorOnCharacter);
+ }
+ }
+ // right of last item
+// qDebug() << "right of last";
+ int item = visualOrder[nItems-1]+firstItem;
+ QScriptItem &si = eng->layoutData->items[item];
+ if (!si.num_glyphs)
+ eng->shape(item);
+ int pos = si.position;
+ if (!(si.analysis.bidiLevel % 2))
+ pos += eng->length(item);
+ pos = qMax(line.from, pos);
+
+ int maxPos = line.from + line_length;
+
+ // except for the last line we assume that the
+ // character between lines is a space and we want
+ // to position the cursor to the left of that
+ // character.
+ // ###### breaks with japanese for example
+ if (this->index < eng->lines.count() - 1)
+ --maxPos;
+
+ pos = qMin(pos, maxPos);
+ return pos;
+}
+
+QT_END_NAMESPACE
diff --git a/Telegram/_qt_5_4_0_patch/qtbase/src/plugins/platforms/windows/qwindowskeymapper.cpp b/Telegram/_qt_5_4_0_patch/qtbase/src/plugins/platforms/windows/qwindowskeymapper.cpp
index ba423b430..3fd084829 100644
--- a/Telegram/_qt_5_4_0_patch/qtbase/src/plugins/platforms/windows/qwindowskeymapper.cpp
+++ b/Telegram/_qt_5_4_0_patch/qtbase/src/plugins/platforms/windows/qwindowskeymapper.cpp
@@ -832,7 +832,10 @@ bool QWindowsKeyMapper::translateMultimediaKeyEventInternal(QWindow *window, con
const int qtKey = CmdTbl[cmd];
sendExtendedPressRelease(receiver, qtKey, Qt::KeyboardModifier(state), 0, 0, 0);
- return true;
+ // QTBUG-43343: Make sure to return false if Qt does not handle the key, otherwise,
+ // the keys are not passed to the active media player.
+ const QKeySequence sequence(Qt::Modifier(state) + qtKey);
+ return QGuiApplicationPrivate::instance()->shortcutMap.hasShortcutForKeySequence(sequence);
#else
Q_UNREACHABLE();
return false;