diff --git a/.gitmodules b/.gitmodules index 0cc748142f6..428e75ff118 100644 --- a/.gitmodules +++ b/.gitmodules @@ -6,9 +6,9 @@ path = src/tools/perfparser url = ../perfparser.git ignore = dirty -[submodule "litehtml"] - path = src/plugins/help/qlitehtml/litehtml - url = https://github.com/litehtml/litehtml.git [submodule "googletest"] path = tests/unit/unittest/3rdparty/googletest url = https://github.com/google/googletest.git +[submodule "src/libs/qlitehtml"] + path = src/libs/qlitehtml + url = https://code.qt.io/playground/qlitehtml.git diff --git a/src/libs/CMakeLists.txt b/src/libs/CMakeLists.txt index 4cb8e8d925f..bdcd9aabc9a 100644 --- a/src/libs/CMakeLists.txt +++ b/src/libs/CMakeLists.txt @@ -18,3 +18,31 @@ add_subdirectory(clangsupport) add_subdirectory(tracing) add_subdirectory(qtcreatorcdbext) + +if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/qlitehtml/src/CMakeLists.txt) + option(BUILD_LIBRARY_QLITEHTML "Build library qlitehtml." ${BUILD_LIBRARIES_BY_DEFAULT}) + set(QLITEHTML_VERSION_COMPAT ${IDE_VERSION_COMPAT} CACHE STRING "") + if(BUILD_LIBRARY_QLITEHTML) + set(QLITEHTML_BIN_PATH ${IDE_BIN_PATH}) + set(QLITEHTML_LIBRARY_PATH ${IDE_LIBRARY_PATH}) + set(QLITEHTML_EXPORT QtCreator) + set(QLITEHTML_DEVEL_COMPONENT Devel) + set(QLITEHTML_DEVEL_EXCLUDE_FROM_ALL ON) + set(QLITEHTML_HEADER_PATH "include/src/lib/qlitehtml") + add_subdirectory(qlitehtml/src) + endif() + if(TARGET qlitehtml) + target_compile_definitions(qlitehtml PRIVATE ${DEFAULT_DEFINES}) # for precompiled headers + enable_pch(qlitehtml) + #doesn't work: TARGET 'qlitehtml' was not created in this directory. + #qtc_enable_separate_debug_info(qlitehtml "${IDE_LIBRARY_PATH}") + qtc_output_binary_dir(_output_binary_dir) + set_target_properties(qlitehtml PROPERTIES + BUILD_RPATH "${_LIB_RPATH}" + INSTALL_RPATH "${_LIB_RPATH}" + RUNTIME_OUTPUT_DIRECTORY "${_output_binary_dir}/${IDE_BIN_PATH}" + LIBRARY_OUTPUT_DIRECTORY "${_output_binary_dir}/${IDE_LIBRARY_PATH}" + ARCHIVE_OUTPUT_DIRECTORY "${_output_binary_dir}/${IDE_LIBRARY_PATH}" + ) + endif() +endif() diff --git a/src/libs/libs.qbs b/src/libs/libs.qbs index 0c3c8733706..0cccb89269c 100644 --- a/src/libs/libs.qbs +++ b/src/libs/libs.qbs @@ -1,7 +1,10 @@ import qbs +import qbs.File Project { name: "Libs" + property string qlitehtmlQbs: path + "/qlitehtml/src/qlitehtml.qbs" + property stringList qlitehtml: File.exists(qlitehtmlQbs) ? [qlitehtmlQbs] : [] references: [ "advanceddockingsystem/advanceddockingsystem.qbs", "aggregation/aggregation.qbs", @@ -24,5 +27,5 @@ Project { "utils/utils.qbs", "3rdparty/syntax-highlighting/syntax-highlighting.qbs", "3rdparty/yaml-cpp/yaml-cpp.qbs", - ].concat(project.additionalLibs) + ].concat(qlitehtml).concat(project.additionalLibs) } diff --git a/src/libs/qlitehtml b/src/libs/qlitehtml new file mode 160000 index 00000000000..3bccbb86ee3 --- /dev/null +++ b/src/libs/qlitehtml @@ -0,0 +1 @@ +Subproject commit 3bccbb86ee31d16776f2f881968e982d3739d837 diff --git a/src/plugins/help/CMakeLists.txt b/src/plugins/help/CMakeLists.txt index 6ec6faba051..112402757b9 100644 --- a/src/plugins/help/CMakeLists.txt +++ b/src/plugins/help/CMakeLists.txt @@ -60,11 +60,8 @@ extend_qtc_plugin(Help webenginehelpviewer.h ) -find_package(litehtml QUIET) -add_subdirectory(qlitehtml) - extend_qtc_plugin(Help - CONDITION TARGET litehtml AND TARGET qlitehtml + CONDITION TARGET qlitehtml FEATURE_INFO "litehtml help viewer" DEPENDS qlitehtml DEFINES QTC_LITEHTML_HELPVIEWER diff --git a/src/plugins/help/help.pro b/src/plugins/help/help.pro index 1299eb63849..1cb2f11156d 100644 --- a/src/plugins/help/help.pro +++ b/src/plugins/help/help.pro @@ -75,11 +75,14 @@ osx { } } -exists($$PWD/qlitehtml/litehtml/CMakeLists.txt)|!isEmpty(LITEHTML_INSTALL_DIR) { - include(qlitehtml/qlitehtml.pri) - HEADERS += litehtmlhelpviewer.h - SOURCES += litehtmlhelpviewer.cpp - DEFINES += QTC_LITEHTML_HELPVIEWER +QLITEHTML_DIR = $$PWD/../../libs/qlitehtml +exists($$QLITEHTML_DIR/src/qlitehtml.pri) { + exists($$QLITEHTML_DIR/src/3rdparty/litehtml/CMakeLists.txt)|!isEmpty(LITEHTML_INSTALL_DIR) { + include($$QLITEHTML_DIR/src/qlitehtml.pri) + HEADERS += litehtmlhelpviewer.h + SOURCES += litehtmlhelpviewer.cpp + DEFINES += QTC_LITEHTML_HELPVIEWER + } } RESOURCES += help.qrc diff --git a/src/plugins/help/help.qbs b/src/plugins/help/help.qbs index 2eea437473e..a1bfca2de61 100644 --- a/src/plugins/help/help.qbs +++ b/src/plugins/help/help.qbs @@ -3,8 +3,6 @@ import qbs.Utilities Project { name: "Help" - references: "qlitehtml" - QtcPlugin { name: "Help" diff --git a/src/plugins/help/qlitehtml/CMakeLists.txt b/src/plugins/help/qlitehtml/CMakeLists.txt deleted file mode 100644 index 4df18719a60..00000000000 --- a/src/plugins/help/qlitehtml/CMakeLists.txt +++ /dev/null @@ -1,32 +0,0 @@ -find_package(litehtml QUIET) -if(NOT TARGET litehtml AND EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/litehtml/CMakeLists.txt) - set(ORIG_FPIC ${CMAKE_POSITION_INDEPENDENT_CODE}) - if (WIN32) - set(LITEHTML_UTF8 ON CACHE BOOL "") - endif() - set(CMAKE_POSITION_INDEPENDENT_CODE ON) - - add_subdirectory(litehtml EXCLUDE_FROM_ALL) - - set(CMAKE_POSITION_INDEPENDENT_CODE "${ORIG_FPIC}") - # force optimized litehtml even in debug - if (CMAKE_BUILD_TYPE STREQUAL "Debug") - # except for windows - if (NOT WIN32) - target_compile_options(gumbo PRIVATE -O2) - target_compile_options(litehtml PRIVATE -O2) - endif() - endif() -endif() - -add_qtc_library(qlitehtml - CONDITION TARGET litehtml - PUBLIC_INCLUDES ${CMAKE_CURRENT_SOURCE_DIR} - DEPENDS Qt5::Widgets litehtml - PROPERTIES - POSITION_INDEPENDENT_CODE ON - SOURCES - container_qpainter.cpp container_qpainter.h container_qpainter_p.h - qlitehtml_global.h - qlitehtmlwidget.cpp qlitehtmlwidget.h -) diff --git a/src/plugins/help/qlitehtml/README.md b/src/plugins/help/qlitehtml/README.md deleted file mode 100644 index 9a3cefcd9e9..00000000000 --- a/src/plugins/help/qlitehtml/README.md +++ /dev/null @@ -1,24 +0,0 @@ -# Qt backend for litehtml - -Provides - -* A QPainter based rendering backend for the light-weight HTML/CSS rendering engine [litehtml]. -* A QWidget that uses the QPainter based backend and provides API for simply setting the HTML text - and a base URL plus hook that are used for requesting referenced resources. - -## How to build - -Build and install [litehtml]. It is recommended to build [litehtml] in release mode - -``` -cd litehtml -mkdir build -cd build -cmake -DCMAKE_INSTALL_PREFIX="$PWD/../install" -DCMAKE_BUILD_TYPE=Release -G Ninja .. -cmake --build . -cmake --install . -``` - -Add the [litehtml] installation path to the `CMAKE_PREFIX_PATH` when building the Qt backend - -[litehtml]: https://github.com/litehtml/litehtml diff --git a/src/plugins/help/qlitehtml/container_qpainter.cpp b/src/plugins/help/qlitehtml/container_qpainter.cpp deleted file mode 100644 index b15bc1fd386..00000000000 --- a/src/plugins/help/qlitehtml/container_qpainter.cpp +++ /dev/null @@ -1,1366 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of QLiteHtml. -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#include "container_qpainter.h" -#include "container_qpainter_p.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -const int kDragDistance = 5; - -using Font = QFont; -using Context = QPainter; - -namespace { -static Q_LOGGING_CATEGORY(log, "qlitehtml", QtCriticalMsg) -} - -static QFont toQFont(litehtml::uint_ptr hFont) -{ - return *reinterpret_cast(hFont); -} - -static QPainter *toQPainter(litehtml::uint_ptr hdc) -{ - return reinterpret_cast(hdc); -} - -static QRect toQRect(litehtml::position position) -{ - return {position.x, position.y, position.width, position.height}; -} - -static litehtml::elements_vector path(const litehtml::element::ptr &element) -{ - litehtml::elements_vector result; - litehtml::element::ptr current = element; - while (current) { - result.push_back(current); - current = current->parent(); - } - std::reverse(std::begin(result), std::end(result)); - return result; -} - -static std::pair getCommonParent(const litehtml::elements_vector &a, - const litehtml::elements_vector &b) -{ - litehtml::element::ptr parent; - const size_t minSize = std::min(a.size(), b.size()); - for (size_t i = 0; i < minSize; ++i) { - if (a.at(i) != b.at(i)) - return {parent, i}; - parent = a.at(i); - } - return {parent, minSize}; -} - -static std::pair getStartAndEnd(const Selection::Element &a, - const Selection::Element &b) -{ - if (a.element == b.element) { - if (a.index <= b.index) - return {a, b}; - return {b, a}; - } - const litehtml::elements_vector aPath = path(a.element); - const litehtml::elements_vector bPath = path(b.element); - litehtml::element::ptr commonParent; - size_t firstDifferentIndex; - std::tie(commonParent, firstDifferentIndex) = getCommonParent(aPath, bPath); - if (!commonParent) { - qWarning() << "internal error: litehtml elements do not have common parent"; - return {a, b}; - } - if (commonParent == a.element) - return {a, a}; // 'a' already contains 'b' - if (commonParent == b.element) - return {b, b}; - // find out if a or b is first in the child sub-trees of commonParent - const litehtml::element::ptr aBranch = aPath.at(firstDifferentIndex); - const litehtml::element::ptr bBranch = bPath.at(firstDifferentIndex); - for (int i = 0; i < int(commonParent->get_children_count()); ++i) { - const litehtml::element::ptr child = commonParent->get_child(i); - if (child == aBranch) - return {a, b}; - if (child == bBranch) - return {b, a}; - } - qWarning() << "internal error: failed to find out order of litehtml elements"; - return {a, b}; -} - -static int findChild(const litehtml::element::ptr &child, const litehtml::element::ptr &parent) -{ - for (int i = 0; i < int(parent->get_children_count()); ++i) - if (parent->get_child(i) == child) - return i; - return -1; -} - -// 1) stops right away if element == stop, otherwise stops whenever stop element is encountered -// 2) moves down the first children from element until there is none anymore -static litehtml::element::ptr firstLeaf(const litehtml::element::ptr &element, - const litehtml::element::ptr &stop) -{ - if (element == stop) - return element; - litehtml::element::ptr current = element; - while (current != stop && current->get_children_count() > 0) - current = current->get_child(0); - return current; -} - -// 1) stops right away if element == stop, otherwise stops whenever stop element is encountered -// 2) starts at next sibling (up the hierarchy chain) if possible, otherwise root -// 3) returns first leaf of the element found in 2 -static litehtml::element::ptr nextLeaf(const litehtml::element::ptr &element, - const litehtml::element::ptr &stop) -{ - if (element == stop) - return element; - litehtml::element::ptr current = element; - if (current->have_parent()) { - // find next sibling - const litehtml::element::ptr parent = current->parent(); - const int childIndex = findChild(current, parent); - if (childIndex < 0) { - qWarning() << "internal error: filed to find litehtml child element in parent"; - return stop; - } - if (childIndex + 1 >= int(parent->get_children_count())) // no sibling, move up - return nextLeaf(parent, stop); - current = parent->get_child(childIndex + 1); - } - return firstLeaf(current, stop); -} - -static Selection::Element selectionDetails(const litehtml::element::ptr &element, - const QString &text, - const QPoint &pos) -{ - // shortcut, which _might_ not really be correct - if (element->get_children_count() > 0) - return {element, -1, -1}; // everything selected - const QFont &font = toQFont(element->get_font()); - const QFontMetrics fm(font); - int previous = 0; - for (int i = 0; i < text.size(); ++i) { - const int width = fm.size(0, text.left(i + 1)).width(); - if ((width + previous) / 2 >= pos.x()) - return {element, i, previous}; - previous = width; - } - return {element, int(text.size()), previous}; -} - -static Selection::Element deepest_child_at_point(const litehtml::document::ptr &document, - const QPoint &pos, - const QPoint &viewportPos, - Selection::Mode mode) -{ - if (!document) - return {}; - - // the following does not find the "smallest" element, it often consists of children - // with individual words as text... - const litehtml::element::ptr element = document->root()->get_element_by_point(pos.x(), - pos.y(), - viewportPos.x(), - viewportPos.y()); - // ...so try to find a better match - const std::function recursion = - [&recursion, pos, mode](const litehtml::element::ptr &element, - const QRect &placement) -> Selection::Element { - if (!element) - return {}; - Selection::Element result; - for (int i = 0; i < int(element->get_children_count()); ++i) { - const litehtml::element::ptr child = element->get_child(i); - result = recursion(child, - toQRect(child->get_position()).translated(placement.topLeft())); - if (result.element) - return result; - } - if (placement.contains(pos)) { - litehtml::tstring text; - element->get_text(text); - if (!text.empty()) { - return mode == Selection::Mode::Free - ? selectionDetails(element, - QString::fromStdString(text), - pos - placement.topLeft()) - : Selection::Element({element, -1, -1}); - } - } - return {}; - }; - return recursion(element, element ? toQRect(element->get_placement()) : QRect()); -} - -// CSS: 400 == normal, 700 == bold. -// Qt5: 50 == normal, 75 == bold -// Qt6: == CSS -static QFont::Weight cssWeightToQtWeight(int cssWeight) -{ -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) - return QFont::Weight(cssWeight); -#else - if (cssWeight <= 400) - return QFont::Weight(cssWeight * 50 / 400); - if (cssWeight >= 700) - return QFont::Weight(75 + (cssWeight - 700) * 25 / 300); - return QFont::Weight(50 + (cssWeight - 400) * 25 / 300); -#endif -} - -static QFont::Style toQFontStyle(litehtml::font_style style) -{ - switch (style) { - case litehtml::fontStyleNormal: - return QFont::StyleNormal; - case litehtml::fontStyleItalic: - return QFont::StyleItalic; - } - // should not happen - qWarning(log) << "Unknown litehtml font style:" << style; - return QFont::StyleNormal; -} - -static QColor toQColor(const litehtml::web_color &color) -{ - return {color.red, color.green, color.blue, color.alpha}; -} - -static Qt::PenStyle borderPenStyle(litehtml::border_style style) -{ - switch (style) { - case litehtml::border_style_dotted: - return Qt::DotLine; - case litehtml::border_style_dashed: - return Qt::DashLine; - case litehtml::border_style_solid: - return Qt::SolidLine; - default: - qWarning(log) << "Unsupported border style:" << style; - } - return Qt::SolidLine; -} - -static QPen borderPen(const litehtml::border &border) -{ - return {toQColor(border.color), qreal(border.width), borderPenStyle(border.style)}; -} - -static QCursor toQCursor(const QString &c) -{ - if (c == "alias") - return {Qt::PointingHandCursor}; // ??? - if (c == "all-scroll") - return {Qt::SizeAllCursor}; - if (c == "auto") - return {Qt::ArrowCursor}; // ??? - if (c == "cell") - return {Qt::UpArrowCursor}; - if (c == "context-menu") - return {Qt::ArrowCursor}; // ??? - if (c == "col-resize") - return {Qt::SplitHCursor}; - if (c == "copy") - return {Qt::DragCopyCursor}; - if (c == "crosshair") - return {Qt::CrossCursor}; - if (c == "default") - return {Qt::ArrowCursor}; - if (c == "e-resize") - return {Qt::SizeHorCursor}; // ??? - if (c == "ew-resize") - return {Qt::SizeHorCursor}; - if (c == "grab") - return {Qt::OpenHandCursor}; - if (c == "grabbing") - return {Qt::ClosedHandCursor}; - if (c == "help") - return {Qt::WhatsThisCursor}; - if (c == "move") - return {Qt::SizeAllCursor}; - if (c == "n-resize") - return {Qt::SizeVerCursor}; // ??? - if (c == "ne-resize") - return {Qt::SizeBDiagCursor}; // ??? - if (c == "nesw-resize") - return {Qt::SizeBDiagCursor}; - if (c == "ns-resize") - return {Qt::SizeVerCursor}; - if (c == "nw-resize") - return {Qt::SizeFDiagCursor}; // ??? - if (c == "nwse-resize") - return {Qt::SizeFDiagCursor}; - if (c == "no-drop") - return {Qt::ForbiddenCursor}; - if (c == "none") - return {Qt::BlankCursor}; - if (c == "not-allowed") - return {Qt::ForbiddenCursor}; - if (c == "pointer") - return {Qt::PointingHandCursor}; - if (c == "progress") - return {Qt::BusyCursor}; - if (c == "row-resize") - return {Qt::SplitVCursor}; - if (c == "s-resize") - return {Qt::SizeVerCursor}; // ??? - if (c == "se-resize") - return {Qt::SizeFDiagCursor}; // ??? - if (c == "sw-resize") - return {Qt::SizeBDiagCursor}; // ??? - if (c == "text") - return {Qt::IBeamCursor}; - if (c == "url") - return {Qt::ArrowCursor}; // ??? - if (c == "w-resize") - return {Qt::SizeHorCursor}; // ??? - if (c == "wait") - return {Qt::BusyCursor}; - if (c == "zoom-in") - return {Qt::ArrowCursor}; // ??? - qWarning(log) << QString("unknown cursor property \"%1\"").arg(c).toUtf8().constData(); - return {Qt::ArrowCursor}; -} - -bool Selection::isValid() const -{ - return !selection.isEmpty(); -} - -void Selection::update() -{ - const auto addElement = [this](const Selection::Element &element, - const Selection::Element &end = {}) { - litehtml::tstring elemText; - element.element->get_text(elemText); - const QString textStr = QString::fromStdString(elemText); - if (!textStr.isEmpty()) { - QRect rect = toQRect(element.element->get_placement()).adjusted(-1, -1, 1, 1); - if (element.index < 0) { // fully selected - text += textStr; - } else if (end.element) { // select from element "to end" - if (element.element == end.element) { - // end.index is guaranteed to be >= element.index by caller, same for x - text += textStr.mid(element.index, end.index - element.index); - const int left = rect.left(); - rect.setLeft(left + element.x); - rect.setRight(left + end.x); - } else { - text += textStr.mid(element.index); - rect.setLeft(rect.left() + element.x); - } - } else { // select from start of element - text += textStr.left(element.index); - rect.setRight(rect.left() + element.x); - } - selection.append(rect); - } - }; - - if (startElem.element && endElem.element) { - // Edge cases: - // start and end elements could be reversed or children of each other - Selection::Element start; - Selection::Element end; - std::tie(start, end) = getStartAndEnd(startElem, endElem); - - selection.clear(); - text.clear(); - - // Treats start element as a leaf even if it isn't, because it already contains all its - // children - addElement(start, end); - if (start.element != end.element) { - litehtml::element::ptr current = start.element; - do { - current = nextLeaf(current, end.element); - if (current == end.element) - addElement(end); - else - addElement({current, -1, -1}); - } while (current != end.element); - } - } else { - selection = {}; - text.clear(); - } - QClipboard *cb = QGuiApplication::clipboard(); - if (cb->supportsSelection()) - cb->setText(text, QClipboard::Selection); -} - -QRect Selection::boundingRect() const -{ - QRect rect; - for (const QRect &r : selection) - rect = rect.united(r); - return rect; -} - -DocumentContainer::DocumentContainer() - : d(new DocumentContainerPrivate) -{} - -DocumentContainer::~DocumentContainer() = default; - -litehtml::uint_ptr DocumentContainerPrivate::create_font(const litehtml::tchar_t *faceName, - int size, - int weight, - litehtml::font_style italic, - unsigned int decoration, - litehtml::font_metrics *fm) -{ - const QStringList splitNames = QString::fromUtf8(faceName).split(',', Qt::SkipEmptyParts); - QStringList familyNames; - std::transform(splitNames.cbegin(), - splitNames.cend(), - std::back_inserter(familyNames), - [this](const QString &s) { - // clean whitespace and quotes - QString name = s.trimmed(); - if (name.startsWith('\"')) - name = name.mid(1); - if (name.endsWith('\"')) - name.chop(1); - const QString lowerName = name.toLower(); - if (lowerName == "serif") - return serifFont(); - if (lowerName == "sans-serif") - return sansSerifFont(); - if (lowerName == "monospace") - return monospaceFont(); - return name; - }); - auto font = new QFont(); -#if (QT_VERSION >= QT_VERSION_CHECK(5, 13, 0)) - font->setFamilies(familyNames); -#else - struct CompareCaseinsensitive - { - bool operator()(const QString &a, const QString &b) const - { - return a.compare(b, Qt::CaseInsensitive) < 0; - } - }; - static const QStringList known = QFontDatabase().families(); - static const std::set knownFamilies(known.cbegin(), - known.cend()); - font->setFamily(familyNames.last()); - for (const QString &name : qAsConst(familyNames)) { - const auto found = knownFamilies.find(name); - if (found != knownFamilies.end()) { - font->setFamily(*found); - break; - } - } -#endif - font->setPixelSize(size); - font->setWeight(cssWeightToQtWeight(weight)); - font->setStyle(toQFontStyle(italic)); - if (decoration == litehtml::font_decoration_underline) - font->setUnderline(true); - if (decoration == litehtml::font_decoration_overline) - font->setOverline(true); - if (decoration == litehtml::font_decoration_linethrough) - font->setStrikeOut(true); - if (fm) { - const QFontMetrics metrics(*font); - fm->height = metrics.height(); - fm->ascent = metrics.ascent(); - fm->descent = metrics.descent(); - fm->x_height = metrics.xHeight(); - fm->draw_spaces = true; - } - return reinterpret_cast(font); -} - -void DocumentContainerPrivate::delete_font(litehtml::uint_ptr hFont) -{ - auto font = reinterpret_cast(hFont); - delete font; -} - -int DocumentContainerPrivate::text_width(const litehtml::tchar_t *text, litehtml::uint_ptr hFont) -{ - const QFontMetrics fm(toQFont(hFont)); - return fm.horizontalAdvance(QString::fromUtf8(text)); -} - -void DocumentContainerPrivate::draw_text(litehtml::uint_ptr hdc, - const litehtml::tchar_t *text, - litehtml::uint_ptr hFont, - litehtml::web_color color, - const litehtml::position &pos) -{ - auto painter = toQPainter(hdc); - painter->setFont(toQFont(hFont)); - painter->setPen(toQColor(color)); - painter->drawText(toQRect(pos), 0, QString::fromUtf8(text)); -} - -int DocumentContainerPrivate::pt_to_px(int pt) -{ - // magic factor of 11/12 to account for differences to webengine/webkit - return m_paintDevice->physicalDpiY() * pt * 11 / m_paintDevice->logicalDpiY() / 12; -} - -int DocumentContainerPrivate::get_default_font_size() const -{ - return m_defaultFont.pointSize(); -} - -const litehtml::tchar_t *DocumentContainerPrivate::get_default_font_name() const -{ - return m_defaultFontFamilyName.constData(); -} - -void DocumentContainerPrivate::draw_list_marker(litehtml::uint_ptr hdc, - const litehtml::list_marker &marker) -{ - auto painter = toQPainter(hdc); - if (marker.image.empty()) { - if (marker.marker_type == litehtml::list_style_type_square) { - painter->setPen(Qt::NoPen); - painter->setBrush(toQColor(marker.color)); - painter->drawRect(toQRect(marker.pos)); - } else if (marker.marker_type == litehtml::list_style_type_disc) { - painter->setPen(Qt::NoPen); - painter->setBrush(toQColor(marker.color)); - painter->drawEllipse(toQRect(marker.pos)); - } else if (marker.marker_type == litehtml::list_style_type_circle) { - painter->setPen(toQColor(marker.color)); - painter->setBrush(Qt::NoBrush); - painter->drawEllipse(toQRect(marker.pos)); - } else { - // TODO we do not get information about index and font for e.g. decimal / roman - // at least draw a bullet - painter->setPen(Qt::NoPen); - painter->setBrush(toQColor(marker.color)); - painter->drawEllipse(toQRect(marker.pos)); - qWarning(log) << "list marker of type" << marker.marker_type << "not supported"; - } - } else { - const QPixmap pixmap = getPixmap(QString::fromStdString(marker.image), - QString::fromStdString(marker.baseurl)); - painter->drawPixmap(toQRect(marker.pos), pixmap); - } -} - -void DocumentContainerPrivate::load_image(const litehtml::tchar_t *src, - const litehtml::tchar_t *baseurl, - bool redraw_on_ready) -{ - const auto qtSrc = QString::fromUtf8(src); - const auto qtBaseUrl = QString::fromUtf8(baseurl); - Q_UNUSED(redraw_on_ready) - qDebug(log) << "load_image:" << QString("src = \"%1\";").arg(qtSrc).toUtf8().constData() - << QString("base = \"%1\"").arg(qtBaseUrl).toUtf8().constData(); - const QUrl url = resolveUrl(qtSrc, qtBaseUrl); - if (m_pixmaps.contains(url)) - return; - - QPixmap pixmap; - pixmap.loadFromData(m_dataCallback(url)); - m_pixmaps.insert(url, pixmap); -} - -void DocumentContainerPrivate::get_image_size(const litehtml::tchar_t *src, - const litehtml::tchar_t *baseurl, - litehtml::size &sz) -{ - const auto qtSrc = QString::fromUtf8(src); - const auto qtBaseUrl = QString::fromUtf8(baseurl); - if (qtSrc.isEmpty()) // for some reason that happens - return; - qDebug(log) << "get_image_size:" << QString("src = \"%1\";").arg(qtSrc).toUtf8().constData() - << QString("base = \"%1\"").arg(qtBaseUrl).toUtf8().constData(); - const QPixmap pm = getPixmap(qtSrc, qtBaseUrl); - sz.width = pm.width(); - sz.height = pm.height(); -} - -void DocumentContainerPrivate::drawSelection(QPainter *painter, const QRect &clip) const -{ - painter->save(); - painter->setClipRect(clip, Qt::IntersectClip); - for (const QRect &r : m_selection.selection) { - const QRect clientRect = r.translated(-m_scrollPosition); - const QPalette palette = m_paletteCallback(); - painter->fillRect(clientRect, palette.brush(QPalette::Highlight)); - } - painter->restore(); -} - -static QString tagName(const litehtml::element::ptr &e) -{ - litehtml::element::ptr current = e; - while (current && std::strlen(current->get_tagName()) == 0) - current = current->parent(); - return current ? QString::fromUtf8(current->get_tagName()) : QString(); -} - -void DocumentContainerPrivate::buildIndex() -{ - m_index.elementToIndex.clear(); - m_index.indexToElement.clear(); - m_index.text.clear(); - - int index = 0; - bool inBody = false; - litehtml::element::ptr current = firstLeaf(m_document->root(), nullptr); - while (current != m_document->root()) { - m_index.elementToIndex.insert({current, index}); - if (!inBody) - inBody = tagName(current).toLower() == "body"; - if (inBody && current->is_visible()) { - litehtml::tstring text; - current->get_text(text); - if (!text.empty()) { - m_index.indexToElement.push_back({index, current}); - const QString str = QString::fromStdString(text); - m_index.text += str; - index += str.size(); - } - } - current = nextLeaf(current, m_document->root()); - } -} - -void DocumentContainerPrivate::draw_background(litehtml::uint_ptr hdc, - const litehtml::background_paint &bg) -{ - auto painter = toQPainter(hdc); - if (bg.is_root) { - // TODO ? - return; - } - painter->save(); - painter->setClipRect(toQRect(bg.clip_box)); - const QRegion horizontalMiddle( - QRect(bg.border_box.x, - bg.border_box.y + bg.border_radius.top_left_y, - bg.border_box.width, - bg.border_box.height - bg.border_radius.top_left_y - bg.border_radius.bottom_left_y)); - const QRegion horizontalTop( - QRect(bg.border_box.x + bg.border_radius.top_left_x, - bg.border_box.y, - bg.border_box.width - bg.border_radius.top_left_x - bg.border_radius.top_right_x, - bg.border_radius.top_left_y)); - const QRegion horizontalBottom(QRect(bg.border_box.x + bg.border_radius.bottom_left_x, - bg.border_box.bottom() - bg.border_radius.bottom_left_y, - bg.border_box.width - bg.border_radius.bottom_left_x - - bg.border_radius.bottom_right_x, - bg.border_radius.bottom_left_y)); - const QRegion topLeft(QRect(bg.border_box.left(), - bg.border_box.top(), - 2 * bg.border_radius.top_left_x, - 2 * bg.border_radius.top_left_y), - QRegion::Ellipse); - const QRegion topRight(QRect(bg.border_box.right() - 2 * bg.border_radius.top_right_x, - bg.border_box.top(), - 2 * bg.border_radius.top_right_x, - 2 * bg.border_radius.top_right_y), - QRegion::Ellipse); - const QRegion bottomLeft(QRect(bg.border_box.left(), - bg.border_box.bottom() - 2 * bg.border_radius.bottom_left_y, - 2 * bg.border_radius.bottom_left_x, - 2 * bg.border_radius.bottom_left_y), - QRegion::Ellipse); - const QRegion bottomRight(QRect(bg.border_box.right() - 2 * bg.border_radius.bottom_right_x, - bg.border_box.bottom() - 2 * bg.border_radius.bottom_right_y, - 2 * bg.border_radius.bottom_right_x, - 2 * bg.border_radius.bottom_right_y), - QRegion::Ellipse); - const QRegion clipRegion = horizontalMiddle.united(horizontalTop) - .united(horizontalBottom) - .united(topLeft) - .united(topRight) - .united(bottomLeft) - .united(bottomRight); - painter->setClipRegion(clipRegion, Qt::IntersectClip); - painter->setPen(Qt::NoPen); - painter->setBrush(toQColor(bg.color)); - painter->drawRect(bg.border_box.x, bg.border_box.y, bg.border_box.width, bg.border_box.height); - drawSelection(painter, toQRect(bg.border_box)); - if (!bg.image.empty()) { - const QPixmap pixmap = getPixmap(QString::fromStdString(bg.image), - QString::fromStdString(bg.baseurl)); - if (bg.repeat == litehtml::background_repeat_no_repeat) { - painter->drawPixmap(QRect(bg.position_x, - bg.position_y, - bg.image_size.width, - bg.image_size.height), - pixmap); - } else if (bg.repeat == litehtml::background_repeat_repeat_x) { - if (bg.image_size.width > 0) { - int x = bg.border_box.left(); - while (x <= bg.border_box.right()) { - painter->drawPixmap(QRect(x, - bg.border_box.top(), - bg.image_size.width, - bg.image_size.height), - pixmap); - x += bg.image_size.width; - } - } - } else { - qWarning(log) << "unsupported background repeat" << bg.repeat; - } - } - painter->restore(); -} - -void DocumentContainerPrivate::draw_borders(litehtml::uint_ptr hdc, - const litehtml::borders &borders, - const litehtml::position &draw_pos, - bool root) -{ - Q_UNUSED(root) - // TODO: special border styles - auto painter = toQPainter(hdc); - if (borders.top.style != litehtml::border_style_none - && borders.top.style != litehtml::border_style_hidden) { - painter->setPen(borderPen(borders.top)); - painter->drawLine(draw_pos.left() + borders.radius.top_left_x, - draw_pos.top(), - draw_pos.right() - borders.radius.top_right_x, - draw_pos.top()); - painter->drawArc(draw_pos.left(), - draw_pos.top(), - 2 * borders.radius.top_left_x, - 2 * borders.radius.top_left_y, - 90 * 16, - 90 * 16); - painter->drawArc(draw_pos.right() - 2 * borders.radius.top_right_x, - draw_pos.top(), - 2 * borders.radius.top_right_x, - 2 * borders.radius.top_right_y, - 0, - 90 * 16); - } - if (borders.bottom.style != litehtml::border_style_none - && borders.bottom.style != litehtml::border_style_hidden) { - painter->setPen(borderPen(borders.bottom)); - painter->drawLine(draw_pos.left() + borders.radius.bottom_left_x, - draw_pos.bottom(), - draw_pos.right() - borders.radius.bottom_right_x, - draw_pos.bottom()); - painter->drawArc(draw_pos.left(), - draw_pos.bottom() - 2 * borders.radius.bottom_left_y, - 2 * borders.radius.bottom_left_x, - 2 * borders.radius.bottom_left_y, - 180 * 16, - 90 * 16); - painter->drawArc(draw_pos.right() - 2 * borders.radius.bottom_right_x, - draw_pos.bottom() - 2 * borders.radius.bottom_right_y, - 2 * borders.radius.bottom_right_x, - 2 * borders.radius.bottom_right_y, - 270 * 16, - 90 * 16); - } - if (borders.left.style != litehtml::border_style_none - && borders.left.style != litehtml::border_style_hidden) { - painter->setPen(borderPen(borders.left)); - painter->drawLine(draw_pos.left(), - draw_pos.top() + borders.radius.top_left_y, - draw_pos.left(), - draw_pos.bottom() - borders.radius.bottom_left_y); - } - if (borders.right.style != litehtml::border_style_none - && borders.right.style != litehtml::border_style_hidden) { - painter->setPen(borderPen(borders.right)); - painter->drawLine(draw_pos.right(), - draw_pos.top() + borders.radius.top_right_y, - draw_pos.right(), - draw_pos.bottom() - borders.radius.bottom_right_y); - } -} - -void DocumentContainerPrivate::set_caption(const litehtml::tchar_t *caption) -{ - m_caption = QString::fromUtf8(caption); -} - -void DocumentContainerPrivate::set_base_url(const litehtml::tchar_t *base_url) -{ - m_baseUrl = QString::fromUtf8(base_url); -} - -void DocumentContainerPrivate::link(const std::shared_ptr &doc, - const litehtml::element::ptr &el) -{ - // TODO - qDebug(log) << "link"; - Q_UNUSED(doc) - Q_UNUSED(el) -} - -void DocumentContainerPrivate::on_anchor_click(const litehtml::tchar_t *url, - const litehtml::element::ptr &el) -{ - Q_UNUSED(el) - if (!m_blockLinks) - m_linkCallback(resolveUrl(QString::fromUtf8(url), m_baseUrl)); -} - -void DocumentContainerPrivate::set_cursor(const litehtml::tchar_t *cursor) -{ - m_cursorCallback(toQCursor(QString::fromUtf8(cursor))); -} - -void DocumentContainerPrivate::transform_text(litehtml::tstring &text, litehtml::text_transform tt) -{ - // TODO - qDebug(log) << "transform_text"; - Q_UNUSED(text) - Q_UNUSED(tt) -} - -void DocumentContainerPrivate::import_css(litehtml::tstring &text, - const litehtml::tstring &url, - litehtml::tstring &baseurl) -{ - const QUrl actualUrl = resolveUrl(QString::fromStdString(url), QString::fromStdString(baseurl)); - const QString urlString = actualUrl.toString(QUrl::None); - const int lastSlash = urlString.lastIndexOf('/'); - baseurl = urlString.left(lastSlash).toStdString(); - text = QString::fromUtf8(m_dataCallback(actualUrl)).toStdString(); -} - -void DocumentContainerPrivate::set_clip(const litehtml::position &pos, - const litehtml::border_radiuses &bdr_radius, - bool valid_x, - bool valid_y) -{ - // TODO - qDebug(log) << "set_clip"; - Q_UNUSED(pos) - Q_UNUSED(bdr_radius) - Q_UNUSED(valid_x) - Q_UNUSED(valid_y) -} - -void DocumentContainerPrivate::del_clip() -{ - // TODO - qDebug(log) << "del_clip"; -} - -void DocumentContainerPrivate::get_client_rect(litehtml::position &client) const -{ - client = {m_clientRect.x(), m_clientRect.y(), m_clientRect.width(), m_clientRect.height()}; -} - -std::shared_ptr DocumentContainerPrivate::create_element( - const litehtml::tchar_t *tag_name, - const litehtml::string_map &attributes, - const std::shared_ptr &doc) -{ - // TODO - qDebug(log) << "create_element" << QString::fromUtf8(tag_name); - Q_UNUSED(attributes) - Q_UNUSED(doc) - return {}; -} - -void DocumentContainerPrivate::get_media_features(litehtml::media_features &media) const -{ - media.type = litehtml::media_type_screen; - // TODO - qDebug(log) << "get_media_features"; -} - -void DocumentContainerPrivate::get_language(litehtml::tstring &language, litehtml::tstring &culture) const -{ - // TODO - qDebug(log) << "get_language"; - Q_UNUSED(language) - Q_UNUSED(culture) -} - -void DocumentContainer::setPaintDevice(QPaintDevice *paintDevice) -{ - d->m_paintDevice = paintDevice; -} - -void DocumentContainer::setScrollPosition(const QPoint &pos) -{ - d->m_scrollPosition = pos; -} - -void DocumentContainer::setDocument(const QByteArray &data, DocumentContainerContext *context) -{ - d->m_pixmaps.clear(); - d->m_selection = {}; - d->m_document = litehtml::document::createFromUTF8(data.constData(), d.get(), &context->d->context); - d->buildIndex(); -} - -bool DocumentContainer::hasDocument() const -{ - return d->m_document.get(); -} - -void DocumentContainer::setBaseUrl(const QString &url) -{ - d->set_base_url(url.toUtf8().constData()); -} - -void DocumentContainer::render(int width, int height) -{ - d->m_clientRect = {0, 0, width, height}; - if (!d->m_document) - return; - d->m_document->render(width); - d->m_selection.update(); -} - -void DocumentContainer::draw(QPainter *painter, const QRect &clip) -{ - d->drawSelection(painter, clip); - const QPoint pos = -d->m_scrollPosition; - const litehtml::position clipRect = {clip.x(), clip.y(), clip.width(), clip.height()}; - d->m_document->draw(reinterpret_cast(painter), pos.x(), pos.y(), &clipRect); -} - -int DocumentContainer::documentWidth() const -{ - return d->m_document->width(); -} - -int DocumentContainer::documentHeight() const -{ - return d->m_document->height(); -} - -int DocumentContainer::anchorY(const QString &anchorName) const -{ - litehtml::element::ptr element = d->m_document->root()->select_one( - QString("#%1").arg(anchorName).toStdString()); - if (!element) { - element = d->m_document->root()->select_one(QString("[name=%1]").arg(anchorName).toStdString()); - } - if (element) - return element->get_placement().y; - return -1; -} - -QVector DocumentContainer::mousePressEvent(const QPoint &documentPos, - const QPoint &viewportPos, - Qt::MouseButton button) -{ - if (!d->m_document || button != Qt::LeftButton) - return {}; - QVector redrawRects; - // selection - if (d->m_selection.isValid()) - redrawRects.append(d->m_selection.boundingRect()); - d->m_selection = {}; - d->m_selection.selectionStartDocumentPos = documentPos; - d->m_selection.startElem = deepest_child_at_point(d->m_document, - documentPos, - viewportPos, - d->m_selection.mode); - // post to litehtml - litehtml::position::vector redrawBoxes; - if (d->m_document->on_lbutton_down( - documentPos.x(), documentPos.y(), viewportPos.x(), viewportPos.y(), redrawBoxes)) { - for (const litehtml::position &box : redrawBoxes) - redrawRects.append(toQRect(box)); - } - return redrawRects; -} - -QVector DocumentContainer::mouseMoveEvent(const QPoint &documentPos, - const QPoint &viewportPos) -{ - if (!d->m_document) - return {}; - QVector redrawRects; - // selection - if (d->m_selection.isSelecting - || (!d->m_selection.selectionStartDocumentPos.isNull() - && (d->m_selection.selectionStartDocumentPos - documentPos).manhattanLength() >= kDragDistance - && d->m_selection.startElem.element)) { - const Selection::Element element = deepest_child_at_point(d->m_document, - documentPos, - viewportPos, - d->m_selection.mode); - if (element.element) { - redrawRects.append( - d->m_selection.boundingRect() /*.adjusted(-1, -1, +1, +1)*/); // redraw old selection area - d->m_selection.endElem = element; - d->m_selection.update(); - redrawRects.append(d->m_selection.boundingRect()); - } - d->m_selection.isSelecting = true; - } - litehtml::position::vector redrawBoxes; - if (d->m_document->on_mouse_over( - documentPos.x(), documentPos.y(), viewportPos.x(), viewportPos.y(), redrawBoxes)) { - for (const litehtml::position &box : redrawBoxes) - redrawRects.append(toQRect(box)); - } - return redrawRects; -} - -QVector DocumentContainer::mouseReleaseEvent(const QPoint &documentPos, - const QPoint &viewportPos, - Qt::MouseButton button) -{ - if (!d->m_document || button != Qt::LeftButton) - return {}; - QVector redrawRects; - // selection - d->m_selection.isSelecting = false; - d->m_selection.selectionStartDocumentPos = {}; - if (d->m_selection.isValid()) - d->m_blockLinks = true; - else - d->m_selection = {}; - litehtml::position::vector redrawBoxes; - if (d->m_document->on_lbutton_up( - documentPos.x(), documentPos.y(), viewportPos.x(), viewportPos.y(), redrawBoxes)) { - for (const litehtml::position &box : redrawBoxes) - redrawRects.append(toQRect(box)); - } - d->m_blockLinks = false; - return redrawRects; -} - -QVector DocumentContainer::mouseDoubleClickEvent(const QPoint &documentPos, - const QPoint &viewportPos, - Qt::MouseButton button) -{ - if (!d->m_document || button != Qt::LeftButton) - return {}; - QVector redrawRects; - d->m_selection = {}; - d->m_selection.mode = Selection::Mode::Word; - const Selection::Element element = deepest_child_at_point(d->m_document, - documentPos, - viewportPos, - d->m_selection.mode); - if (element.element) { - d->m_selection.startElem = element; - d->m_selection.endElem = d->m_selection.startElem; - d->m_selection.isSelecting = true; - d->m_selection.update(); - if (d->m_selection.isValid()) - redrawRects.append(d->m_selection.boundingRect()); - } else { - if (d->m_selection.isValid()) - redrawRects.append(d->m_selection.boundingRect()); - d->m_selection = {}; - } - return redrawRects; -} - -QVector DocumentContainer::leaveEvent() -{ - if (!d->m_document) - return {}; - litehtml::position::vector redrawBoxes; - if (d->m_document->on_mouse_leave(redrawBoxes)) { - QVector redrawRects; - for (const litehtml::position &box : redrawBoxes) - redrawRects.append(toQRect(box)); - return redrawRects; - } - return {}; -} - -QUrl DocumentContainer::linkAt(const QPoint &documentPos, const QPoint &viewportPos) -{ - if (!d->m_document) - return {}; - const litehtml::element::ptr element = d->m_document->root()->get_element_by_point( - documentPos.x(), documentPos.y(), viewportPos.x(), viewportPos.y()); - const char *href = element->get_attr("href"); - if (href) - return d->resolveUrl(QString::fromUtf8(href), d->m_baseUrl); - return {}; -} - -QString DocumentContainer::caption() const -{ - return d->m_caption; -} - -QString DocumentContainer::selectedText() const -{ - return d->m_selection.text; -} - -void DocumentContainer::findText(const QString &text, - QTextDocument::FindFlags flags, - bool incremental, - bool *wrapped, - bool *success, - QVector *oldSelection, - QVector *newSelection) -{ - if (success) - *success = false; - if (oldSelection) - oldSelection->clear(); - if (newSelection) - newSelection->clear(); - if (!d->m_document) - return; - const bool backward = flags & QTextDocument::FindBackward; - int startIndex = backward ? -1 : 0; - if (d->m_selection.startElem.element && d->m_selection.endElem.element) { // selection - // poor-man's incremental search starts at beginning of selection, - // non-incremental at end (forward search) or beginning (backward search) - Selection::Element start; - Selection::Element end; - std::tie(start, end) = getStartAndEnd(d->m_selection.startElem, d->m_selection.endElem); - Selection::Element searchStart; - if (incremental || backward) { - if (start.index < 0) // fully selected - searchStart = {firstLeaf(start.element, nullptr), 0, -1}; - else - searchStart = start; - } else { - if (end.index < 0) // fully selected - searchStart = {nextLeaf(end.element, nullptr), 0, -1}; - else - searchStart = end; - } - const auto findInIndex = d->m_index.elementToIndex.find(searchStart.element); - if (findInIndex == std::end(d->m_index.elementToIndex)) { - qWarning() << "internal error: cannot find litehmtl element in index"; - return; - } - startIndex = findInIndex->second + searchStart.index; - if (backward) - --startIndex; - } - - const auto fillXPos = [](const Selection::Element &e) { - litehtml::tstring ttext; - e.element->get_text(ttext); - const QString text = QString::fromStdString(ttext); - const QFont &font = toQFont(e.element->get_font()); - const QFontMetrics fm(font); - return Selection::Element{e.element, e.index, fm.size(0, text.left(e.index)).width()}; - }; - - QString term = QRegularExpression::escape(text); - if (flags & QTextDocument::FindWholeWords) - term = QString("\\b%1\\b").arg(term); - const QRegularExpression::PatternOptions patternOptions - = (flags & QTextDocument::FindCaseSensitively) ? QRegularExpression::NoPatternOption - : QRegularExpression::CaseInsensitiveOption; - const QRegularExpression expression(term, patternOptions); - - int foundIndex = backward ? d->m_index.text.lastIndexOf(expression, startIndex) - : d->m_index.text.indexOf(expression, startIndex); - if (foundIndex < 0) { // wrap - foundIndex = backward ? d->m_index.text.lastIndexOf(expression) - : d->m_index.text.indexOf(expression); - if (wrapped && foundIndex >= 0) - *wrapped = true; - } - if (foundIndex >= 0) { - const Index::Entry startEntry = d->m_index.findElement(foundIndex); - const Index::Entry endEntry = d->m_index.findElement(foundIndex + text.size()); - if (!startEntry.second || !endEntry.second) { - qWarning() << "internal error: search ended up with nullptr elements"; - return; - } - if (oldSelection) - *oldSelection = d->m_selection.selection; - d->m_selection = {}; - d->m_selection.startElem = fillXPos({startEntry.second, foundIndex - startEntry.first, -1}); - d->m_selection.endElem = fillXPos( - {endEntry.second, int(foundIndex + text.size() - endEntry.first), -1}); - d->m_selection.update(); - if (newSelection) - *newSelection = d->m_selection.selection; - if (success) - *success = true; - return; - } - return; -} - -void DocumentContainer::setDefaultFont(const QFont &font) -{ - d->m_defaultFont = font; - d->m_defaultFontFamilyName = d->m_defaultFont.family().toUtf8(); - // Since font family name and size are read only once, when parsing html, - // we need to trigger the reparse of this info. - if (d->m_document && d->m_document->root()) { - d->m_document->root()->refresh_styles(); - d->m_document->root()->parse_styles(); - } -} - -QFont DocumentContainer::defaultFont() const -{ - return d->m_defaultFont; -} - -void DocumentContainer::setDataCallback(const DocumentContainer::DataCallback &callback) -{ - d->m_dataCallback = callback; -} - -void DocumentContainer::setCursorCallback(const DocumentContainer::CursorCallback &callback) -{ - d->m_cursorCallback = callback; -} - -void DocumentContainer::setLinkCallback(const DocumentContainer::LinkCallback &callback) -{ - d->m_linkCallback = callback; -} - -void DocumentContainer::setPaletteCallback(const DocumentContainer::PaletteCallback &callback) -{ - d->m_paletteCallback = callback; -} - -static litehtml::element::ptr elementForY(int y, const litehtml::document::ptr &document) -{ - if (!document) - return {}; - - const std::function recursion = - [&recursion](int y, const litehtml::element::ptr &element) { - litehtml::element::ptr result; - const int subY = y - element->get_position().y; - if (subY <= 0) - return element; - for (int i = 0; i < int(element->get_children_count()); ++i) { - const litehtml::element::ptr child = element->get_child(i); - result = recursion(subY, child); - if (result) - return result; - } - return result; - }; - - return recursion(y, document->root()); -} - -int DocumentContainer::withFixedElementPosition(int y, const std::function &action) -{ - const litehtml::element::ptr element = elementForY(y, d->m_document); - action(); - if (element) - return element->get_placement().y; - return -1; -} - -QPixmap DocumentContainerPrivate::getPixmap(const QString &imageUrl, const QString &baseUrl) -{ - const QString actualBaseurl = baseUrl.isEmpty() ? m_baseUrl : baseUrl; - const QUrl url = resolveUrl(imageUrl, baseUrl); - if (!m_pixmaps.contains(url)) { - qWarning(log) << "draw_background: pixmap not loaded for" << url; - return {}; - } - return m_pixmaps.value(url); -} - -QString DocumentContainerPrivate::serifFont() const -{ - // TODO make configurable - return {"Times New Roman"}; -} - -QString DocumentContainerPrivate::sansSerifFont() const -{ - // TODO make configurable - return {"Arial"}; -} - -QString DocumentContainerPrivate::monospaceFont() const -{ - // TODO make configurable - return {"Courier"}; -} - -QUrl DocumentContainerPrivate::resolveUrl(const QString &url, const QString &baseUrl) const -{ - const QUrl qurl(url); - if (qurl.isRelative() && !qurl.path(QUrl::FullyEncoded).isEmpty()) { - const QString actualBaseurl = baseUrl.isEmpty() ? m_baseUrl : baseUrl; - QUrl resolvedUrl(actualBaseurl + '/' + url); - resolvedUrl.setPath(QDir::cleanPath(resolvedUrl.path(QUrl::FullyEncoded))); - return resolvedUrl; - } - return qurl; -} - -Index::Entry Index::findElement(int index) const -{ - const auto upper = std::upper_bound(std::begin(indexToElement), - std::end(indexToElement), - Entry{index, {}}, - [](const Entry &a, const Entry &b) { - return a.first < b.first; - }); - if (upper == std::begin(indexToElement)) // should not happen for index >= 0 - return {-1, {}}; - return *(upper - 1); -} - -DocumentContainerContext::DocumentContainerContext() - : d(new DocumentContainerContextPrivate) -{} - -DocumentContainerContext::~DocumentContainerContext() = default; - -void DocumentContainerContext::setMasterStyleSheet(const QString &css) -{ - d->context.load_master_stylesheet(css.toUtf8().constData()); -} diff --git a/src/plugins/help/qlitehtml/container_qpainter.h b/src/plugins/help/qlitehtml/container_qpainter.h deleted file mode 100644 index a88ec8de266..00000000000 --- a/src/plugins/help/qlitehtml/container_qpainter.h +++ /dev/null @@ -1,124 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of QLiteHtml. -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#pragma once - -#include "qlitehtml_global.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -class DocumentContainerPrivate; -class DocumentContainerContextPrivate; - -class QLITEHTML_EXPORT DocumentContainerContext -{ -public: - DocumentContainerContext(); - ~DocumentContainerContext(); - - void setMasterStyleSheet(const QString &css); - -private: - std::unique_ptr d; - - friend class DocumentContainer; - friend class DocumentContainerPrivate; -}; - -class QLITEHTML_EXPORT DocumentContainer -{ -public: - DocumentContainer(); - virtual ~DocumentContainer(); - -public: // outside API - void setPaintDevice(QPaintDevice *paintDevice); - void setDocument(const QByteArray &data, DocumentContainerContext *context); - bool hasDocument() const; - void setBaseUrl(const QString &url); - void setScrollPosition(const QPoint &pos); - void render(int width, int height); - void draw(QPainter *painter, const QRect &clip); - int documentWidth() const; - int documentHeight() const; - int anchorY(const QString &anchorName) const; - - // these return areas to redraw in document space - QVector mousePressEvent(const QPoint &documentPos, - const QPoint &viewportPos, - Qt::MouseButton button); - QVector mouseMoveEvent(const QPoint &documentPos, const QPoint &viewportPos); - QVector mouseReleaseEvent(const QPoint &documentPos, - const QPoint &viewportPos, - Qt::MouseButton button); - QVector mouseDoubleClickEvent(const QPoint &documentPos, - const QPoint &viewportPos, - Qt::MouseButton button); - QVector leaveEvent(); - - QUrl linkAt(const QPoint &documentPos, const QPoint &viewportPos); - - QString caption() const; - QString selectedText() const; - - void findText(const QString &text, - QTextDocument::FindFlags flags, - bool incremental, - bool *wrapped, - bool *success, - QVector *oldSelection, - QVector *newSelection); - - void setDefaultFont(const QFont &font); - QFont defaultFont() const; - - using DataCallback = std::function; - void setDataCallback(const DataCallback &callback); - - using CursorCallback = std::function; - void setCursorCallback(const CursorCallback &callback); - - using LinkCallback = std::function; - void setLinkCallback(const LinkCallback &callback); - - using PaletteCallback = std::function; - void setPaletteCallback(const PaletteCallback &callback); - - int withFixedElementPosition(int y, const std::function &action); - -private: - std::unique_ptr d; -}; diff --git a/src/plugins/help/qlitehtml/container_qpainter_p.h b/src/plugins/help/qlitehtml/container_qpainter_p.h deleted file mode 100644 index 37b4b3adf40..00000000000 --- a/src/plugins/help/qlitehtml/container_qpainter_p.h +++ /dev/null @@ -1,163 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of QLiteHtml. -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#pragma once - -#include "container_qpainter.h" - -#include - -#include -#include -#include -#include -#include -#include - -#include - -class Selection -{ -public: - struct Element - { - litehtml::element::ptr element; - int index = -1; - int x = -1; - }; - - enum class Mode { Free, Word }; - - bool isValid() const; - - void update(); - QRect boundingRect() const; - - Element startElem; - Element endElem; - QVector selection; - QString text; - - QPoint selectionStartDocumentPos; - Mode mode = Mode::Free; - bool isSelecting = false; -}; - -struct Index -{ - QString text; - // only contains leaf elements - std::unordered_map elementToIndex; - - using Entry = std::pair; - std::vector indexToElement; - - Entry findElement(int index) const; -}; - -class DocumentContainerPrivate : public litehtml::document_container -{ -public: // document_container API - litehtml::uint_ptr create_font(const litehtml::tchar_t *faceName, - int size, - int weight, - litehtml::font_style italic, - unsigned int decoration, - litehtml::font_metrics *fm) override; - void delete_font(litehtml::uint_ptr hFont) override; - int text_width(const litehtml::tchar_t *text, litehtml::uint_ptr hFont) override; - void draw_text(litehtml::uint_ptr hdc, - const litehtml::tchar_t *text, - litehtml::uint_ptr hFont, - litehtml::web_color color, - const litehtml::position &pos) override; - int pt_to_px(int pt) override; - int get_default_font_size() const override; - const litehtml::tchar_t *get_default_font_name() const override; - void draw_list_marker(litehtml::uint_ptr hdc, const litehtml::list_marker &marker) override; - void load_image(const litehtml::tchar_t *src, - const litehtml::tchar_t *baseurl, - bool redraw_on_ready) override; - void get_image_size(const litehtml::tchar_t *src, - const litehtml::tchar_t *baseurl, - litehtml::size &sz) override; - void draw_background(litehtml::uint_ptr hdc, const litehtml::background_paint &bg) override; - void draw_borders(litehtml::uint_ptr hdc, - const litehtml::borders &borders, - const litehtml::position &draw_pos, - bool root) override; - void set_caption(const litehtml::tchar_t *caption) override; - void set_base_url(const litehtml::tchar_t *base_url) override; - void link(const std::shared_ptr &doc, - const litehtml::element::ptr &el) override; - void on_anchor_click(const litehtml::tchar_t *url, const litehtml::element::ptr &el) override; - void set_cursor(const litehtml::tchar_t *cursor) override; - void transform_text(litehtml::tstring &text, litehtml::text_transform tt) override; - void import_css(litehtml::tstring &text, - const litehtml::tstring &url, - litehtml::tstring &baseurl) override; - void set_clip(const litehtml::position &pos, - const litehtml::border_radiuses &bdr_radius, - bool valid_x, - bool valid_y) override; - void del_clip() override; - void get_client_rect(litehtml::position &client) const override; - std::shared_ptr create_element(const litehtml::tchar_t *tag_name, - const litehtml::string_map &attributes, - const std::shared_ptr &doc) override; - void get_media_features(litehtml::media_features &media) const override; - void get_language(litehtml::tstring &language, litehtml::tstring &culture) const override; - - QPixmap getPixmap(const QString &imageUrl, const QString &baseUrl); - QString serifFont() const; - QString sansSerifFont() const; - QString monospaceFont() const; - QUrl resolveUrl(const QString &url, const QString &baseUrl) const; - void drawSelection(QPainter *painter, const QRect &clip) const; - void buildIndex(); - - QPaintDevice *m_paintDevice = nullptr; - litehtml::document::ptr m_document; - Index m_index; - QString m_baseUrl; - QRect m_clientRect; - QPoint m_scrollPosition; - QString m_caption; - QFont m_defaultFont = QFont(sansSerifFont(), 16); - QByteArray m_defaultFontFamilyName = m_defaultFont.family().toUtf8(); - QHash m_pixmaps; - Selection m_selection; - DocumentContainer::DataCallback m_dataCallback; - DocumentContainer::CursorCallback m_cursorCallback; - DocumentContainer::LinkCallback m_linkCallback; - DocumentContainer::PaletteCallback m_paletteCallback; - bool m_blockLinks = false; -}; - -class DocumentContainerContextPrivate -{ -public: - litehtml::context context; -}; diff --git a/src/plugins/help/qlitehtml/litehtml b/src/plugins/help/qlitehtml/litehtml deleted file mode 160000 index db7f59d5886..00000000000 --- a/src/plugins/help/qlitehtml/litehtml +++ /dev/null @@ -1 +0,0 @@ -Subproject commit db7f59d5886fd50f84d48720c79dc2e6152efa83 diff --git a/src/plugins/help/qlitehtml/qlitehtml.pri b/src/plugins/help/qlitehtml/qlitehtml.pri deleted file mode 100644 index c5cfc42baab..00000000000 --- a/src/plugins/help/qlitehtml/qlitehtml.pri +++ /dev/null @@ -1,164 +0,0 @@ -exists($$PWD/litehtml/CMakeLists.txt) { - LH_SRC = $$PWD/litehtml - LH_HDR = $$LH_SRC/include/litehtml - GB_SRC = $$PWD/litehtml/src/gumbo - GB_HDR = $$GB_SRC/include/gumbo - - # gumbo - SOURCES += \ - $$GB_SRC/attribute.c \ - $$GB_SRC/char_ref.c \ - $$GB_SRC/error.c \ - $$GB_SRC/parser.c \ - $$GB_SRC/string_buffer.c \ - $$GB_SRC/string_piece.c \ - $$GB_SRC/tag.c \ - $$GB_SRC/tokenizer.c \ - $$GB_SRC/utf8.c \ - $$GB_SRC/util.c \ - $$GB_SRC/vector.c - - HEADERS += \ - $$GB_SRC/include//gumbo.h \ - $$GB_HDR/attribute.h \ - $$GB_HDR/char_ref.h \ - $$GB_HDR/error.h \ - $$GB_HDR/insertion_mode.h \ - $$GB_HDR/parser.h \ - $$GB_HDR/string_buffer.h \ - $$GB_HDR/string_piece.h \ - $$GB_HDR/tag_enum.h \ - $$GB_HDR/tag_gperf.h \ - $$GB_HDR/tag_sizes.h \ - $$GB_HDR/tag_strings.h \ - $$GB_HDR/token_type.h \ - $$GB_HDR/tokenizer.h \ - $$GB_HDR/tokenizer_states.h \ - $$GB_HDR/utf8.h \ - $$GB_HDR/util.h \ - $$GB_HDR/vector.h - - INCLUDEPATH *= $$GB_SRC/include $$GB_HDR - - win32 { - HEADERS += \ - $$GB_SRC/visualc/include/strings.h - INCLUDEPATH *= $$GB_SRC/visualc/include - } - - # litehtml - SOURCES += \ - $$LH_SRC/src/background.cpp \ - $$LH_SRC/src/box.cpp \ - $$LH_SRC/src/context.cpp \ - $$LH_SRC/src/css_length.cpp \ - $$LH_SRC/src/css_selector.cpp \ - $$LH_SRC/src/document.cpp \ - $$LH_SRC/src/el_anchor.cpp \ - $$LH_SRC/src/el_base.cpp \ - $$LH_SRC/src/el_before_after.cpp \ - $$LH_SRC/src/el_body.cpp \ - $$LH_SRC/src/el_break.cpp \ - $$LH_SRC/src/el_cdata.cpp \ - $$LH_SRC/src/el_comment.cpp \ - $$LH_SRC/src/el_div.cpp \ - $$LH_SRC/src/element.cpp \ - $$LH_SRC/src/el_font.cpp \ - $$LH_SRC/src/el_image.cpp \ - $$LH_SRC/src/el_li.cpp \ - $$LH_SRC/src/el_link.cpp \ - $$LH_SRC/src/el_para.cpp \ - $$LH_SRC/src/el_script.cpp \ - $$LH_SRC/src/el_space.cpp \ - $$LH_SRC/src/el_style.cpp \ - $$LH_SRC/src/el_table.cpp \ - $$LH_SRC/src/el_td.cpp \ - $$LH_SRC/src/el_text.cpp \ - $$LH_SRC/src/el_title.cpp \ - $$LH_SRC/src/el_tr.cpp \ - $$LH_SRC/src/html.cpp \ - $$LH_SRC/src/html_tag.cpp \ - $$LH_SRC/src/iterators.cpp \ - $$LH_SRC/src/media_query.cpp \ - $$LH_SRC/src/num_cvt.cpp \ - $$LH_SRC/src/style.cpp \ - $$LH_SRC/src/stylesheet.cpp \ - $$LH_SRC/src/table.cpp \ - $$LH_SRC/src/utf8_strings.cpp \ - $$LH_SRC/src/web_color.cpp - - HEADERS += \ - $$LH_SRC/include/litehtml.h \ - $$LH_HDR/attributes.h \ - $$LH_HDR/background.h \ - $$LH_HDR/borders.h \ - $$LH_HDR/box.h \ - $$LH_HDR/context.h \ - $$LH_HDR/css_length.h \ - $$LH_HDR/css_margins.h \ - $$LH_HDR/css_offsets.h \ - $$LH_HDR/css_position.h \ - $$LH_HDR/css_selector.h \ - $$LH_HDR/document.h \ - $$LH_HDR/el_anchor.h \ - $$LH_HDR/el_base.h \ - $$LH_HDR/el_before_after.h \ - $$LH_HDR/el_body.h \ - $$LH_HDR/el_break.h \ - $$LH_HDR/el_cdata.h \ - $$LH_HDR/el_comment.h \ - $$LH_HDR/el_div.h \ - $$LH_HDR/el_font.h \ - $$LH_HDR/el_image.h \ - $$LH_HDR/el_li.h \ - $$LH_HDR/el_link.h \ - $$LH_HDR/el_para.h \ - $$LH_HDR/el_script.h \ - $$LH_HDR/el_space.h \ - $$LH_HDR/el_style.h \ - $$LH_HDR/el_table.h \ - $$LH_HDR/el_td.h \ - $$LH_HDR/el_text.h \ - $$LH_HDR/el_title.h \ - $$LH_HDR/el_tr.h \ - $$LH_HDR/element.h \ - $$LH_HDR/html.h \ - $$LH_HDR/html_tag.h \ - $$LH_HDR/iterators.h \ - $$LH_HDR/media_query.h \ - $$LH_HDR/num_cvt.h \ - $$LH_HDR/os_types.h \ - $$LH_HDR/style.h \ - $$LH_HDR/stylesheet.h \ - $$LH_HDR/table.h \ - $$LH_HDR/types.h \ - $$LH_HDR/utf8_strings.h \ - $$LH_HDR/web_color.h - - INCLUDEPATH *= $$LH_SRC/include $$LH_HDR - - # litehtml without optimization is not fun - QMAKE_CFLAGS_DEBUG += -O2 - QMAKE_CXXFLAGS_DEBUG += -O2 -} else { - INCLUDEPATH *= $$LITEHTML_INSTALL_DIR/include $$LITEHTML_INSTALL_DIR/include/litehtml - LITEHTML_LIB_DIR = $$LITEHTML_INSTALL_DIR/lib - LIBS += -L$$LITEHTML_LIB_DIR -llitehtml -lgumbo - - win32: PRE_TARGETDEPS += $$LITEHTML_LIB_DIR/litehtml.lib $$LITEHTML_LIB_DIR/gumbo.lib - else:unix: PRE_TARGETDEPS += $$LITEHTML_LIB_DIR/liblitehtml.a $$LITEHTML_LIB_DIR/libgumbo.a -} - -HEADERS += \ - $$PWD/container_qpainter.h \ - $$PWD/container_qpainter_p.h \ - $$PWD/qlitehtmlwidget.h - -SOURCES += \ - $$PWD/container_qpainter.cpp \ - $$PWD/qlitehtmlwidget.cpp - -INCLUDEPATH *= $$PWD -win32: DEFINES += LITEHTML_UTF8 - -DEFINES *= QLITEHTML_STATIC_LIBRARY diff --git a/src/plugins/help/qlitehtml/qlitehtml.qbs b/src/plugins/help/qlitehtml/qlitehtml.qbs deleted file mode 100644 index 823e7be6bef..00000000000 --- a/src/plugins/help/qlitehtml/qlitehtml.qbs +++ /dev/null @@ -1,216 +0,0 @@ -import qbs.File -import qbs.FileInfo - -Product { - type: buildLib ? ["staticlibrary"] : undefined - - Depends { name: "cpp" } - Depends { name: "qtc" } - - property bool useExternalLib: qtc.litehtmlInstallDir - property bool buildLib: !useExternalLib && File.exists(path + "/litehtml/CMakeLists.txt") - condition: useExternalLib || buildLib - - property string gumboSrcDir: path + "/litehtml/src/gumbo" - property string gumboHeaderDir: gumboSrcDir + "/include/gumbo" - property string litehtmlHeaderDir: path + "/litehtml/include/litehtml" - property string mainHeaderDir: litehtmlHeaderDir + '/..' - property stringList sharedDefines: { - var defines = ["QLITEHTML_STATIC_LIBRARY"]; - if (qbs.targetOS.contains("windows")) - defines.push("LITEHTML_UTF8"); - return defines; - } - - cpp.defines: sharedDefines - cpp.includePaths: { - var paths = [gumboHeaderDir, gumboHeaderDir + '/..', litehtmlHeaderDir, mainHeaderDir]; - if (qbs.targetOS.contains("windows")) - paths.push(gumboSrcDir + "/visualc/include"); - return paths; - } - cpp.optimization: "fast" - cpp.warningLevel: "none" - cpp.cxxLanguageVersion: "c++14" - - Export { - Depends { name: "cpp" } - Group { - name: "litehtml/Qt glue" - cpp.warningLevel: "none" - files: [ - "container_qpainter.cpp", - "container_qpainter.h", - "container_qpainter_p.h", - "qlitehtmlwidget.cpp", - "qlitehtmlwidget.h", - ] - } - - Properties { - condition: product.useExternalLib - cpp.dynamicLibraries: ["litehtml", "gumbo"] - cpp.includePaths: [ - FileInfo.joinPaths(qtc.litehtmlInstallDir, "include"), - FileInfo.joinPaths(qtc.litehtmlInstallDir, "include", "litehtml"), - ] - cpp.libraryPaths: FileInfo.joinPaths(qtc.litehtmlInstallDir, "lib") - } - Properties { - condition: product.buildLib - cpp.defines: product.sharedDefines - cpp.includePaths: [product.mainHeaderDir, path] - } - } - - Group { - condition: buildLib - name: "gumbo sources" - prefix: gumboSrcDir + '/' - files: [ - "attribute.c", - "char_ref.c", - "error.c", - "parser.c", - "string_buffer.c", - "string_piece.c", - "tag.c", - "tokenizer.c", - "utf8.c", - "util.c", - "vector.c", - ] - } - - Group { - condition: buildLib - name: "gumbo headers" - prefix: gumboHeaderDir + '/' - files: [ - "../gumbo.h", - "attribute.h", - "char_ref.h", - "error.h", - "insertion_mode.h", - "parser.h", - "string_buffer.h", - "string_piece.h", - "tag_enum.h", - "tag_gperf.h", - "tag_sizes.h", - "tag_strings.h", - "token_type.h", - "tokenizer.h", - "tokenizer_states.h", - "utf8.h", - "util.h", - "vector.h", - ] - - Group { - name: "gumbo Windows headers" - condition: qbs.targetOS.contains("windows") - files: "../../visualc/include/strings.h" - } - } - - Group { - condition: buildLib - name: "litehtml sources" - prefix: "litehtml/src/" - files: [ - "background.cpp", - "box.cpp", - "context.cpp", - "css_length.cpp", - "css_selector.cpp", - "document.cpp", - "el_anchor.cpp", - "el_base.cpp", - "el_before_after.cpp", - "el_body.cpp", - "el_break.cpp", - "el_cdata.cpp", - "el_comment.cpp", - "el_div.cpp", - "element.cpp", - "el_font.cpp", - "el_image.cpp", - "el_li.cpp", - "el_link.cpp", - "el_para.cpp", - "el_script.cpp", - "el_space.cpp", - "el_style.cpp", - "el_table.cpp", - "el_td.cpp", - "el_text.cpp", - "el_title.cpp", - "el_tr.cpp", - "html.cpp", - "html_tag.cpp", - "iterators.cpp", - "media_query.cpp", - "num_cvt.cpp", - "style.cpp", - "stylesheet.cpp", - "table.cpp", - "utf8_strings.cpp", - "web_color.cpp", - ] - } - - Group { - condition: buildLib - name: "litehtml headers" - prefix: litehtmlHeaderDir + '/' - files: [ - "../litehtml.h", - "attributes.h", - "background.h", - "borders.h", - "box.h", - "context.h", - "css_length.h", - "css_margins.h", - "css_offsets.h", - "css_position.h", - "css_selector.h", - "document.h", - "el_anchor.h", - "el_base.h", - "el_before_after.h", - "el_body.h", - "el_break.h", - "el_cdata.h", - "el_comment.h", - "el_div.h", - "el_font.h", - "el_image.h", - "el_li.h", - "el_link.h", - "el_para.h", - "el_script.h", - "el_space.h", - "el_style.h", - "el_table.h", - "el_td.h", - "el_text.h", - "el_title.h", - "el_tr.h", - "element.h", - "html.h", - "html_tag.h", - "iterators.h", - "media_query.h", - "num_cvt.h", - "os_types.h", - "style.h", - "stylesheet.h", - "table.h", - "types.h", - "utf8_strings.h", - "web_color.h", - ] - } -} diff --git a/src/plugins/help/qlitehtml/qlitehtml_global.h b/src/plugins/help/qlitehtml/qlitehtml_global.h deleted file mode 100644 index a480b1146b6..00000000000 --- a/src/plugins/help/qlitehtml/qlitehtml_global.h +++ /dev/null @@ -1,36 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#pragma once - -#include - -#if defined(QLITEHTML_LIBRARY) -# define QLITEHTML_EXPORT Q_DECL_EXPORT -#elif defined(QLITEHTML_STATIC_LIBRARY) // Abuse single files for manual tests -# define QLITEHTML_EXPORT -#else -# define QLITEHTML_EXPORT Q_DECL_IMPORT -#endif diff --git a/src/plugins/help/qlitehtml/qlitehtmlwidget.cpp b/src/plugins/help/qlitehtml/qlitehtmlwidget.cpp deleted file mode 100644 index 475c6ca5eb5..00000000000 --- a/src/plugins/help/qlitehtml/qlitehtmlwidget.cpp +++ /dev/null @@ -1,656 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of QLiteHtml. -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#include "qlitehtmlwidget.h" - -#include "container_qpainter.h" - -#include -#include -#include -#include -#include -#include - -const int kScrollBarStep = 40; - -// TODO copied from litehtml/include/master.css -const char mastercss[] = R"RAW( -html { - display: block; -height:100%; -width:100%; -position: relative; -} - -head { - display: none -} - -meta { - display: none -} - -title { - display: none -} - -link { - display: none -} - -style { - display: none -} - -script { - display: none -} - -body { -display:block; - margin:8px; - height:100%; -width:100%; -} - -p { -display:block; - margin-top:1em; - margin-bottom:1em; -} - -b, strong { -display:inline; - font-weight:bold; -} - -i, em { -display:inline; - font-style:italic; -} - -center -{ - text-align:center; -display:block; -} - -a:link -{ - text-decoration: underline; -color: #00f; -cursor: pointer; -} - -h1, h2, h3, h4, h5, h6, div { -display:block; -} - -h1 { - font-weight:bold; - margin-top:0.67em; - margin-bottom:0.67em; - font-size: 2em; -} - -h2 { - font-weight:bold; - margin-top:0.83em; - margin-bottom:0.83em; - font-size: 1.5em; -} - -h3 { - font-weight:bold; - margin-top:1em; - margin-bottom:1em; - font-size:1.17em; -} - -h4 { - font-weight:bold; - margin-top:1.33em; - margin-bottom:1.33em -} - -h5 { - font-weight:bold; - margin-top:1.67em; - margin-bottom:1.67em; - font-size:.83em; -} - -h6 { - font-weight:bold; - margin-top:2.33em; - margin-bottom:2.33em; - font-size:.67em; -} - -br { -display:inline-block; -} - -br[clear="all"] -{ -clear:both; -} - -br[clear="left"] -{ -clear:left; -} - -br[clear="right"] -{ -clear:right; -} - -span { - display:inline -} - -img { -display: inline-block; -} - -img[align="right"] -{ - float: right; -} - -img[align="left"] -{ - float: left; -} - -hr { -display: block; - margin-top: 0.5em; - margin-bottom: 0.5em; - margin-left: auto; - margin-right: auto; - border-style: inset; - border-width: 1px -} - - -/***************** TABLES ********************/ - -table { -display: table; - border-collapse: separate; - border-spacing: 2px; - border-top-color:gray; - border-left-color:gray; - border-bottom-color:black; - border-right-color:black; -} - -tbody, tfoot, thead { -display:table-row-group; - vertical-align:middle; -} - -tr { -display: table-row; - vertical-align: inherit; - border-color: inherit; -} - -td, th { -display: table-cell; - vertical-align: inherit; - border-width:1px; -padding:1px; -} - -th { - font-weight: bold; -} - -table[border] { - border-style:solid; -} - -table[border|=0] { - border-style:none; -} - -table[border] td, table[border] th { - border-style:solid; - border-top-color:black; - border-left-color:black; - border-bottom-color:gray; - border-right-color:gray; -} - -table[border|=0] td, table[border|=0] th { - border-style:none; -} - -caption { -display: table-caption; -} - -td[nowrap], th[nowrap] { - white-space:nowrap; -} - -tt, code, kbd, samp { - font-family: monospace -} -pre, xmp, plaintext, listing { -display: block; - font-family: monospace; - white-space: pre; -margin: 1em 0 -} - -/***************** LISTS ********************/ - -ul, menu, dir { -display: block; - list-style-type: disc; - margin-top: 1em; - margin-bottom: 1em; - margin-left: 0; - margin-right: 0; - padding-left: 40px -} - -ol { -display: block; - list-style-type: decimal; - margin-top: 1em; - margin-bottom: 1em; - margin-left: 0; - margin-right: 0; - padding-left: 40px -} - -li { -display: list-item; -} - -ul ul, ol ul { - list-style-type: circle; -} - -ol ol ul, ol ul ul, ul ol ul, ul ul ul { - list-style-type: square; -} - -dd { -display: block; - margin-left: 40px; -} - -dl { -display: block; - margin-top: 1em; - margin-bottom: 1em; - margin-left: 0; - margin-right: 0; -} - -dt { -display: block; -} - -ol ul, ul ol, ul ul, ol ol { - margin-top: 0; - margin-bottom: 0 -} - -blockquote { -display: block; - margin-top: 1em; - margin-bottom: 1em; - margin-left: 40px; - margin-left: 40px; -} - -/*********** FORM ELEMENTS ************/ - -form { -display: block; - margin-top: 0em; -} - -option { -display: none; -} - -input, textarea, keygen, select, button, isindex { -margin: 0em; -color: initial; - line-height: normal; - text-transform: none; - text-indent: 0; - text-shadow: none; -display: inline-block; -} -input[type="hidden"] { -display: none; -} - - -article, aside, footer, header, hgroup, nav, section -{ -display: block; -} -)RAW"; - -class QLiteHtmlWidgetPrivate -{ -public: - QString html; - DocumentContainerContext context; - QUrl url; - DocumentContainer documentContainer; - qreal zoomFactor = 1; -}; - -QLiteHtmlWidget::QLiteHtmlWidget(QWidget *parent) - : QAbstractScrollArea(parent) - , d(new QLiteHtmlWidgetPrivate) -{ - setMouseTracking(true); - horizontalScrollBar()->setSingleStep(kScrollBarStep); - verticalScrollBar()->setSingleStep(kScrollBarStep); - - d->documentContainer.setCursorCallback([this](const QCursor &c) { viewport()->setCursor(c); }); - d->documentContainer.setPaletteCallback([this] { return palette(); }); - d->documentContainer.setLinkCallback([this](const QUrl &url) { - QUrl fullUrl = url; - if (url.isRelative() && url.path(QUrl::FullyEncoded).isEmpty()) { // fragment/anchor only - fullUrl = d->url; - fullUrl.setFragment(url.fragment(QUrl::FullyEncoded)); - } - // delay because document may not be changed directly during this callback - QTimer::singleShot(0, this, [this, fullUrl] { emit linkClicked(fullUrl); }); - }); - - // TODO adapt mastercss to palette (default text & background color) - d->context.setMasterStyleSheet(mastercss); -} - -QLiteHtmlWidget::~QLiteHtmlWidget() -{ - delete d; -} - -void QLiteHtmlWidget::setUrl(const QUrl &url) -{ - d->url = url; - QUrl urlWithoutAnchor = url; - urlWithoutAnchor.setFragment({}); - const QString urlString = urlWithoutAnchor.toString(QUrl::None); - const int lastSlash = urlString.lastIndexOf('/'); - const QString baseUrl = lastSlash >= 0 ? urlString.left(lastSlash) : urlString; - d->documentContainer.setBaseUrl(baseUrl); -} - -QUrl QLiteHtmlWidget::url() const -{ - return d->url; -} - -void QLiteHtmlWidget::setHtml(const QString &content) -{ - d->html = content; - d->documentContainer.setPaintDevice(viewport()); - d->documentContainer.setDocument(content.toUtf8(), &d->context); - verticalScrollBar()->setValue(0); - horizontalScrollBar()->setValue(0); - render(); -} - -QString QLiteHtmlWidget::html() const -{ - return d->html; -} - -QString QLiteHtmlWidget::title() const -{ - return d->documentContainer.caption(); -} - -void QLiteHtmlWidget::setZoomFactor(qreal scale) -{ - Q_ASSERT(scale != 0); - d->zoomFactor = scale; - withFixedTextPosition([this] { render(); }); -} - -qreal QLiteHtmlWidget::zoomFactor() const -{ - return d->zoomFactor; -} - -bool QLiteHtmlWidget::findText(const QString &text, - QTextDocument::FindFlags flags, - bool incremental, - bool *wrapped) -{ - bool success = false; - QVector oldSelection; - QVector newSelection; - d->documentContainer - .findText(text, flags, incremental, wrapped, &success, &oldSelection, &newSelection); - // scroll to search result position and/or redraw as necessary - QRect newSelectionCombined; - for (const QRect &r : qAsConst(newSelection)) - newSelectionCombined = newSelectionCombined.united(r); - QScrollBar *vBar = verticalScrollBar(); - const int top = newSelectionCombined.top(); - const int bottom = newSelectionCombined.bottom() - toVirtual(viewport()->size()).height(); - if (success && top < vBar->value() && vBar->minimum() <= top) { - vBar->setValue(top); - } else if (success && vBar->value() < bottom && bottom <= vBar->maximum()) { - vBar->setValue(bottom); - } else { - viewport()->update(fromVirtual(newSelectionCombined.translated(-scrollPosition()))); - for (const QRect &r : qAsConst(oldSelection)) - viewport()->update(fromVirtual(r.translated(-scrollPosition()))); - } - return success; -} - -void QLiteHtmlWidget::setDefaultFont(const QFont &font) -{ - withFixedTextPosition([this, &font] { - d->documentContainer.setDefaultFont(font); - render(); - }); -} - -QFont QLiteHtmlWidget::defaultFont() const -{ - return d->documentContainer.defaultFont(); -} - -void QLiteHtmlWidget::scrollToAnchor(const QString &name) -{ - if (!d->documentContainer.hasDocument()) - return; - horizontalScrollBar()->setValue(0); - if (name.isEmpty()) { - verticalScrollBar()->setValue(0); - return; - } - const int y = d->documentContainer.anchorY(name); - if (y >= 0) - verticalScrollBar()->setValue(std::min(y, verticalScrollBar()->maximum())); -} - -void QLiteHtmlWidget::setResourceHandler(const QLiteHtmlWidget::ResourceHandler &handler) -{ - d->documentContainer.setDataCallback(handler); -} - -QString QLiteHtmlWidget::selectedText() const -{ - return d->documentContainer.selectedText(); -} - -void QLiteHtmlWidget::paintEvent(QPaintEvent *event) -{ - if (!d->documentContainer.hasDocument()) - return; - d->documentContainer.setScrollPosition(scrollPosition()); - QPainter p(viewport()); - p.setWorldTransform(QTransform().scale(d->zoomFactor, d->zoomFactor)); - p.setRenderHint(QPainter::SmoothPixmapTransform, true); - p.setRenderHint(QPainter::Antialiasing, true); - d->documentContainer.draw(&p, toVirtual(event->rect())); -} - -void QLiteHtmlWidget::resizeEvent(QResizeEvent *event) -{ - withFixedTextPosition([this, event] { - QAbstractScrollArea::resizeEvent(event); - render(); - }); -} - -void QLiteHtmlWidget::mouseMoveEvent(QMouseEvent *event) -{ - QPoint viewportPos; - QPoint pos; - htmlPos(event->pos(), &viewportPos, &pos); - for (const QRect &r : d->documentContainer.mouseMoveEvent(pos, viewportPos)) - viewport()->update(fromVirtual(r.translated(-scrollPosition()))); -} - -void QLiteHtmlWidget::mousePressEvent(QMouseEvent *event) -{ - QPoint viewportPos; - QPoint pos; - htmlPos(event->pos(), &viewportPos, &pos); - for (const QRect &r : d->documentContainer.mousePressEvent(pos, viewportPos, event->button())) - viewport()->update(fromVirtual(r.translated(-scrollPosition()))); -} - -void QLiteHtmlWidget::mouseReleaseEvent(QMouseEvent *event) -{ - QPoint viewportPos; - QPoint pos; - htmlPos(event->pos(), &viewportPos, &pos); - for (const QRect &r : d->documentContainer.mouseReleaseEvent(pos, viewportPos, event->button())) - viewport()->update(fromVirtual(r.translated(-scrollPosition()))); -} - -void QLiteHtmlWidget::mouseDoubleClickEvent(QMouseEvent *event) -{ - QPoint viewportPos; - QPoint pos; - htmlPos(event->pos(), &viewportPos, &pos); - for (const QRect &r : - d->documentContainer.mouseDoubleClickEvent(pos, viewportPos, event->button())) { - viewport()->update(fromVirtual(r.translated(-scrollPosition()))); - } -} - -void QLiteHtmlWidget::leaveEvent(QEvent *event) -{ - Q_UNUSED(event) - for (const QRect &r : d->documentContainer.leaveEvent()) - viewport()->update(fromVirtual(r.translated(-scrollPosition()))); -} - -void QLiteHtmlWidget::contextMenuEvent(QContextMenuEvent *event) -{ - QPoint viewportPos; - QPoint pos; - htmlPos(event->pos(), &viewportPos, &pos); - emit contextMenuRequested(event->pos(), d->documentContainer.linkAt(pos, viewportPos)); -} - -void QLiteHtmlWidget::withFixedTextPosition(const std::function &action) -{ - // remember element to which to scroll after re-rendering - QPoint viewportPos; - QPoint pos; - htmlPos({}, &viewportPos, &pos); // top-left - const int y = d->documentContainer.withFixedElementPosition(pos.y(), action); - if (y >= 0) - verticalScrollBar()->setValue(std::min(y, verticalScrollBar()->maximum())); -} - -void QLiteHtmlWidget::render() -{ - if (!d->documentContainer.hasDocument()) - return; - const int fullWidth = width() / d->zoomFactor; - const QSize vViewportSize = toVirtual(viewport()->size()); - const int scrollbarWidth = style()->pixelMetric(QStyle::PM_ScrollBarExtent, nullptr, this); - const int w = fullWidth - scrollbarWidth - 2; - d->documentContainer.render(w, vViewportSize.height()); - // scroll bars reflect virtual/scaled size of html document - horizontalScrollBar()->setPageStep(vViewportSize.width()); - horizontalScrollBar()->setRange(0, std::max(0, d->documentContainer.documentWidth() - w)); - verticalScrollBar()->setPageStep(vViewportSize.height()); - verticalScrollBar() - ->setRange(0, std::max(0, d->documentContainer.documentHeight() - vViewportSize.height())); - viewport()->update(); -} - -QPoint QLiteHtmlWidget::scrollPosition() const -{ - return {horizontalScrollBar()->value(), verticalScrollBar()->value()}; -} - -void QLiteHtmlWidget::htmlPos(const QPoint &pos, QPoint *viewportPos, QPoint *htmlPos) const -{ - *viewportPos = toVirtual(viewport()->mapFromParent(pos)); - *htmlPos = *viewportPos + scrollPosition(); -} - -QPoint QLiteHtmlWidget::toVirtual(const QPoint &p) const -{ - return {int(p.x() / d->zoomFactor), int(p.y() / d->zoomFactor)}; -} - -QSize QLiteHtmlWidget::toVirtual(const QSize &s) const -{ - return {int(s.width() / d->zoomFactor), int(s.height() / d->zoomFactor)}; -} - -QRect QLiteHtmlWidget::toVirtual(const QRect &r) const -{ - return {toVirtual(r.topLeft()), toVirtual(r.size())}; -} - -QRect QLiteHtmlWidget::fromVirtual(const QRect &r) const -{ - const QPoint tl{int(r.x() * d->zoomFactor), int(r.y() * d->zoomFactor)}; - // round size up, and add one since the topleft point was rounded down - const QSize s{int(r.width() * d->zoomFactor + 0.5) + 1, - int(r.height() * d->zoomFactor + 0.5) + 1}; - return {tl, s}; -} diff --git a/src/plugins/help/qlitehtml/qlitehtmlwidget.h b/src/plugins/help/qlitehtml/qlitehtmlwidget.h deleted file mode 100644 index d314c4ca61c..00000000000 --- a/src/plugins/help/qlitehtml/qlitehtmlwidget.h +++ /dev/null @@ -1,95 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of QLiteHtml. -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#pragma once - -#include "qlitehtml_global.h" - -#include -#include - -#include - -class QLiteHtmlWidgetPrivate; - -class QLITEHTML_EXPORT QLiteHtmlWidget : public QAbstractScrollArea -{ - Q_OBJECT -public: - explicit QLiteHtmlWidget(QWidget *parent = nullptr); - ~QLiteHtmlWidget() override; - - // declaring the getters Q_INVOKABLE to make them Squish-testable - void setUrl(const QUrl &url); - Q_INVOKABLE QUrl url() const; - void setHtml(const QString &content); - Q_INVOKABLE QString html() const; - Q_INVOKABLE QString title() const; - - void setZoomFactor(qreal scale); - qreal zoomFactor() const; - - bool findText(const QString &text, - QTextDocument::FindFlags flags, - bool incremental, - bool *wrapped = nullptr); - - void setDefaultFont(const QFont &font); - QFont defaultFont() const; - - void scrollToAnchor(const QString &name); - - using ResourceHandler = std::function; - void setResourceHandler(const ResourceHandler &handler); - - // declaring this Q_INVOKABLE to make it Squish-testable - Q_INVOKABLE QString selectedText() const; - -signals: - void linkClicked(const QUrl &url); - void contextMenuRequested(const QPoint &pos, const QUrl &url); - -protected: - void paintEvent(QPaintEvent *event) override; - void resizeEvent(QResizeEvent *event) override; - void mouseMoveEvent(QMouseEvent *event) override; - void mousePressEvent(QMouseEvent *event) override; - void mouseReleaseEvent(QMouseEvent *event) override; - void mouseDoubleClickEvent(QMouseEvent *event) override; - void leaveEvent(QEvent *event) override; - void contextMenuEvent(QContextMenuEvent *event) override; - -private: - void withFixedTextPosition(const std::function &action); - void render(); - QPoint scrollPosition() const; - void htmlPos(const QPoint &pos, QPoint *viewportPos, QPoint *htmlPos) const; - QPoint toVirtual(const QPoint &p) const; - QSize toVirtual(const QSize &s) const; - QRect toVirtual(const QRect &r) const; - QRect fromVirtual(const QRect &r) const; - - QLiteHtmlWidgetPrivate *d; -};