diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 298b068..a01651f 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -17,84 +17,91 @@ jobs:
max-parallel: 1
matrix:
os: [ubuntu-20.04, macos-10.15]
- qt-version: [ '6.1.2' ]
- python-version: [ '3.8' ]
+ qt-version: ["6.2.2"]
+ python-version: ["3.8"]
steps:
- - uses: actions/checkout@v2
-
- - name: Cache Qt
- id: cache-qt
- uses: actions/cache@v2
- with:
- path: ${{ runner.workspace }}/Qt
- key: ${{ runner.os }}-Qt${{ matrix.qt-version }}-cache
-
- - name: Install Qt
- uses: jurplel/install-qt-action@v2.11.1
- with:
- version: ${{ matrix.qt-version }}
- cached: ${{ steps.cache-qt.outputs.cache-hit }}
- aqtversion: ==1.2.4 # <1.2.0 has problem with osx installation of Qt 6
-
- - name: Cache Python dependencies
- if: ${{ runner.os == 'Linux' }}
- uses: actions/cache@v2
- with:
- path: /home/runner/.cache/pypoetry/
- key: ${{ matrix.python-version }}-${{ hashFiles('**/poetry.lock') }}
-
- - name: Cache Python dependencies
- if: ${{ runner.os == 'macOS' }}
- uses: actions/cache@v2
- with:
- path: /Users/runner/Library/Caches/pypoetry/
- key: ${{ matrix.python-version }}-${{ hashFiles('**/poetry.lock') }}
-
- - name: Set up Python ${{ matrix.python-version }}
- uses: actions/setup-python@v2
- with:
- python-version: ${{ matrix.python-version }}
-
- - name: Install dependencies
- run: |
- if [ "${{ matrix.os }}" == "ubuntu-20.04" ]; then
- sudo apt-get install llvm
- fi
-
- python${{ matrix.python-version }} -m pip install --upgrade pip
- git clone --depth 1 --single-branch --branch 1.2.0a2 https://github.com/python-poetry/poetry.git
- pushd poetry
- python${{ matrix.python-version }} install-poetry.py --version 1.2.0a2
- popd
- rm -r poetry
- echo "$HOME/.local/bin" >> $GITHUB_PATH
-
- if [ "${{ runner.os }}" == "macOS" ]; then
- # is needed for macos to make poetry available
- export PATH="/Users/runner/.local/bin:$PATH"
-
- # qt on macos is installed in framework mode, includes like 'QtCore/QUrl' cannot be resolved by default
- ln -sf $Qt6_DIR/lib/QtCore.framework/Versions/A/Headers/* $Qt6_DIR/lib/QtCore.framework/Versions/A/Headers/${{ matrix.qt-version }}/QtCore/
- ln -sf $Qt6_DIR/lib/QtGui.framework/Versions/A/Headers/* $Qt6_DIR/lib/QtGui.framework/Versions/A/Headers/${{ matrix.qt-version }}/QtGui/
- ln -sf $Qt6_DIR/lib/QtNetwork.framework/Versions/A/Headers/* $Qt6_DIR/lib/QtNetwork.framework/Versions/A/Headers/${{ matrix.qt-version }}/QtNetwork/
- fi
- poetry env use ${{ matrix.python-version }}
- poetry install --verbose
- poetry run pip3 install PySide6 # temporary workaround, poetry can't install PySide6. TODO: investigate
- # git clone https://github.com/quotient-im/libQuotient.git PyQuotient/libQuotient
- git clone https://github.com/Aksem/libQuotient.git -b qt-6.1 PyQuotient/libQuotient
-
- - name: Build
- run: |
- poetry env use ${{ matrix.python-version }}
- mkdir -p build
- pushd build
- cmake -DBUILD_TESTING=OFF -DBUILD_WITH_QT6=ON -DQT_PATH=$Qt6_DIR -DPYTHON_ENV_VERSION=${{ matrix.python-version }} ../
- make
- popd
-
- - name: Run unit tests
- run: |
- poetry env use ${{ matrix.python-version }}
- poetry run python3 -m pytest
+ - uses: actions/checkout@v2
+
+ - name: Cache Qt
+ id: cache-qt
+ uses: actions/cache@v2
+ with:
+ path: ${{ runner.workspace }}/Qt
+ key: ${{ runner.os }}-Qt${{ matrix.qt-version }}-cache
+
+ - name: Install Qt
+ uses: jurplel/install-qt-action@v2.11.1
+ with:
+ version: ${{ matrix.qt-version }}
+ cached: ${{ steps.cache-qt.outputs.cache-hit }}
+ aqtversion: ==1.2.4 # <1.2.0 has problem with osx installation of Qt 6
+
+ - name: Cache Python dependencies
+ if: ${{ runner.os == 'Linux' }}
+ uses: actions/cache@v2
+ with:
+ path: /home/runner/.cache/pypoetry/
+ key: ${{ matrix.python-version }}-${{ hashFiles('**/poetry.lock') }}
+
+ - name: Cache Python dependencies
+ if: ${{ runner.os == 'macOS' }}
+ uses: actions/cache@v2
+ with:
+ path: /Users/runner/Library/Caches/pypoetry/
+ key: ${{ matrix.python-version }}-${{ hashFiles('**/poetry.lock') }}
+
+ - name: Set up Python ${{ matrix.python-version }}
+ uses: actions/setup-python@v2
+ with:
+ python-version: ${{ matrix.python-version }}
+
+ - name: Set up build environment(Linux)
+ if: ${{ runner.os == 'Linux' }}
+ run: |
+ # use gcc 10 on linux
+ CXX_VERSION_POSTFIX='-10'
+ echo "CC=gcc$CXX_VERSION_POSTFIX" >>$GITHUB_ENV
+ echo "CXX=g++$CXX_VERSION_POSTFIX" >>$GITHUB_ENV
+
+ - name: Install dependencies
+ run: |
+ if [ "${{ matrix.os }}" == "ubuntu-20.04" ]; then
+ sudo apt-get install llvm
+ fi
+
+ python${{ matrix.python-version }} -m pip install --upgrade pip
+ git clone --depth 1 --single-branch --branch 1.2.0a2 https://github.com/python-poetry/poetry.git
+ pushd poetry
+ python${{ matrix.python-version }} install-poetry.py --version 1.2.0a2
+ popd
+ rm -r poetry
+ echo "$HOME/.local/bin" >> $GITHUB_PATH
+
+ if [ "${{ runner.os }}" == "macOS" ]; then
+ # is needed for macos to make poetry available
+ export PATH="/Users/runner/.local/bin:$PATH"
+
+ # qt on macos is installed in framework mode, includes like 'QtCore/QUrl' cannot be resolved by default
+ ln -sf $Qt6_DIR/lib/QtCore.framework/Versions/A/Headers/* $Qt6_DIR/lib/QtCore.framework/Versions/A/Headers/${{ matrix.qt-version }}/QtCore/
+ ln -sf $Qt6_DIR/lib/QtGui.framework/Versions/A/Headers/* $Qt6_DIR/lib/QtGui.framework/Versions/A/Headers/${{ matrix.qt-version }}/QtGui/
+ ln -sf $Qt6_DIR/lib/QtNetwork.framework/Versions/A/Headers/* $Qt6_DIR/lib/QtNetwork.framework/Versions/A/Headers/${{ matrix.qt-version }}/QtNetwork/
+ fi
+ poetry env use ${{ matrix.python-version }}
+ poetry install --verbose
+ poetry run pip3 install PySide6 # temporary workaround, poetry can't install PySide6. TODO: investigate
+ git clone https://github.com/quotient-im/libQuotient.git PyQuotient/libQuotient
+
+ - name: Build
+ run: |
+ poetry env use ${{ matrix.python-version }}
+ mkdir -p build
+ pushd build
+ cmake -DBUILD_TESTING=OFF -DBUILD_WITH_QT6=ON -DQT_PATH=$Qt6_DIR -DPYTHON_ENV_VERSION=${{ matrix.python-version }} ../
+ make
+ popd
+
+ - name: Run unit tests
+ run: |
+ poetry env use ${{ matrix.python-version }}
+ poetry run python3 -m pytest
diff --git a/.gitignore b/.gitignore
index 4461b1b..2255fbd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,4 +10,6 @@ PyQuotient/libQuotient.a
__pycache__
.pytest_cache
-.vscode
\ No newline at end of file
+.vscode
+
+build.sh
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 4ce1eed..300326d 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -15,7 +15,7 @@ project(LibQuotientBindings)
option(USE_POETRY "Use poetry package manager" ON)
option(USE_PREBUILD_LIBQUOTIENT "Use prebuilt quotient" OFF)
SET(QT_PATH "" CACHE STRING "Path to qt directory")
-SET(PYTHON_ENV_VERSION "3.9" CACHE STRING "Python env version(e.g. 3.9)")
+SET(PYTHON_ENV_VERSION "3.8" CACHE STRING "Python env version(e.g. 3.8)")
set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} ${QT_PATH}/lib/cmake/Qt6 ${QT_PATH}/lib/cmake/)
# ================================ General configuration ======================================
@@ -439,6 +439,8 @@ quotient_timelineitem_wrapper.cpp
quotient_timelineitem_wrapper.h
quotient_pendingeventitem_wrapper.cpp
quotient_pendingeventitem_wrapper.h
+quotient_eventstatus_wrapper.h
+quotient_eventstatus_wrapper.cpp
)
# generated wrappers end
@@ -705,9 +707,9 @@ target_link_libraries(${bindings_library} PRIVATE Qt6::Core Qt6::Gui Qt6::Networ
add_library(pyside STATIC IMPORTED GLOBAL)
# TODO: change hardcoded name
if(UNIX AND NOT APPLE)
- SET_TARGET_PROPERTIES(pyside PROPERTIES IMPORTED_LOCATION ${pyside6_path}/libpyside6.abi3.so.6.1)
+ SET_TARGET_PROPERTIES(pyside PROPERTIES IMPORTED_LOCATION ${pyside6_path}/libpyside6.abi3.so.6.2)
else(APPLE)
- SET_TARGET_PROPERTIES(pyside PROPERTIES IMPORTED_LOCATION ${pyside6_path}/libpyside6.abi3.6.1.dylib)
+ SET_TARGET_PROPERTIES(pyside PROPERTIES IMPORTED_LOCATION ${pyside6_path}/libpyside6.abi3.6.2.dylib)
endif()
target_link_libraries(${bindings_library} PRIVATE pyside)
diff --git a/PyQuotient/__init__.py b/PyQuotient/__init__.py
index 288d78b..cbb12c8 100644
--- a/PyQuotient/__init__.py
+++ b/PyQuotient/__init__.py
@@ -1 +1,23 @@
+from enum import Flag
+from PySide6 import QtCore
from .PyQuotient import Quotient
+from __feature__ import snake_case, true_property
+
+
+class EventStatus(QtCore.QObject):
+
+ @QtCore.QFlag
+ class Code(Flag):
+ Normal = 0x00 #< No special designation
+ Submitted = 0x01 #< The event has just been submitted for sending
+ FileUploaded = 0x02 #< The file attached to the event has been
+ # uploaded to the server
+ Departed = 0x03 #< The event has left the client
+ ReachedServer = 0x04 #< The server has received the event
+ SendingFailed = 0x05 #< The server could not receive the event
+ Redacted = 0x08 #< The event has been redacted
+ Replaced = 0x10 #< The event has been replaced
+ Hidden = 0x100
+
+
+Quotient.EventStatus = EventStatus
diff --git a/PyQuotient/typesystems/jobs/typesystem_basejob.xml b/PyQuotient/typesystems/jobs/typesystem_basejob.xml
index b5f8fa5..6f17ec4 100644
--- a/PyQuotient/typesystems/jobs/typesystem_basejob.xml
+++ b/PyQuotient/typesystems/jobs/typesystem_basejob.xml
@@ -21,6 +21,10 @@
+
+
+
+
diff --git a/PyQuotient/typesystems/typesystem_index.xml b/PyQuotient/typesystems/typesystem_index.xml
index 6910cfd..e1456fc 100644
--- a/PyQuotient/typesystems/typesystem_index.xml
+++ b/PyQuotient/typesystems/typesystem_index.xml
@@ -4,6 +4,9 @@
+
+
+
diff --git a/PyQuotient/typesystems/typesystem_settings.xml b/PyQuotient/typesystems/typesystem_settings.xml
index a0154d7..85baa89 100644
--- a/PyQuotient/typesystems/typesystem_settings.xml
+++ b/PyQuotient/typesystems/typesystem_settings.xml
@@ -4,10 +4,10 @@
-
+
-
+
diff --git a/README.md b/README.md
index 7b80c56..edb004c 100644
--- a/README.md
+++ b/README.md
@@ -6,8 +6,8 @@
`poetry install`
- or use pip with qt repository `http://download.qt.io/official_releases/QtForPython/`(see dependencies and their versions in pyproject.toml) [More](https://doc.qt.io/qtforpython/shiboken6/gettingstarted.html):
-
+ or use pip with qt repository `http://download.qt.io/official_releases/QtForPython/`(see dependencies and their versions in pyproject.toml) [More](https://doc.qt.io/qtforpython/shiboken6/gettingstarted.html):
+
`pip3 install --index-url=http://download.qt.io/official_releases/QtForPython/ --trusted-host download.qt.io shiboken6 PySide6 shiboken6-generator`
2. Clone libQuotient:
@@ -29,7 +29,6 @@ Optional parameters:
**Note:**: deprecated API(properties, methods, etc) is not tested!
-
### Usage
After PyQuotient import use also `__feature__` import to be able to use API with snake case and true properties:
@@ -52,6 +51,8 @@ If you use pip, install development requirements:
Run tests:
-* If you use poetry: `poetry run python -m pytest`
+- If you use poetry: `poetry run python -m pytest`
+
+- otherwise: `python -m pytest`
-* otherwise: `python -m pytest`
+Update resources in demo client: `poetry run pyside6-rcc demo/resources.qrc -o demo/resources.py`
diff --git a/demo/chatroomwidget.py b/demo/chatroomwidget.py
new file mode 100644
index 0000000..eea4932
--- /dev/null
+++ b/demo/chatroomwidget.py
@@ -0,0 +1,60 @@
+from PySide6 import QtCore, QtWidgets, QtQuickWidgets, QtQml
+from PyQuotient import Quotient
+
+from demo.models.messageeventmodel import MessageEventModel
+
+from __feature__ import snake_case, true_property
+
+
+class ChatRoomWidget(QtWidgets.QWidget):
+ resourceRequested = QtCore.Signal()
+ roomSettingsRequested = QtCore.Signal()
+ showStatusMessage = QtCore.Signal()
+
+ def __init__(self, parent=None) -> None:
+ super().__init__(parent)
+
+ self.message_model = MessageEventModel(self)
+
+ QtQml.qmlRegisterUncreatableType(Quotient.Room, 'Quotient', 1, 0, 'Room', 'Room objects can only be created by libQuotient')
+ QtQml.qmlRegisterUncreatableType(Quotient.User, 'Quotient', 1, 0, 'User', 'User objects can only be created by libQuotient')
+ QtQml.qmlRegisterType(Quotient.GetRoomEventsJob, 'Quotient', 1, 0, 'GetRoomEventsJob')
+ QtQml.qmlRegisterType(MessageEventModel, 'Quotient', 1, 0, 'MessageEventModel')
+
+ QtQml.qmlRegisterType(Quotient.Settings, 'Quotient', 1, 0, 'Settings')
+
+ self.timeline_widget = QtQuickWidgets.QQuickWidget(self)
+ self.timeline_widget.size_policy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
+ self.timeline_widget.resize_mode = QtQuickWidgets.QQuickWidget.SizeRootObjectToView
+
+ self.timeline_widget.root_context().set_context_property("messageModel", self.message_model)
+ self.timeline_widget.root_context().set_context_property("controller", self)
+ self.timeline_widget.root_context().set_context_property("room", None)
+
+ self.timeline_widget.source = "qrc:///qml/Timeline.qml"
+
+ self.message_text_edit = QtWidgets.QTextEdit(self)
+ self.message_text_edit.maximum_height = self.maximum_chat_edit_height()
+ self.send_button = QtWidgets.QPushButton('Send', self)
+ self.send_button.clicked.connect(self.send_message)
+
+ self.h_layout = QtWidgets.QHBoxLayout()
+ self.h_layout.add_widget(self.message_text_edit)
+ self.h_layout.add_widget(self.send_button)
+
+ self.v_layout = QtWidgets.QVBoxLayout(self)
+ self.v_layout.add_widget(self.timeline_widget)
+ self.v_layout.add_layout(self.h_layout)
+ self.set_layout(self.v_layout)
+
+
+ @QtCore.Slot(str, bool)
+ def onMessageShownChanged(self, eventId: str, shown: bool): # name should be in camelCase, because it is used so in QML
+ ...
+
+ def maximum_chat_edit_height(self):
+ return self.maximum_height / 3
+
+ @QtCore.Slot()
+ def send_message(self):
+ ...
diff --git a/demo/mainwindow.py b/demo/mainwindow.py
index 10ac8bf..afde2e8 100644
--- a/demo/mainwindow.py
+++ b/demo/mainwindow.py
@@ -1,11 +1,12 @@
import math
+import demo.resources
from PySide6 import QtCore, QtWidgets, QtGui
from PyQuotient import Quotient
from demo.accountregistry import AccountRegistry
+from demo.chatroomwidget import ChatRoomWidget
from demo.logindialog import LoginDialog
from demo.roomlistdock import RoomListDock
-from demo.pyquaternionroom import PyquaternionRoom
from __feature__ import snake_case, true_property
@@ -25,6 +26,10 @@ def __init__(self):
self.room_list_dock.roomSelected.connect(self.select_room)
self.add_dock_widget(QtCore.Qt.LeftDockWidgetArea, self.room_list_dock)
+ self.chat_room_widget = ChatRoomWidget(self)
+ self.chat_room_widget.showStatusMessage.connect(lambda x = '': self.status_bar().show_message(x))
+ self.set_central_widget(self.chat_room_widget)
+
self.create_menu()
# Only GUI, account settings will be loaded in invoke_login
self.load_settings()
@@ -219,8 +224,8 @@ def show_millis_to_recon(self, connection: Quotient.Connection):
user=connection.local_user_id, seconds=math.ceil(connection.millis_to_reconnect)
))
- @QtCore.Slot(PyquaternionRoom)
- def select_room(self, room: PyquaternionRoom) -> None:
+ @QtCore.Slot(Quotient.Room)
+ def select_room(self, room: Quotient.Room) -> None:
if room is not None:
print(f'Opening room {room.object_name()}')
elif self.current_room is not None:
diff --git a/demo/models/messageeventmodel.py b/demo/models/messageeventmodel.py
new file mode 100644
index 0000000..0d3a8af
--- /dev/null
+++ b/demo/models/messageeventmodel.py
@@ -0,0 +1,87 @@
+from enum import Enum, auto
+from PySide6 import QtCore, QtQml
+from PyQuotient import Quotient
+from __feature__ import snake_case, true_property
+
+
+class EventRoles(Enum):
+ EventTypeRole = QtCore.Qt.UserRole + 1
+ EventIdRole = auto()
+ TimeRole = auto()
+ SectionRole = auto()
+ AboveSectionRole = auto()
+ AuthorRole = auto()
+ AboveAuthorRole = auto()
+ ContentRole = auto()
+ ContentTypeRole = auto()
+ HighlightRole = auto()
+ SpecialMarksRole = auto()
+ LongOperationRole = auto()
+ AnnotationRole = auto()
+ UserHueRole = auto()
+ RefRole = auto()
+ ReactionsRole = auto()
+ EventResolvedTypeRole = auto()
+
+
+class MessageEventModel(QtCore.QAbstractListModel):
+ def __init__(self, *args, **kwargs) -> None:
+ super().__init__(*args, **kwargs)
+
+ QtQml.qmlRegisterUncreatableType(Quotient.EventStatus, 'Quotient', 1, 0, 'EventStatus', 'EventStatus is not a creatable type')
+
+ def row_count(self, parent: QtCore.QModelIndex = QtCore.QModelIndex()):
+ # TODO: implement
+ return 2
+
+ def data(self, index: QtCore.QModelIndex, role: int):
+ row = index.row()
+
+ if not index.is_valid() or row >= self.row_count():
+ return {}
+
+ if role == QtCore.Qt.DisplayRole:
+ return 'it\'s a test message!'
+
+ if role == QtCore.Qt.ToolTipRole:
+ # return evt.originalJson()
+ return {}
+
+ if role == EventRoles.AuthorRole.value:
+ return {
+ 'avatarMediaId': 1 # TODO: user
+ }
+
+ if role == EventRoles.AnnotationRole.value:
+ return 'annotation'
+
+ if role == EventRoles.ReactionsRole.value:
+ return []
+
+ if role == EventRoles.SectionRole.value:
+ return 'date' # TODO: render_date()
+ return {}
+
+ @QtCore.Slot(result='QVariant')
+ def role_names(self):
+ roles = super().role_names()
+
+ roles[EventRoles.EventTypeRole.value] = b'eventType'
+ roles[EventRoles.EventIdRole.value] = b'eventId'
+ roles[EventRoles.TimeRole.value] = b'time'
+ roles[EventRoles.SectionRole.value] = b'section'
+ roles[EventRoles.AboveSectionRole.value] = b'aboveSection'
+ roles[EventRoles.AuthorRole.value] = b'author'
+ roles[EventRoles.AboveAuthorRole.value] = b'aboveAuthor'
+ roles[EventRoles.ContentRole.value] = b'content'
+ roles[EventRoles.ContentTypeRole.value] = b'contentType'
+ roles[EventRoles.HighlightRole.value] = b'highlight'
+ roles[EventRoles.SpecialMarksRole.value] = b'marks'
+ roles[EventRoles.LongOperationRole.value] = b'progressInfo'
+ roles[EventRoles.AnnotationRole.value] = b'annotation'
+ roles[EventRoles.UserHueRole.value] = b'userHue'
+ roles[EventRoles.EventResolvedTypeRole.value] = b'eventResolvedType'
+ roles[EventRoles.RefRole.value] = b'refId'
+ roles[EventRoles.ReactionsRole.value] = b'reactions'
+
+ return roles
diff --git a/demo/qml/AnimatedTransition.qml b/demo/qml/AnimatedTransition.qml
new file mode 100644
index 0000000..4168012
--- /dev/null
+++ b/demo/qml/AnimatedTransition.qml
@@ -0,0 +1,7 @@
+import QtQuick 2.0
+import Quotient 1.0
+
+Transition {
+ property var settings: TimelineSettings { }
+ enabled: settings.enable_animations
+}
diff --git a/demo/qml/AnimationBehavior.qml b/demo/qml/AnimationBehavior.qml
new file mode 100644
index 0000000..7ed7926
--- /dev/null
+++ b/demo/qml/AnimationBehavior.qml
@@ -0,0 +1,7 @@
+import QtQuick 2.0
+import Quotient 1.0
+
+Behavior {
+ property var settings: TimelineSettings { }
+ enabled: settings.enable_animations
+}
diff --git a/demo/qml/Attachment.qml b/demo/qml/Attachment.qml
new file mode 100644
index 0000000..e59d401
--- /dev/null
+++ b/demo/qml/Attachment.qml
@@ -0,0 +1,49 @@
+import QtQuick 2.0
+import Quotient 1.0
+
+Item {
+ width: parent.width
+ height: visible ? childrenRect.height : 0
+
+ property bool openOnFinished: false
+ readonly property bool downloaded: progressInfo &&
+ !progressInfo.isUpload && progressInfo.completed
+
+ onDownloadedChanged: {
+ if (downloaded && openOnFinished)
+ openLocalFile()
+ }
+
+ function openExternally()
+ {
+ if (progressInfo.localPath.toString() || downloaded)
+ openLocalFile()
+ else
+ {
+ openOnFinished = true
+ room.downloadFile(eventId)
+ }
+ }
+
+ function openLocalFile()
+ {
+ if (Qt.openUrlExternally(progressInfo.localPath))
+ return;
+
+ controller.showStatusMessage(
+ "Couldn't determine how to open the file, " +
+ "opening its folder instead", 5000)
+
+ if (Qt.openUrlExternally(progressInfo.localDir))
+ return;
+
+ controller.showStatusMessage(
+ "Couldn't determine how to open the file or its folder.",
+ 5000)
+ }
+
+ Connections {
+ target: controller
+ onOpenExternally: if (currentIndex === index) openExternally()
+ }
+}
diff --git a/demo/qml/AuthorInteractionArea.qml b/demo/qml/AuthorInteractionArea.qml
new file mode 100644
index 0000000..d26a463
--- /dev/null
+++ b/demo/qml/AuthorInteractionArea.qml
@@ -0,0 +1,15 @@
+TimelineMouseArea {
+ property var authorId
+
+ enabled: parent.visible
+ anchors.fill: parent
+ cursorShape: Qt.PointingHandCursor
+ acceptedButtons: Qt.LeftButton|Qt.MiddleButton
+ hoverEnabled: true
+ onEntered: controller.showStatusMessage(authorId)
+ onExited: controller.showStatusMessage("")
+ onClicked:
+ controller.resourceRequested(authorId,
+ mouse.button === Qt.LeftButton
+ ? "mention" : "_interactive")
+}
diff --git a/demo/qml/FastNumberAnimation.qml b/demo/qml/FastNumberAnimation.qml
new file mode 100644
index 0000000..a10368d
--- /dev/null
+++ b/demo/qml/FastNumberAnimation.qml
@@ -0,0 +1,7 @@
+import QtQuick 2.0
+import Quotient 1.0
+
+NumberAnimation {
+ property var settings: TimelineSettings { }
+ duration: settings.fast_animations_duration_ms
+}
diff --git a/demo/qml/ImageContent.qml b/demo/qml/ImageContent.qml
new file mode 100644
index 0000000..1f4f499
--- /dev/null
+++ b/demo/qml/ImageContent.qml
@@ -0,0 +1,47 @@
+import QtQuick 2.0
+// import QtQuick.Controls 1.4
+// import QtQuick.Layouts 1.1
+
+Attachment {
+ property var sourceSize
+ property url source
+ property var maxHeight
+ property bool autoload
+
+ // Image {
+ // id: imageContent
+ // width: parent.width
+ // height: sourceSize.height *
+ // Math.min(maxHeight / sourceSize.height * 0.9,
+ // Math.min(width / sourceSize.width, 1))
+ // fillMode: Image.PreserveAspectFit
+ // horizontalAlignment: Image.AlignLeft
+
+ // source: parent.source
+ // sourceSize: parent.sourceSize
+
+ // TimelineMouseArea {
+ // anchors.fill: parent
+ // acceptedButtons: Qt.LeftButton
+ // hoverEnabled: true
+
+ // onContainsMouseChanged:
+ // controller.showStatusMessage(containsMouse
+ // ? room.fileSource(eventId) : "")
+ // onClicked: openExternally()
+ // }
+
+ // TimelineMouseArea {
+ // anchors.fill: parent
+ // acceptedButtons: Qt.RightButton
+ // cursorShape: Qt.PointingHandCursor
+ // onClicked: controller.showMenu(index, textFieldImpl.hoveredLink,
+ // textFieldImpl.selectedText, showingDetails)
+ // }
+
+ // Component.onCompleted:
+ // if (visible && autoload && !downloaded && !(progressInfo && progressInfo.isUpload))
+ // room.downloadFile(eventId)
+ // }
+
+}
diff --git a/demo/qml/MyToolTip.qml b/demo/qml/MyToolTip.qml
new file mode 100644
index 0000000..2beeead
--- /dev/null
+++ b/demo/qml/MyToolTip.qml
@@ -0,0 +1,32 @@
+import QtQuick 2.0
+import QtQuick.Controls 2.1
+import Quotient 1.0
+
+ToolTip {
+ id:tooltip
+ TimelineSettings { id: settings }
+
+ padding: 4
+ font: settings.font
+
+ background: Rectangle {
+ SystemPalette { id: palette; colorGroup: SystemPalette.Active }
+ radius: 3
+ color: palette.window
+ border.width: 1
+ border.color: palette.windowText
+ }
+ enter: AnimatedTransition { NormalNumberAnimation {
+ target: tooltip
+ property: "opacity"
+ easing.type: Easing.OutQuad
+ from: 0
+ to: 0.9
+ } }
+ exit: AnimatedTransition { FastNumberAnimation {
+ target: tooltip
+ property: "opacity"
+ easing.type: Easing.InQuad
+ to: 0
+ } }
+}
diff --git a/demo/qml/NormalNumberAnimation.qml b/demo/qml/NormalNumberAnimation.qml
new file mode 100644
index 0000000..de203c9
--- /dev/null
+++ b/demo/qml/NormalNumberAnimation.qml
@@ -0,0 +1,7 @@
+import QtQuick 2.0
+import Quotient 1.0
+
+NumberAnimation {
+ property var settings: TimelineSettings { }
+ duration: settings.animations_duration_ms
+}
diff --git a/demo/qml/ScrollToButton.qml b/demo/qml/ScrollToButton.qml
new file mode 100644
index 0000000..f438f2c
--- /dev/null
+++ b/demo/qml/ScrollToButton.qml
@@ -0,0 +1,23 @@
+import QtQuick 2.9
+import QtQuick.Controls 2.2
+
+RoundButton {
+ height: settings.fontHeight * 2
+ width: height
+ hoverEnabled: true
+ opacity: visible * (0.7 + hovered * 0.2)
+
+ display: Button.IconOnly
+ icon.color: defaultPalette.buttonText
+
+ AnimationBehavior on opacity {
+ NormalNumberAnimation {
+ easing.type: Easing.OutQuad
+ }
+ }
+ AnimationBehavior on anchors.bottomMargin {
+ NormalNumberAnimation {
+ easing.type: Easing.OutQuad
+ }
+ }
+}
diff --git a/demo/qml/Timeline.qml b/demo/qml/Timeline.qml
new file mode 100644
index 0000000..4f0ca83
--- /dev/null
+++ b/demo/qml/Timeline.qml
@@ -0,0 +1,741 @@
+import QtQuick 2.4
+import QtQuick.Controls 2.2
+import QtQuick.Layouts 1.1
+// import QtGraphicalEffects 1.0 // For fancy highlighting
+import Quotient 1.0
+
+Rectangle {
+ id: root
+
+ TimelineSettings {
+ id: settings
+ readonly property bool use_shuttle_dial: value("UI/use_shuttle_dial", true)
+
+ Component.onCompleted: console.log("Using timeline font: " + font)
+ }
+ SystemPalette { id: defaultPalette; colorGroup: SystemPalette.Active }
+ SystemPalette { id: disabledPalette; colorGroup: SystemPalette.Disabled }
+
+ color: defaultPalette.base
+ radius: 2
+
+ function humanSize(bytes)
+ {
+ if (!bytes)
+ return qsTr("Unknown", "Unknown attachment size")
+ if (bytes < 4000)
+ return qsTr("%Ln byte(s)", "", bytes)
+ bytes = Math.round(bytes / 100) / 10
+ if (bytes < 2000)
+ return qsTr("%L1 kB").arg(bytes)
+ bytes = Math.round(bytes / 100) / 10
+ if (bytes < 2000)
+ return qsTr("%L1 MB").arg(bytes)
+ return qsTr("%L1 GB").arg(Math.round(bytes / 100) / 10)
+ }
+
+ function mixColors(baseColor, mixedColor, mixRatio)
+ {
+ return Qt.tint(baseColor,
+ Qt.rgba(mixedColor.r, mixedColor.g, mixedColor.b, mixRatio))
+ }
+
+ Rectangle {
+ id: roomHeader
+
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.top: parent.top
+ height: headerText.height + 5
+
+ color: defaultPalette.window
+ border.color: disabledPalette.windowText
+ radius: 2
+ visible: room
+
+ Image {
+ id: roomAvatar
+ anchors.verticalCenter: headerText.verticalCenter
+ anchors.left: parent.left
+ anchors.margins: 2
+ height: headerText.height
+ // implicitWidth on its own doesn't respect the scale down of
+ // the received image (that almost always happens)
+ width: Math.min(headerText.height / implicitHeight * implicitWidth,
+ parent.width / 2.618) // Golden ratio - just for fun
+
+ source: room && room.avatarMediaId
+ ? "image://mtx/" + room.avatarMediaId : ""
+ // Safe upper limit (see also topicField)
+ sourceSize: Qt.size(-1, settings.lineSpacing * 9)
+
+ fillMode: Image.PreserveAspectFit
+
+ AnimationBehavior on width {
+ NormalNumberAnimation { easing.type: Easing.OutQuad }
+ }
+ }
+
+ Column {
+ id: headerText
+ anchors.left: roomAvatar.right
+ anchors.right: versionActionButton.left
+ anchors.top: parent.top
+ anchors.margins: 2
+
+ spacing: 2
+
+ TextEdit {
+ id: roomName
+ width: roomNameMetrics.advanceWidth
+ height: roomNameMetrics.height
+ clip: true
+
+ readonly property bool hasName: room && room.displayName !== ""
+ TextMetrics {
+ id: roomNameMetrics
+ font: roomName.font
+ elide: Text.ElideRight
+ elideWidth: headerText.width
+ text: roomName.hasName ? room.displayName : qsTr("(no name)")
+ }
+
+ text: roomNameMetrics.elidedText
+ color: (hasName ? defaultPalette : disabledPalette).windowText
+
+ font.bold: true
+ font.family: settings.font.family
+ font.pointSize: settings.font.pointSize
+ renderType: settings.render_type
+ readOnly: true
+ selectByKeyboard: true
+ selectByMouse: true
+
+ ToolTipArea {
+ enabled: roomName.hasName &&
+ (roomNameMetrics.text != roomNameMetrics.elidedText
+ || roomName.lineCount > 1)
+ text: room ? room.htmlSafeDisplayName : ""
+ }
+ }
+
+ Label {
+ id: versionNotice
+ visible: room && (room.isUnstable || room.successorId !== "")
+ width: parent.width
+
+ text: !room ? "" :
+ room.successorId !== ""
+ ? qsTr("This room has been upgraded.") :
+ room.isUnstable ? qsTr("Unstable room version!") : ""
+ elide: Text.ElideRight
+ font.italic: true
+ font.family: settings.font.family
+ font.pointSize: settings.font.pointSize
+ renderType: settings.render_type
+ ToolTipArea {
+ enabled: parent.truncated
+ text: parent.text
+ }
+ }
+ ScrollView {
+ id: topicField
+ width: parent.width
+ // Allow 6 lines of the topic (or 20% of the vertical space);
+ // if there are more than 6 lines, show half-line as a hint
+ height: Math.min(topicText.contentHeight, root.height / 5,
+ settings.lineSpacing * 6.5)
+ clip: true
+
+ ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
+ ScrollBar.vertical.policy: ScrollBar.AsNeeded
+
+ AnimationBehavior on height {
+ NormalNumberAnimation { easing.type: Easing.OutQuad }
+ }
+
+ // FIXME: The below TextEdit+MouseArea is a massive copy-paste
+ // from TimelineItem.qml. We need to make a separate component
+ // for these (RichTextField?).
+ TextEdit {
+ id: topicText
+ width: topicField.width
+
+ readonly property bool hasTopic: room && room.topic !== ""
+ text: hasTopic
+ ? room.prettyPrint(room.topic) : qsTr("(no topic)")
+ color:
+ (hasTopic ? defaultPalette : disabledPalette).windowText
+ textFormat: TextEdit.RichText
+ font: settings.font
+ renderType: settings.render_type
+ readOnly: true
+ selectByKeyboard: true;
+ selectByMouse: true;
+ wrapMode: TextEdit.Wrap
+
+ onHoveredLinkChanged:
+ controller.showStatusMessage(hoveredLink)
+
+ onLinkActivated: controller.resourceRequested(link)
+ }
+ }
+ }
+ MouseArea {
+ anchors.fill: headerText
+ acceptedButtons: Qt.MiddleButton
+ cursorShape: topicText.hoveredLink
+ ? Qt.PointingHandCursor : Qt.IBeamCursor
+
+ onClicked: {
+ if (topicText.hoveredLink)
+ controller.resourceRequested(topicText.hoveredLink,
+ "_interactive")
+ }
+ }
+ Button {
+ id: versionActionButton
+ visible: room && ((room.isUnstable && room.canSwitchVersions())
+ || room.successorId !== "")
+ anchors.verticalCenter: headerText.verticalCenter
+ anchors.right: parent.right
+ width: visible * implicitWidth
+ text: !room ? "" : room.successorId !== ""
+ ? qsTr("Go to\nnew room") : qsTr("Room\nsettings")
+
+ onClicked:
+ if (room.successorId !== "")
+ controller.resourceRequested(room.successorId, "join")
+ else
+ controller.roomSettingsRequested()
+ }
+ }
+
+ DropArea {
+ anchors.fill: parent
+ onEntered: if (!room) drag.accepted = false
+ onDropped: {
+ if (drop.hasUrls) {
+ controller.fileDrop(drop.urls)
+ drop.acceptProposedAction()
+ } else if (drop.hasHtml) {
+ controller.htmlDrop(drop.html)
+ drop.acceptProposedAction()
+ } else if (drop.hasText) {
+ controller.textDrop(drop.text)
+ drop.acceptProposedAction()
+ }
+ }
+ }
+ ScrollView {
+ id: chatScrollView
+ anchors.top: roomHeader.bottom
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.bottom: parent.bottom
+ clip: true
+ ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
+ ScrollBar.vertical.policy:
+ settings.use_shuttle_dial ? ScrollBar.AlwaysOff
+ : ScrollBar.AsNeeded
+ ScrollBar.vertical.interactive: true
+ ScrollBar.vertical.active: true
+// ScrollBar.vertical.background: Item { /* TODO: timeline map */ }
+
+ ListView {
+ id: chatView
+
+ model: messageModel
+ delegate: TimelineItem {
+ width: chatView.width - scrollerArea.width
+ view: chatView
+ moving: chatView.moving || shuttleDial.value
+ // #737; the solution found in
+ // https://bugreports.qt.io/browse/QT3DS-784
+ ListView.delayRemove: true
+ }
+ verticalLayoutDirection: ListView.BottomToTop
+ flickableDirection: Flickable.VerticalFlick
+ flickDeceleration: 8000
+ boundsBehavior: Flickable.StopAtBounds
+// pixelAligned: true // Causes false-negatives in atYEnd
+ cacheBuffer: 200
+
+ section.property: "section"
+
+ readonly property int bottommostVisibleIndex: count > 0 ?
+ atYEnd ? 0 : indexAt(contentX, contentY + height - 1) : -1
+ readonly property bool noNeedMoreContent:
+ !room || room.eventsHistoryJob || room.allHistoryLoaded
+
+ /// The number of events per height unit - always positive
+ readonly property real eventDensity:
+ contentHeight > 0 && count > 0 ? count / contentHeight : 0.03
+ // 0.03 is just an arbitrary reasonable number
+
+ property int lastRequestedEvents: 0
+ readonly property int currentRequestedEvents:
+ room && room.eventsHistoryJob ? lastRequestedEvents : 0
+
+ property var textEditWithSelection
+ property real readMarkerContentPos: originY
+ readonly property real readMarkerViewportPos:
+ readMarkerContentPos < contentY ? 0 :
+ readMarkerContentPos > contentY + height ? height + readMarkerLine.height :
+ readMarkerContentPos - contentY
+
+ function parkReadMarker() {
+ readMarkerContentPos = Qt.binding(function() {
+ return !messageModel || messageModel.readMarkerVisualIndex
+ > indexAt(contentX, contentY)
+ ? originY : contentY + contentHeight
+ })
+ console.log("Read marker parked at index",
+ messageModel.readMarkerVisualIndex
+ + ", content pos", chatView.readMarkerContentPos,
+ "(full range is", chatView.originY, "-",
+ chatView.originY + chatView.contentHeight,
+ "as of now)")
+ }
+
+ function ensurePreviousContent() {
+ if (noNeedMoreContent)
+ return
+
+ // Take the current speed, or assume we can scroll 8 screens/s
+ var velocity = moving ? -verticalVelocity :
+ cruisingAnimation.running ?
+ cruisingAnimation.velocity :
+ chatScrollView.height * 8
+ // Check if we're about to bump into the ceiling in
+ // 2 seconds and if yes, request the amount of messages
+ // enough to scroll at this rate for 3 more seconds
+ if (velocity > 0 && contentY - velocity*2 < originY) {
+ lastRequestedEvents = velocity * eventDensity * 3
+ room.getPreviousContent(lastRequestedEvents)
+ }
+ }
+ onContentYChanged: ensurePreviousContent()
+ onContentHeightChanged: ensurePreviousContent()
+
+ function saveViewport() {
+ if (room)
+ room.saveViewport(indexAt(contentX, contentY),
+ bottommostVisibleIndex)
+ }
+
+ function onModelReset() {
+ console.log("Model timeline reset")
+ if (room)
+ {
+ forceLayout()
+ // Load events if there are not enough of them
+ ensurePreviousContent()
+ var lastScrollPosition = room.savedTopVisibleIndex()
+ console.log("Scrolling to position", lastScrollPosition)
+ positionViewAtIndex(lastScrollPosition, ListView.Contain)
+ }
+ }
+
+ function scrollUp(dy) {
+ if (contentHeight > height)
+ contentY = Math.max(originY, contentY - dy)
+ }
+ function scrollDown(dy) {
+ if (contentHeight > height)
+ contentY = Math.min(originY + contentHeight - height,
+ contentY + dy)
+ }
+
+ function onWheel(wheel) {
+ if (wheel.angleDelta.x === 0) {
+ var yDelta = wheel.angleDelta.y * 10 / 36
+
+ if (yDelta > 0)
+ scrollUp(yDelta)
+ else
+ scrollDown(-yDelta)
+ wheel.accepted = true
+ } else {
+ wheel.accepted = false
+ }
+ }
+ Connections {
+ target: controller
+ // TODO
+ // onPageUpPressed:
+ // chatView.scrollUp(chatView.height
+ // - sectionBanner.childrenRect.height)
+
+ // onPageDownPressed:
+ // chatView.scrollDown(chatView.height
+ // - sectionBanner.childrenRect.height)
+ // onScrollViewTo: chatView.positionViewAtIndex(currentIndex, ListView.Contain)
+ }
+
+ Component.onCompleted: {
+ console.log("QML view loaded")
+ model.modelAboutToBeReset.connect(parkReadMarker)
+ // FIXME: This is not on the right place: ListView may or
+ // may not have updated his structures according to the new
+ // model by now
+ model.modelReset.connect(onModelReset)
+ }
+
+ onMovementEnded: saveViewport()
+
+ populate: AnimatedTransition {
+ // FastNumberAnimation { property: "opacity"; from: 0; to: 1 }
+ }
+
+ add: AnimatedTransition {
+ // FastNumberAnimation { property: "opacity"; from: 0; to: 1 }
+ }
+
+ move: AnimatedTransition {
+ // FastNumberAnimation { property: "y"; }
+ // FastNumberAnimation { property: "opacity"; to: 1 }
+ }
+
+ displaced: AnimatedTransition {
+ // FastNumberAnimation {
+ // property: "y";
+ // easing.type: Easing.OutQuad
+ // }
+ // FastNumberAnimation { property: "opacity"; to: 1 }
+ }
+
+ Behavior on contentY {
+ enabled: settings.enable_animations && !chatView.moving
+ && !cruisingAnimation.running
+ // SmoothedAnimation {
+ // id: scrollAnimation
+ // duration: settings.fast_animations_duration_ms / 3
+ // maximumEasingTime: settings.fast_animations_duration_ms
+
+ // onRunningChanged: {
+ // if (!running)
+ // chatView.saveViewport()
+ // }
+ // }
+ }
+
+ AnimationBehavior on readMarkerContentPos {
+ // NormalNumberAnimation { easing.type: Easing.OutQuad }
+ }
+
+ // This covers the area above the items if there are not enough
+ // of them to fill the viewport
+ MouseArea {
+ z: -1
+ anchors.fill: parent
+ acceptedButtons: Qt.AllButtons
+ onReleased: controller.focusInput()
+ }
+
+ Rectangle {
+ id: readShade
+
+ visible: chatView.count > 0
+ anchors.top: parent.top
+ anchors.topMargin: chatView.originY > chatView.contentY
+ ? chatView.originY - chatView.contentY : 0
+ /// At the bottom of the read shade is the read marker. If
+ /// the last read item is on the screen, the read marker is at
+ /// the item's bottom; otherwise, it's just beyond the edge of
+ /// chatView in the direction of the read marker index (or the
+ /// timeline, if the timeline is short enough).
+ /// @sa readMarkerViewportPos
+ height: chatView.readMarkerViewportPos - anchors.topMargin
+ anchors.left: parent.left
+ width: readMarkerLine.width
+ z: -1
+ opacity: 0.1
+
+ radius: readMarkerLine.height
+ color: mixColors(disabledPalette.base, defaultPalette.highlight, 0.5)
+ }
+ Rectangle {
+ id: readMarkerLine
+
+ visible: chatView.count > 0
+ width: parent.width - scrollerArea.width
+ anchors.bottom: readShade.bottom
+ height: 4
+ z: 2.5 // On top of any ListView content, below the banner
+
+ gradient: Gradient {
+ GradientStop { position: 0; color: "transparent" }
+ GradientStop { position: 1; color: defaultPalette.highlight }
+ }
+ }
+
+
+ // itemAt is a function rather than a property, so it doesn't
+ // produce a QML binding; the piece with contentHeight compensates.
+ readonly property var underlayingItem: contentHeight >= height &&
+ itemAt(contentX, contentY + sectionBanner.height - 2)
+ readonly property bool sectionBannerVisible: underlayingItem &&
+ (!underlayingItem.sectionVisible || underlayingItem.y < contentY)
+
+ Rectangle {
+ id: sectionBanner
+ z: 3 // On top of ListView sections that have z=2
+ anchors.left: parent.left
+ anchors.top: parent.top
+ width: childrenRect.width + 2
+ height: childrenRect.height + 2
+ visible: chatView.sectionBannerVisible
+ color: defaultPalette.window
+ opacity: 0.8
+ Label {
+ font.bold: true
+ font.family: settings.font.family
+ font.pointSize: settings.font.pointSize
+ opacity: 0.8
+ renderType: settings.render_type
+ text: chatView.underlayingItem ?
+ chatView.underlayingItem.ListView.section : ""
+ }
+ }
+ }
+ }
+
+ // === Timeline map ===
+ // Only used with the shuttle scroller for now
+
+ Rectangle {
+ id: cachedEventsBar
+
+ // A proxy property for animation
+ property int requestedHistoryEventsCount:
+ chatView.currentRequestedEvents
+ AnimationBehavior on requestedHistoryEventsCount {
+ // NormalNumberAnimation { }
+ }
+
+ property real averageEvtHeight:
+ chatView.count + requestedHistoryEventsCount > 0
+ ? chatView.height
+ / (chatView.count + requestedHistoryEventsCount)
+ : 0
+ AnimationBehavior on averageEvtHeight {
+ // FastNumberAnimation { }
+ }
+
+ anchors.horizontalCenter: shuttleDial.horizontalCenter
+ anchors.bottom: chatScrollView.bottom
+ anchors.bottomMargin:
+ averageEvtHeight * chatView.bottommostVisibleIndex
+ width: shuttleDial.backgroundWidth / 2
+ height: chatView.bottommostVisibleIndex < 0 ? 0 :
+ averageEvtHeight
+ * (chatView.count - chatView.bottommostVisibleIndex)
+ visible: shuttleDial.visible
+
+ color: defaultPalette.mid
+ }
+ Rectangle {
+ // Loading history events bar, stacked above
+ // the cached events bar when more history has been requested
+ anchors.right: cachedEventsBar.right
+ anchors.top: chatScrollView.top
+ anchors.bottom: cachedEventsBar.top
+ width: cachedEventsBar.width
+ visible: shuttleDial.visible
+
+ opacity: 0.4
+ color: defaultPalette.mid
+ }
+
+ // === Scrolling extensions ===
+
+ Slider {
+ id: shuttleDial
+ orientation: Qt.Vertical
+ height: chatScrollView.height * 0.7
+ width: chatScrollView.ScrollBar.vertical.width
+ padding: 2
+ anchors.right: parent.right
+ anchors.rightMargin: (background.width - width) / 2
+ anchors.verticalCenter: chatScrollView.verticalCenter
+ enabled: settings.use_shuttle_dial
+ visible: enabled && chatView.count > 0
+
+ readonly property real backgroundWidth:
+ handle.width + leftPadding + rightPadding
+ // Npages/sec = value^2 => maxNpages/sec = 9
+ readonly property real maxValue: 3.0
+ readonly property real deviation:
+ value / (maxValue * 2) * availableHeight
+
+ background: Item {
+ x: shuttleDial.handle.x - shuttleDial.leftPadding
+ width: shuttleDial.backgroundWidth
+ Rectangle {
+ id: springLine
+ // Rectangles (normally) have (x,y) as their top-left corner.
+ // To draw the "spring" line up from the middle point, its `y`
+ // should still be the top edge, not the middle point.
+ y: shuttleDial.height / 2 - Math.max(shuttleDial.deviation, 0)
+ height: Math.abs(shuttleDial.deviation)
+ anchors.horizontalCenter: parent.horizontalCenter
+ width: 2
+ color: defaultPalette.highlight
+ }
+ }
+ opacity: scrollerArea.containsMouse ? 1 : 0.7
+ AnimationBehavior on opacity {
+ // FastNumberAnimation { }
+ }
+
+ from: -maxValue
+ to: maxValue
+
+ activeFocusOnTab: false
+
+ onPressedChanged: {
+ if (!pressed) {
+ value = 0
+ controller.focusInput()
+ }
+ }
+
+ // This is not an ordinary animation, it's the engine that makes
+ // the shuttle dial work; for that reason it's not governed by
+ // settings.enable_animations and only can be disabled together with
+ // the shuttle dial.
+ SmoothedAnimation {
+ id: cruisingAnimation
+ target: chatView
+ property: "contentY"
+ velocity: shuttleDial.value * shuttleDial.value * chatView.height
+ maximumEasingTime: settings.animations_duration_ms
+ to: chatView.originY + (shuttleDial.value > 0 ? 0 :
+ chatView.contentHeight - chatView.height)
+ running: shuttleDial.value != 0
+
+ onStopped: chatView.saveViewport()
+ }
+
+ // Animations don't update `to` value when they are running; so
+ // when the shuttle value changes sign without becoming zero (which,
+ // turns out, is quite usual when dragging the shuttle around) the
+ // animation has to be restarted.
+ onValueChanged: cruisingAnimation.restart()
+ Component.onCompleted: { // same reason as above
+ chatView.originYChanged.connect(cruisingAnimation.restart)
+ chatView.contentHeightChanged.connect(cruisingAnimation.restart)
+ }
+ }
+
+ MouseArea {
+ id: scrollerArea
+ anchors.top: chatScrollView.top
+ anchors.bottom: chatScrollView.bottom
+ anchors.right: parent.right
+ width: settings.use_shuttle_dial
+ ? shuttleDial.backgroundWidth
+ : chatScrollView.ScrollBar.vertical.width
+ acceptedButtons: Qt.NoButton
+
+ hoverEnabled: true
+ }
+
+ Rectangle {
+ id: timelineStats
+ anchors.right: scrollerArea.left
+ anchors.top: chatScrollView.top
+ width: childrenRect.width + 3
+ height: childrenRect.height + 3
+ color: defaultPalette.alternateBase
+ property bool shown:
+ (chatView.bottommostVisibleIndex >= 0)
+ // && (scrollerArea.containsMouse || scrollAnimation.running))
+ || chatView.currentRequestedEvents > 0
+
+ onShownChanged: {
+ if (shown) {
+ fadeOutDelay.stop()
+ opacity = 0.8
+ } else
+ fadeOutDelay.restart()
+ }
+ Timer {
+ id: fadeOutDelay
+ interval: 2000
+ onTriggered: parent.opacity = 0
+ }
+
+ AnimationBehavior on opacity {
+ // FastNumberAnimation { }
+ }
+
+ Label {
+ font.bold: true
+ font.family: settings.font.family
+ font.pointSize: settings.font.pointSize
+ opacity: 0.8
+ renderType: settings.render_type
+ text: (chatView.count > 0
+ ? (chatView.bottommostVisibleIndex === 0
+ ? qsTr("Latest events")
+ : qsTr("%Ln events back from now","",
+ chatView.bottommostVisibleIndex))
+ + "\n" + qsTr("%Ln events cached", "", chatView.count)
+ : "")
+ + (chatView.currentRequestedEvents > 0
+ ? (chatView.count > 0 ? "\n" : "")
+ + qsTr("%Ln events requested from the server",
+ "", chatView.currentRequestedEvents)
+ : "")
+ horizontalAlignment: Label.AlignRight
+ }
+ }
+
+ ScrollToButton {
+ id: scrollToBottomButton
+
+ anchors.right: scrollerArea.left
+ anchors.rightMargin: 2
+ anchors.bottom: parent.bottom
+ anchors.bottomMargin: visible ? 0.5 * height : -height
+
+ visible: !chatView.atYEnd
+
+ icon {
+ name: "go-bottom"
+ source: "qrc:///scrolldown.svg"
+ }
+
+ onClicked: {
+ chatView.positionViewAtBeginning()
+ chatView.saveViewport()
+ }
+ }
+
+ ScrollToButton {
+ id: scrollToReaderMarkerButton
+
+ anchors.right: scrollerArea.left
+ anchors.rightMargin: 2
+ anchors.bottom: scrollToBottomButton.top
+ anchors.bottomMargin: visible ? 0.5 * height : -3 * height
+
+ visible: chatView.count > 1 &&
+ messageModel.readMarkerVisualIndex > 0 &&
+ messageModel.readMarkerVisualIndex
+ > chatView.indexAt(chatView.contentX, chatView.contentY)
+
+ icon {
+ name: "go-top"
+ source: "qrc:///scrollup.svg"
+ }
+
+ onClicked: {
+ chatView.positionViewAtIndex(messageModel.readMarkerVisualIndex,
+ ListView.Center)
+ chatView.saveViewport()
+ }
+ }
+}
diff --git a/demo/qml/TimelineItem.qml b/demo/qml/TimelineItem.qml
new file mode 100644
index 0000000..6aafc83
--- /dev/null
+++ b/demo/qml/TimelineItem.qml
@@ -0,0 +1,698 @@
+import QtQuick 2.6
+import QtQuick.Controls 2.2
+// import QtGraphicalEffects 1.0 // For fancy highlighting
+import Quotient 1.0
+
+Item {
+ // Supplementary components
+
+ TimelineSettings {
+ id: settings
+ readonly property bool autoload_images: value("UI/autoload_images", true)
+ readonly property string highlight_mode: value("UI/highlight_mode", "background")
+ readonly property color highlight_color: value("UI/highlight_color", "orange")
+ readonly property color outgoing_color_base: value("UI/outgoing_color", "#4A8780")
+ readonly property color outgoing_color:
+ mixColors(defaultPalette.text, settings.outgoing_color_base, 0.5)
+ readonly property bool show_author_avatars:
+ value("UI/show_author_avatars", timeline_style != "xchat")
+ }
+ SystemPalette { id: defaultPalette; colorGroup: SystemPalette.Active }
+ SystemPalette { id: disabledPalette; colorGroup: SystemPalette.Disabled }
+
+ // Property interface
+
+ /** Determines whether the view is moving at the moment */
+ property var view
+ property bool moving: view.moving
+
+ // TimelineItem definition
+
+ visible: marks !== EventStatus.Hidden
+ enabled: visible
+ height: childrenRect.height * visible
+
+ readonly property bool sectionVisible: section !== aboveSection
+ readonly property bool authorSectionVisible:
+ sectionVisible || author !== aboveAuthor
+ readonly property bool replaced: marks === EventStatus.Replaced
+ readonly property bool pending: [
+ EventStatus.Submitted,
+ EventStatus.Departed,
+ EventStatus.ReachedServer,
+ EventStatus.SendingFailed
+ ].indexOf(marks) != -1
+ readonly property bool failed: marks === EventStatus.SendingFailed
+ readonly property bool eventWithTextPart:
+ ["message", "emote", "image", "file"].indexOf(eventType) >= 0
+ /* readonly but animated */ property string textColor:
+ marks === EventStatus.Submitted || failed ? disabledPalette.text :
+ marks === EventStatus.Departed ?
+ mixColors(disabledPalette.text, defaultPalette.text, 0.5) :
+ marks === EventStatus.Redacted ? disabledPalette.text :
+ (eventWithTextPart && author === room.localUser) ? settings.outgoing_color :
+ highlight && settings.highlight_mode == "text" ? settings.highlight_color :
+ (["state", "notice", "other"].indexOf(eventType) >= 0)
+ ? mixColors(disabledPalette.text, defaultPalette.text, 0.5)
+ : defaultPalette.text
+ readonly property string authorName: room && room.safeMemberName(author.id)
+ // FIXME: boilerplate with models/userlistmodel.cpp:115
+ readonly property string authorColor: Qt.hsla(userHue,
+ (1-defaultPalette.window.hslSaturation),
+ /* contrast but not too heavy: */
+ (-0.7*defaultPalette.window.hslLightness + 0.9),
+ defaultPalette.buttonText.a)
+
+ readonly property bool xchatStyle: settings.timeline_style === "xchat"
+ readonly property bool actionEvent: eventType == "state" || eventType == "emote"
+
+ readonly property bool readMarkerHere: messageModel.readMarkerVisualIndex === index
+
+ readonly property bool partiallyShown: y + height - 1 > view.contentY
+ && y < view.contentY + view.height
+
+ readonly property bool bottomEdgeShown:
+ y + height - 1 > view.contentY
+ && y + height - 1 < view.contentY + view.height
+
+ onBottomEdgeShownChanged: {
+ // A message is considered as "read" if its bottom spent long enough
+ // within the viewing area of the timeline
+ if (!pending)
+ controller.onMessageShownChanged(eventId, bottomEdgeShown)
+ }
+
+ onPendingChanged: bottomEdgeShownChanged()
+
+ onReadMarkerHereChanged: {
+ if (readMarkerHere) {
+ if (partiallyShown) {
+ chatView.readMarkerContentPos =
+ Qt.binding(function() { return y + height })
+ console.log("Read marker line bound at index", index)
+ } else
+ chatView.parkReadMarker()
+ }
+ }
+
+ onPartiallyShownChanged: readMarkerHereChanged()
+
+ Component.onCompleted: {
+ if (bottomEdgeShown)
+ bottomEdgeShownChanged(true)
+ readMarkerHereChanged()
+ }
+
+ AnimationBehavior on textColor {
+ ColorAnimation { duration: settings.animations_duration_ms }
+ }
+
+ property bool showingDetails
+
+ Connections {
+ target: controller
+ // TODO
+ // onShowDetails: {
+ // if (currentIndex === index) {
+ // showingDetails = !showingDetails
+ // if (!settings.enable_animations) {
+ // detailsAreaLoader.visible = showingDetails
+ // detailsAreaLoader.opacity = showingDetails
+ // } else
+ // detailsAnimation.start()
+ // }
+ // }
+ // onAnimateMessage: {
+ // if (currentIndex === index)
+ // blinkAnimation.start()
+ // }
+ }
+
+ SequentialAnimation {
+ id: detailsAnimation
+ PropertyAction {
+ target: detailsAreaLoader; property: "visible"
+ value: true
+ }
+ FastNumberAnimation {
+ target: detailsAreaLoader; property: "opacity"
+ to: showingDetails
+ easing.type: Easing.OutQuad
+ }
+ PropertyAction {
+ target: detailsAreaLoader; property: "visible"
+ value: showingDetails
+ }
+ }
+ SequentialAnimation {
+ id: blinkAnimation
+ loops: 3
+ PropertyAction {
+ target: messageFlasher; property: "visible"
+ value: true
+ }
+ PauseAnimation {
+ // `settings.animations_duration_ms` intentionally is not in use here
+ // because this is not just an eye candy animation - the user will lose
+ // functionality if this animation stops working.
+ duration: 200
+ }
+ PropertyAction {
+ target: messageFlasher; property: "visible"
+ value: false
+ }
+ PauseAnimation {
+ duration: 200
+ }
+ }
+
+ TimelineMouseArea {
+ anchors.fill: fullMessage
+ acceptedButtons: Qt.AllButtons
+ }
+
+ Column {
+ id: fullMessage
+ width: parent.width
+
+ Rectangle {
+ width: parent.width
+ height: childrenRect.height + 2
+ visible: sectionVisible
+ color: defaultPalette.window
+ Label {
+ font.family: settings.font.family
+ font.pointSize: settings.font.pointSize
+ font.bold: true
+ renderType: settings.render_type
+ text: section
+ }
+ }
+ Loader {
+ id: detailsAreaLoader
+// asynchronous: true // https://bugreports.qt.io/browse/QTBUG-50992
+ active: visible
+ visible: false // Controlled by showDetailsButton
+ opacity: 0
+ width: parent.width
+
+ sourceComponent: detailsArea
+ }
+
+ Item {
+ id: message
+ width: parent.width
+ height: childrenRect.height
+
+ // There are several layout styles (av - author avatar,
+ // al - author label, ts - timestamp, c - content
+ // default (when "timeline_style" is not "xchat"):
+ // av al
+ // c ts
+ // action events (for state and emote events):
+ // av (al+c in a single control) ts
+ // (spanning both rows )
+ // xchat (when "timeline_style" is "xchat"):
+ // ts av al c
+ // xchat action events
+ // ts av *(asterisk) al c
+ //
+ // For any layout, authorAvatar.top is the vertical anchor
+ // (can't use parent.top because of using childrenRect.height)
+
+ Label {
+ id: timelabel
+ visible: xchatStyle
+ width: if (!visible) { 0 }
+ anchors.top: authorAvatar.top
+ anchors.left: parent.left
+
+ opacity: 0.8
+ renderType: settings.render_type
+ font.family: settings.font.family
+ font.pointSize: settings.font.pointSize
+ font.italic: pending
+
+ // TODO
+ // text: "<" + time.toLocaleTimeString(Qt.locale(),
+ // Locale.ShortFormat) + ">"
+ text: 'time'
+ }
+ Image {
+ id: authorAvatar
+ visible: (authorSectionVisible || xchatStyle)
+ && settings.show_author_avatars && author.avatarMediaId
+ anchors.left: timelabel.right
+ anchors.leftMargin: 3
+ height: visible ? settings.lineSpacing * (2 - xchatStyle)
+ : authorLabel.height
+
+ // 2 text line heights by default; 1 line height for XChat
+ width: settings.show_author_avatars
+ * settings.lineSpacing * (2 - xchatStyle)
+
+ fillMode: Image.PreserveAspectFit
+ horizontalAlignment: Image.AlignRight
+
+ source: author.avatarMediaId
+ ? "image://mtx/" + author.avatarMediaId : ""
+ sourceSize: Qt.size(width, -1)
+
+ AuthorInteractionArea { authorId: author.id }
+ AnimationBehavior on height { FastNumberAnimation { } }
+ }
+ Label {
+ id: authorLabel
+ visible: xchatStyle || (!actionEvent && authorSectionVisible)
+ anchors.left: authorAvatar.right
+ anchors.leftMargin: 2
+ anchors.top: authorAvatar.top
+ width: xchatStyle ? 120 - authorAvatar.width
+ : Math.min(textField.width, implicitWidth)
+ horizontalAlignment:
+ actionEvent ? Text.AlignRight : Text.AlignLeft
+ elide: Text.ElideRight
+
+ color: authorColor
+ textFormat: Label.PlainText
+ font.family: settings.font.family
+ font.pointSize: settings.font.pointSize
+ font.bold: !xchatStyle
+ renderType: settings.render_type
+
+ text: (actionEvent ? "* " : "") + authorName
+
+ AuthorInteractionArea { authorId: author.id }
+ }
+
+ Item {
+ id: textField
+ height: textFieldImpl.height
+ anchors.top:
+ !xchatStyle && authorLabel.visible ? authorLabel.bottom :
+ height >= authorAvatar.height ? authorLabel.top : undefined
+ anchors.verticalCenter: !xchatStyle && !authorLabel.visible
+ && height < authorAvatar.height
+ ? authorAvatar.verticalCenter
+ : undefined
+ anchors.left: (xchatStyle ? authorLabel : authorAvatar).right
+ anchors.leftMargin: 2
+ anchors.right: parent.right
+ anchors.rightMargin: 1
+
+ // RectangularGlow {
+ // id: highlighter
+ // anchors.fill: parent
+ // anchors.margins: glowRadius / 2
+ // visible: highlight && settings.highlight_mode != "text"
+ // glowRadius: 5
+ // cornerRadius: glowRadius
+ // spread: 1 / glowRadius
+ // color: settings.highlight_color
+ // opacity: 0.3
+ // cached: true
+ // }
+ Rectangle {
+ id: messageFlasher
+ visible: false
+ anchors.fill: parent
+ opacity: 0.5
+ color: settings.highlight_color
+ radius: 2
+ }
+ TextEdit {
+ id: textFieldImpl
+ anchors.top: textField.top
+ width: parent.width
+ leftPadding: 2
+ rightPadding: 2
+ x: -textScrollBar.position * contentWidth
+
+ // Doesn't work for attributes
+ function toHtmlEscaped(txt) {
+ // Make sure to replace & first
+ return txt.replace(/&/g, '&')
+ .replace(//g, '>')
+ }
+
+ selectByMouse: true
+ readOnly: true
+ textFormat: TextEdit.RichText
+ // FIXME: The text is clumsy and slows down creation
+ text: (!xchatStyle
+ ? ("
"
+ // TODO
+ // + toHtmlEscaped(time.toLocaleTimeString(Qt.locale(),
+ // Locale.ShortFormat))
+ + " |
"
+ + (actionEvent
+ ? (""
+ + toHtmlEscaped(authorName) + " ")
+ : ""))
+ : "")
+ + display
+ + (replaced
+ ? "" + " (" + qsTr("edited") + ")"
+ : "")
+ horizontalAlignment: Text.AlignLeft
+ wrapMode: Text.Wrap
+ color: textColor
+ font: settings.font
+ renderType: settings.render_type
+
+ // TODO: In the code below, links should be resolved
+ // with Qt.resolvedLink, once we figure out what
+ // to do with relative URLs (note: www.google.com
+ // is a relative URL, https://www.google.com is not).
+ // Instead of Qt.resolvedUrl (and, most likely,
+ // QQmlAbstractUrlInterceptor to convert URLs)
+ // we might just prefer to do the whole resolving
+ // in C++.
+ onHoveredLinkChanged:
+ controller.showStatusMessage(hoveredLink)
+
+ onLinkActivated: controller.resourceRequested(link)
+
+ TimelineTextEditSelector {}
+ }
+
+ TimelineMouseArea {
+ anchors.fill: parent
+ cursorShape: textFieldImpl.hoveredLink
+ ? Qt.PointingHandCursor : Qt.IBeamCursor
+ acceptedButtons: Qt.MiddleButton | Qt.RightButton
+
+ onClicked: {
+ if (mouse.button === Qt.MiddleButton) {
+ if (textFieldImpl.hoveredLink)
+ controller.resourceRequested(
+ textFieldImpl.hoveredLink, "_interactive")
+ } else if (mouse.button === Qt.RightButton) {
+ controller.showMenu(index, textFieldImpl.hoveredLink,
+ textFieldImpl.selectedText, showingDetails)
+ }
+ }
+
+ onWheel: {
+ if (wheel.angleDelta.x != 0 &&
+ textFieldImpl.width < textFieldImpl.contentWidth)
+ {
+ if (wheel.pixelDelta.x != 0)
+ textScrollBar.position -=
+ wheel.pixelDelta.x / width
+ else
+ textScrollBar.position -=
+ wheel.angleDelta.x / 6 / width
+ textScrollBar.position =
+ Math.min(1, Math.max(0,
+ textScrollBar.position))
+ } else
+ wheel.accepted = false
+ }
+ }
+ ScrollBar {
+ id: textScrollBar
+ hoverEnabled: true
+ visible: textFieldImpl.contentWidth > textFieldImpl.width
+ active: visible
+ orientation: Qt.Horizontal
+ size: textFieldImpl.width / textFieldImpl.contentWidth
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.bottom: parent.bottom
+ }
+ }
+
+ Loader {
+ id: imageLoader
+ active: eventType == "image"
+
+ anchors.top: textField.bottom
+ anchors.left: textField.left
+ anchors.right: textField.right
+
+ sourceComponent: ImageContent {
+ property var info:
+ !progressInfo.isUpload && !progressInfo.active &&
+ content.info && content.info.thumbnail_info
+ ? content.info.thumbnail_info
+ : content.info
+ sourceSize: if (info) { Qt.size(info.w, info.h) }
+ source: downloaded || progressInfo.isUpload
+ ? progressInfo.localPath
+ : progressInfo.failed
+ ? ""
+ : content.info && content.info.thumbnail_info
+ && !autoload
+ ? "image://mtx/" + content.thumbnailMediaId
+ : ""
+ maxHeight: chatView.height - textField.height -
+ authorLabel.height * !xchatStyle
+ autoload: settings.autoload_images
+ }
+ }
+ Loader {
+ id: fileLoader
+ active: eventType == "file"
+
+ anchors.top: textField.bottom
+ anchors.left: textField.left
+ anchors.right: textField.right
+ height: childrenRect.height
+
+ // sourceComponent: FileContent { }
+ }
+
+ Label {
+ id: annotationLabel
+ anchors.top: imageLoader.active ? imageLoader.bottom
+ : fileLoader.bottom
+ anchors.left: textField.left
+ anchors.right: textField.right
+ height: annotation ? implicitHeight : 0
+ visible: annotation
+
+ font.family: settings.font.family
+ font.pointSize: settings.font.pointSize
+ font.italic: true
+ leftPadding: 2
+ rightPadding: 2
+
+ text: annotation
+ }
+ Flow {
+ anchors.top: annotationLabel.bottom
+ anchors.left: textField.left
+ anchors.right: textField.right
+
+ Repeater {
+ model: reactions
+ ToolButton {
+ id: reactionButton
+
+ topPadding: 2
+ bottomPadding: 2
+
+ contentItem: Text {
+ text: modelData.key + " \u00d7" /* Math "multiply" */
+ + modelData.authorsCount
+ font.family: settings.font.family
+ font.pointSize: settings.font.pointSize
+ color: modelData.includesLocalUser
+ ? defaultPalette.highlight
+ : defaultPalette.buttonText
+ }
+
+ background: Rectangle {
+ radius: 4
+ color: reactionButton.down
+ ? defaultPalette.button : "transparent"
+ border.color: modelData.includesLocalUser
+ ? defaultPalette.highlight
+ : disabledPalette.buttonText
+ border.width: 1
+ }
+
+ hoverEnabled: true
+ MyToolTip {
+ visible: hovered
+ //: %2 is the list of users
+ text: qsTr("Reaction '%1' from %2")
+ .arg(modelData.key).arg(modelData.authors)
+ }
+
+ onClicked: controller.reactionButtonClicked(
+ eventId, modelData.key)
+ }
+ }
+ }
+ Loader {
+ id: buttonAreaLoader
+ active: failed || // resendButton
+ (pending && marks !== EventStatus.ReachedServer && marks !== EventStatus.Departed) || // discardButton
+ (!pending && eventResolvedType == "m.room.create" && refId) || // goToPredecessorButton
+ (!pending && eventResolvedType == "m.room.tombstone") // goToSuccessorButton
+
+ anchors.top: textField.top
+ anchors.right: parent.right
+ height: textField.height
+
+ sourceComponent: buttonArea
+ }
+ }
+ }
+
+ // Components loaded on demand
+
+ Component {
+ id: buttonArea
+
+ Item {
+ TimelineItemToolButton {
+ id: resendButton
+ visible: failed
+ anchors.right: discardButton.left
+ text: qsTr("Resend")
+
+ onClicked: room.retryMessage(eventId)
+ }
+ TimelineItemToolButton {
+ id: discardButton
+ visible: pending && marks !== EventStatus.ReachedServer
+ && marks !== EventStatus.Departed
+ anchors.right: parent.right
+ text: qsTr("Discard")
+
+ onClicked: room.discardMessage(eventId)
+ }
+ TimelineItemToolButton {
+ id: goToPredecessorButton
+ visible: !pending && eventResolvedType == "m.room.create" && refId
+ anchors.right: parent.right
+ text: qsTr("Go to\nolder room")
+
+ // TODO: Treat unjoined invite-only rooms specially
+ onClicked: controller.resourceRequested(refId, "join")
+ }
+ TimelineItemToolButton {
+ id: goToSuccessorButton
+ visible: !pending && eventResolvedType == "m.room.tombstone"
+ anchors.right: parent.right
+ text: qsTr("Go to\nnew room")
+
+ // TODO: Treat unjoined invite-only rooms specially
+ onClicked: controller.resourceRequested(refId, "join")
+ }
+ }
+ }
+
+ Component {
+ id: detailsArea
+
+ Rectangle {
+ height: childrenRect.height
+ radius: 5
+
+ color: defaultPalette.button
+ border.color: defaultPalette.mid
+
+ readonly property url evtLink:
+ "https://matrix.to/#/" + room.id + "/" + eventId
+ readonly property string sourceText: toolTip
+
+ Item {
+ id: detailsHeader
+ width: parent.width
+ height: childrenRect.height
+
+ TextEdit {
+ // TODO
+ // text: "<" + time.toLocaleString(Qt.locale(), Locale.ShortFormat) + ">"
+ text: 'date'
+ font.bold: true
+ font.family: settings.font.family
+ font.pointSize: settings.font.pointSize
+ renderType: settings.render_type
+ readOnly: true
+ selectByKeyboard: true; selectByMouse: true
+
+ anchors.top: eventTitle.bottom
+ anchors.left: parent.left
+ anchors.leftMargin: 3
+ z: 1
+ }
+ TextEdit {
+ id: eventTitle
+ text: ""+ eventId + ""
+ textFormat: Text.RichText
+ font.bold: true
+ font.family: settings.font.family
+ font.pointSize: settings.font.pointSize
+ renderType: settings.render_type
+ horizontalAlignment: Text.AlignHCenter
+ readOnly: true
+ selectByKeyboard: true; selectByMouse: true
+
+ width: parent.width
+
+ onLinkActivated: Qt.openUrlExternally(link)
+
+ MouseArea {
+ anchors.fill: parent
+ cursorShape: parent.hoveredLink ?
+ Qt.PointingHandCursor :
+ Qt.IBeamCursor
+ acceptedButtons: Qt.NoButton
+ }
+ }
+ TextEdit {
+ text: eventResolvedType
+ textFormat: Text.PlainText
+ font.bold: true
+ font.family: settings.font.family
+ font.pointSize: settings.font.pointSize
+ renderType: settings.render_type
+
+ anchors.top: eventTitle.bottom
+ anchors.right: parent.right
+ anchors.rightMargin: 3
+ }
+
+ TextEdit {
+ id: permalink
+ text: evtLink
+ font: settings.font
+ renderType: settings.render_type
+ width: 0; height: 0; visible: false
+ }
+ }
+
+ ScrollView {
+ anchors.top: detailsHeader.bottom
+ width: parent.width
+ height: Math.min(implicitContentHeight, chatView.height / 2)
+ clip: true
+ ScrollBar.horizontal.policy: ScrollBar.AlwaysOn
+ ScrollBar.vertical.policy: ScrollBar.AlwaysOn
+
+ TextEdit {
+ text: sourceText
+ textFormat: Text.PlainText
+ readOnly: true;
+ font.family: "Monospace"
+ font.pointSize: settings.font.pointSize
+ renderType: settings.render_type
+ selectByKeyboard: true; selectByMouse: true
+ }
+ }
+ }
+ }
+}
diff --git a/demo/qml/TimelineItemToolButton.qml b/demo/qml/TimelineItemToolButton.qml
new file mode 100644
index 0000000..74b9bf3
--- /dev/null
+++ b/demo/qml/TimelineItemToolButton.qml
@@ -0,0 +1,22 @@
+import QtQuick 2.6
+import QtQuick.Controls 2.1
+// import QtQuick.Controls.Styles 2.1
+
+ToolButton {
+ width: visible * implicitWidth
+ height: visible * parent.height
+ anchors.top: parent.top
+ anchors.rightMargin: 2
+
+ // style: ButtonStyle {
+ // label: Text {
+ // text: control.text
+ // font: settings.font
+ // fontSizeMode: Text.VerticalFit
+ // minimumPointSize: settings.font.pointSize - 3
+ // verticalAlignment: Text.AlignVCenter
+ // horizontalAlignment: Text.AlignHCenter
+ // renderType: settings.render_type
+ // }
+ // }
+}
diff --git a/demo/qml/TimelineMouseArea.qml b/demo/qml/TimelineMouseArea.qml
new file mode 100644
index 0000000..fbbfbaf
--- /dev/null
+++ b/demo/qml/TimelineMouseArea.qml
@@ -0,0 +1,6 @@
+import QtQuick 2.2
+
+MouseArea {
+ onWheel: chatView.onWheel(wheel)
+ onReleased: controller.focusInput()
+}
diff --git a/demo/qml/TimelineSettings.qml b/demo/qml/TimelineSettings.qml
new file mode 100644
index 0000000..504ad81
--- /dev/null
+++ b/demo/qml/TimelineSettings.qml
@@ -0,0 +1,34 @@
+import QtQuick 2.4
+import Quotient 1.0
+
+Settings {
+ readonly property int animations_duration_ms_impl:
+ value("UI/animations_duration_ms", 400)
+ readonly property bool enable_animations: animations_duration_ms_impl > 0
+ readonly property int animations_duration_ms:
+ animations_duration_ms_impl == 0 ? 10 : animations_duration_ms_impl
+ readonly property int fast_animations_duration_ms: animations_duration_ms / 2
+
+ readonly property string timeline_style: value("UI/timeline_style", "")
+
+ readonly property string font_family_impl:
+ value("UI/Fonts/timeline_family", "")
+ readonly property real font_pointSize_impl:
+ parseFloat(value("UI/Fonts/timeline_pointSize", ""))
+ readonly property var defaultMetrics: FontMetrics { }
+ readonly property var fontInfo: FontMetrics {
+ font.family: font_family_impl ? font_family_impl
+ : defaultMetrics.font.family
+ font.pointSize: font_pointSize_impl > 0 ? font_pointSize_impl
+ : defaultMetrics.font.pointSize
+ }
+ readonly property var font: fontInfo.font
+ readonly property real fontHeight: fontInfo.height
+ readonly property real lineSpacing: fontInfo.lineSpacing
+
+ readonly property var render_type_impl: value("UI/Fonts/render_type",
+ "NativeRendering")
+ readonly property int render_type:
+ ["NativeRendering", "Native", "native"].indexOf(render_type_impl) != -1
+ ? Text.NativeRendering : Text.QtRendering
+}
diff --git a/demo/qml/TimelineTextEditSelector.qml b/demo/qml/TimelineTextEditSelector.qml
new file mode 100644
index 0000000..c874360
--- /dev/null
+++ b/demo/qml/TimelineTextEditSelector.qml
@@ -0,0 +1,54 @@
+import QtQuick 2.2
+
+/*
+ * Unfortunately, TextEdit captures LeftButton events for text selection in a way which
+ * is not compatible with our focus-cancelling mechanism, so we took over the task here.
+ */
+MouseArea {
+ property var textEdit: parent
+ property var selectionMode: TextEdit.SelectCharacters
+
+ anchors.fill: parent
+ acceptedButtons: Qt.LeftButton
+
+ onPressed: {
+ var x = mouse.x
+ var y = mouse.y
+ if (textEdit.flickableItem) {
+ x += textEdit.flickableItem.contentX
+ y += textEdit.flickableItem.contentY
+ }
+ var hasSelection = textEdit.selectionEnd > textEdit.selectionStart
+ if (hasSelection && controller.getModifierKeys() & Qt.ShiftModifier) {
+ textEdit.moveCursorSelection(textEdit.positionAt(x, y), selectionMode)
+ } else {
+ textEdit.cursorPosition = textEdit.positionAt(x, y)
+ if (chatView.textEditWithSelection)
+ chatView.textEditWithSelection.deselect()
+ }
+ }
+ onClicked: {
+ if (textEdit.hoveredLink)
+ textEdit.onLinkActivated(textEdit.hoveredLink)
+ }
+ onDoubleClicked: {
+ selectionMode = TextEdit.SelectWords
+ textEdit.selectWord()
+ }
+ onReleased: {
+ selectionMode = TextEdit.SelectCharacters
+ controller.setGlobalSelectionBuffer(textEdit.selectedText)
+ chatView.textEditWithSelection = textEdit
+
+ controller.focusInput()
+ }
+ onPositionChanged: {
+ var x = mouse.x
+ var y = mouse.y
+ if (textEdit.flickableItem) {
+ x += textEdit.flickableItem.contentX
+ y += textEdit.flickableItem.contentY
+ }
+ textEdit.moveCursorSelection(textEdit.positionAt(x, y), selectionMode)
+ }
+}
diff --git a/demo/qml/ToolTipArea.qml b/demo/qml/ToolTipArea.qml
new file mode 100644
index 0000000..9c0a85f
--- /dev/null
+++ b/demo/qml/ToolTipArea.qml
@@ -0,0 +1,12 @@
+import QtQuick 2.0
+
+MouseArea {
+ property alias tooltip: tooltip
+ property alias text: tooltip.text
+
+ anchors.fill: parent
+ acceptedButtons: Qt.NoButton
+ hoverEnabled: true
+
+ MyToolTip { id: tooltip; visible: containsMouse }
+}
diff --git a/demo/resources.py b/demo/resources.py
new file mode 100644
index 0000000..edf6149
--- /dev/null
+++ b/demo/resources.py
@@ -0,0 +1,1487 @@
+# Resource object code (Python 3)
+# Created by: object code
+# Created by: The Resource Compiler for Qt version 6.1.1
+# WARNING! All changes made in this file will be lost!
+
+from PySide6 import QtCore
+
+qt_resource_data = b"\
+\x00\x00\x00\x9a\
+i\
+mport QtQuick 2.\
+0\x0aimport Quotien\
+t 1.0\x0a\x0aNumberAni\
+mation {\x0a pro\
+perty var settin\
+gs: TimelineSett\
+ings { }\x0a dur\
+ation: settings.\
+animations_durat\
+ion_ms\x0a}\x0a\
+\x00\x00\x04\xbf\
+i\
+mport QtQuick 2.\
+0\x0aimport Quotien\
+t 1.0\x0a\x0aItem {\x0a \
+ width: parent.\
+width\x0a height\
+: visible ? chil\
+drenRect.height \
+: 0\x0a\x0a propert\
+y bool openOnFin\
+ished: false\x0a \
+ readonly proper\
+ty bool download\
+ed: progressInfo\
+ &&\x0a \
+ !progressInf\
+o.isUpload && pr\
+ogressInfo.compl\
+eted\x0a\x0a onDown\
+loadedChanged: {\
+\x0a if (dow\
+nloaded && openO\
+nFinished)\x0a \
+ openLocal\
+File()\x0a }\x0a\x0a \
+ function openE\
+xternally()\x0a \
+{\x0a if (pr\
+ogressInfo.local\
+Path.toString() \
+|| downloaded)\x0a \
+ openL\
+ocalFile()\x0a \
+ else\x0a \
+{\x0a op\
+enOnFinished = t\
+rue\x0a \
+room.downloadFil\
+e(eventId)\x0a \
+ }\x0a }\x0a\x0a \
+function openLoc\
+alFile()\x0a {\x0a \
+ if (Qt.op\
+enUrlExternally(\
+progressInfo.loc\
+alPath))\x0a \
+ return;\x0a\x0a \
+ controller\
+.showStatusMessa\
+ge(\x0a \
+\x22Couldn't determ\
+ine how to open \
+the file, \x22 +\x0a \
+ \x22openi\
+ng its folder in\
+stead\x22, 5000)\x0a\x0a \
+ if (Qt.op\
+enUrlExternally(\
+progressInfo.loc\
+alDir))\x0a \
+ return;\x0a\x0a \
+ controller.\
+showStatusMessag\
+e(\x0a \x22\
+Couldn't determi\
+ne how to open t\
+he file or its f\
+older.\x22,\x0a \
+ 5000)\x0a }\
+\x0a\x0a Connection\
+s {\x0a targ\
+et: controller\x0a \
+ onOpenExt\
+ernally: if (cur\
+rentIndex === in\
+dex) openExterna\
+lly()\x0a }\x0a}\x0a\
+\x00\x00\x02\xe3\
+i\
+mport QtQuick 2.\
+0\x0aimport QtQuick\
+.Controls 2.1\x0aim\
+port Quotient 1.\
+0\x0a\x0aToolTip {\x0a \
+ id:tooltip\x0a \
+TimelineSettings\
+ { id: settings \
+}\x0a\x0a padding: \
+4\x0a font: sett\
+ings.font\x0a\x0a b\
+ackground: Recta\
+ngle {\x0a S\
+ystemPalette { i\
+d: palette; colo\
+rGroup: SystemPa\
+lette.Active }\x0a \
+ radius: 3\
+\x0a color: \
+palette.window\x0a \
+ border.wi\
+dth: 1\x0a b\
+order.color: pal\
+ette.windowText\x0a\
+ }\x0a enter:\
+ AnimatedTransit\
+ion { NormalNumb\
+erAnimation {\x0a \
+ target: to\
+oltip\x0a pr\
+operty: \x22opacity\
+\x22\x0a easing\
+.type: Easing.Ou\
+tQuad\x0a fr\
+om: 0\x0a to\
+: 0.9\x0a } }\x0a \
+ exit: Animated\
+Transition { Fas\
+tNumberAnimation\
+ {\x0a targe\
+t: tooltip\x0a \
+ property: \x22op\
+acity\x22\x0a e\
+asing.type: Easi\
+ng.InQuad\x0a \
+ to: 0\x0a } }\x0a\
+}\x0a\
+\x00\x00\x00o\
+i\
+mport QtQuick 2.\
+2\x0a\x0aMouseArea {\x0a \
+ onWheel: chat\
+View.onWheel(whe\
+el)\x0a onReleas\
+ed: controller.f\
+ocusInput()\x0a}\x0a\
+\x00\x00\x00\x8f\
+i\
+mport QtQuick 2.\
+0\x0aimport Quotien\
+t 1.0\x0a\x0aTransitio\
+n {\x0a property\
+ var settings: T\
+imelineSettings \
+{ }\x0a enabled:\
+ settings.enable\
+_animations\x0a}\x0a\
+\x00\x00\x17N\
+\x00\
+\x00mJx\x9c\xdd.\x17\x17\xa00O\x81l5\x83\xfd\
+0JYQ\x80\xd9Bu\xcdR^\x8a?\x84!\xc3\
+?\xe6\x00zT\xe3-\x00]l\x96lB\x9e?#\
+{JC\xd4\xd3\xceV%\xa1\x19\x8c\x86\x8d\x01\x15\xd0\
+2x\xa8\xb0\x8fl\x05\xefY\x9af\x0edoI\x05\
+r\xd0TqB\xff\x93>P\x9aQ\xc8\x81\xcf\xa88\
+\xa0n\x13\xa7\xa9Ac\xd2;\xe1\x19\x8bhX\x0eA\
+x\xdc\xda\x18\xf2\xe0\x81\x96f\x04\x9bs\x9e\x06\x09\x07\
+g\xe9C\xc1\xf2\x09@\xf4\x18;\x03he\xe0\x11V\
+\xd5\xddv(\x008\x19!2#\x13d\xc350\x11\
+\xfdaT\xc0\xe2\x04Kd\xe0\x88\x85\xe2/\x8e\xa6\xc0\
+\xcf\x1d\xb5\xd9=\xb8;\xb5+\x18M[)\xfax\x98\
+^q\x9b$\xe4\xb74\x05\xd5\x8c\x94D\x82\x08\x8a\x16\
+t\xceNX:c\xe2\xe5X\xf6\x0b\xe2h\xa2\xcd\xce\
+\xeb\xe3\x7f\x9e\xbc\x9a\x82\xf0\x00\xe3\xe5\xa0\x09\xc1\xc8\xae\
+a\x97\x08\xd2.)vW\xb0\x19I\x5c\x94\xe2g\x10\
+.\x97\xd3\xfd\xfd/\x86\xe0\x22y\x1f\x9c\xe0`Q$\
+t\x8cp\xde\xac\xd8p\xfdS\xb7\xf1\xfeN\x83\x1ek\
+\xd8\x06\xbeF\xc0\xe7\xc0\x869E\x1b1\xb9\x0bh\x10\
+\xe8\x10\x9dwZ\x94B\xa0a\xc3I\xc99\xd8Kz\
+\xbd\x99j\xcb~Ktw\xf6\x82/\x1fz1~\x8b\
+\x5c\x07\xfeEA\x1e\xc1\xae\x7f}'\xb4\x1b\xc0\x01\xf3\
+\x92g(W\x01\x9dtZq\xe1\xa9\x9d\xa3\xe3VG\
+\x03A\xc3\xa3CaT.]\xa71\x17\x96Y(\x83\
+)\xa9\xc4AH\x9b\x94\x1fTg\xf6s\xa9s;\x11\
+\xc4\xc7'\xa0j\x80YX\x8e\xee\x8e\xd4\xd8'\x82\xfb\
+\xea\x97\xe0\x10\xachr\x8c\xc2(\xf0\x15b\xd9\x09\x18\
+\x15cL\x93ds\x0e^m6%\x1b \xbfr\x80\
+v\xc8>y.]2d\x06\xc0\xf8\xbf\x87\xef\x09\xc8\
+\xd9\x86<\xb5G\x03h\xf1[\xc2\xefDk\xc6a\xeb\
+\xd2W\xd1%\x93xU\x13\x0f\xc4OLo\xf5\x1c\x80\
+\x0b\xcf^\xd8\xd3\x1e-0*\x02\xe3[\xc7\x8b\xa0\x19\
+\x0e5\xf5\xd1\xf3\x05xE\x1c\xc1\x9eD\x84\x16d\x84\
+\xcb\x19\x91xNb\x88j\xe5\x22H\xb1Do8\xe1\
+\xa0\x02X\xc6W\x97\x0b\x13\x18\xea\x948\xab\x9ci\xa1\
+'\x00\x08\xe1s\xf1L\xf3`\x1d\xaf\xce\xc9\xf8\xber\
+\xbb&\xd6v\x842\xda\x06m\x15\xf0\xecDbh\xae\
+B\xea\xe6\xe3h\xbbI\x5c\x1d\xa0(\x1a\x9cJ\xe0\xd5\
+\xdagN\x9a\x8c'\xba\xff\x99\xc5\x9a\x0e\x92!\xca6\
+\xffN\x8c\xb7\xba\x87\xcd\x88\xcd\x1eb\x81 z\xdf\xe1\
+\x9e\xd5\xc0\x8e\xe4n\x9er0\xc1N\xde\x045;\x8b\
+\xc5z\xc6\xf3U&Ds\x0c\xb0\x81\xed@?f&\
+\x8f|\x9a\xb4'\x84\xbd\xe5\x09\x03\xeb{9\x1e\xe12\
+\x85\xb5\x87\xd0\x07w\x04\xe8\x02\x818\x86\x11\xb0\x15\xcc\xbf\x12X\xf4UM\
+\xbfq=\xf8ScO,\xe2T4\xce]\xa4\xd7;\
+s\xa4\x93 \xc0\x0e\xf87\xe8\xc4\xf6\xbe8YA7\
+\xcf\xce\xb73\x1b.\x1c\x0c\xfc\x0f\x85;\x08\xa4\x7f\xc1\
+\x16\xf4:\xc6P?\xab\xfd@\x03#\xf1\xbb\xea\x0d\xdb\
+\x14)#fhe\xaa_\x17\x1f\xf5\xdb\x8fia\x13\
+\xac\x1d\xda\xc3P\x08a\xc1\x89,4q\xb2L\xc6P\
+f2\x08B\xf9K\x86\xe1`%J\xa6\xa8^\xbc{\
+\xf9\xce\xfc\xcd3$\x8b\x02\xdb\xd0\x11\x9a\xbe\xe1*\xcf\
+Q\xeal\x8d\xa8\x88#\x83\xdc\x8f5\x8d<3a\x8b\
+$\xd0C\xd8\xc0\xb7\x9c\x82\x0a\x0bT\xbc\x0b\x13\xf7\xcf\
+\xeb\x86\xc0\x974\x8c\x81\xeeC 4d\xc3\x03[\xaf\
+$\x00\xe3\x99\x97\x86\x84\xa8\xfe\x9f\xcc\x07\x9f\xec\xed\x91\
+\x83\x99\xd2\x85\xb7\xdb!\x17^3\x90\xfb\xabN\x8c,\
+.\xb1\x0b\xe1\xe1S+.\xb0\xa7\
+.\xf9\xd4\xc7\x14\xd8\x18-\xe0MP\x82O4%\xaf\
+\xe4\x8fw\xab\xf2\xfd\x8aF\x0eL\x7fg\xfax\xf0\xd4\
+\xbb;lsmn\xa9^%\x9c/A[\xfc\xedv\
+kQ\x1e\xc7\xeb\x84\x16\x8b\xdf\xbe\xd1\xa7\x14\xc2\x0e\xdf\
+\x1e\x03\x1b\xff\xabG\xdb\xfeK\xa4\x083\xfc\x85F\x09\
+\xfd \x8c\x10\xc0\x8f\x01\xb8`Ps\xd6\x848c!\
+\xce\x09>\x0d\xf4U\xdd\xff\xbd*0U@\xd8\x86\x91\
+\x90f\xd1\x86T\xb3\x81\xc7\x86\xde\x0f\x06G\xe0!%\
+\x09P\xadh\xc1\xd4\x06\x9d&\xa8r\xe2\xb9\x04^\xc3\
+(J\xa04Y\xf3\xfc\x0a\x99\xc9\x1a]\xdb\x95\xc7{\
+{w\xe5\xaf;\xee\xc9\x9c\x9ajo\xe0\xa6t!\xfc\
+\xc9>\xe48\xe1\x08\x07}\xc8\x1a\x04\xcdB\x08=\x8b\
+`\x0e\xb4\x04\x0cVI\xa2\x94b\xdd#\x0c\xd9\x12\x1c\
+\x84\x17\x22f*D\x80z\x98$\xea\xa79\x0f\x18\xe9\
+U\xdadv\x17\xc8u\x1c\x95\x8b)F\x18\xe8\x84\x88\
+_\xf7\xaa\x97\x98i\x05W\x01\xac\x8d\xbdP\xd7 \xf3\
+}W\xc6\xf6\x11yl\x13\xbc\x91\x94\xfd\xce\xc8\xfc\xea\
+\xa6\x0eG\x9ca\xa9\xd5\xf1-\x9d\xb1\xc4\xe1\x95\xce\xc1\
+W\x08\xe64\x8d\x93\x8d\xe1\xa5\x18O\xdd#\x96\x1cD\
+\xe8<\xfe\x995\x07U/\xdc\xe3f<\x89\x1ar\xad\
+\x1bP\x03\x94\xdc\x85\xd0\xa0\x15L\xf9\xf0#\xea\xd5\xd6\
+\x00t\xc1*\xe2\xd8~\xab\x83?\xa5\x0emz\xee\x91\
+C\xcf\xde\xb3m9-6\xc0~9\x87\xd8\xa7\x90\xa8\
+\xa3\xf4.\xca\x12\xf4\xe0\xee\xeelu\x993<\xdf+\
+\x82\x9f\xca \xe6\xbb\xb3\x9c\xaf\x0b\xb6\xfb\xfe\xe2\xc5\x87\
+ov\xbe\xd8\xfb\xfak{S\xa98\x04\xb1\xd3\xf8\xba\
+U\x1b.d\x0cg9\xd2\x9e\x5cDf\x1b\xa1\xd7\x95\
+R\x97\x8cm\x8dVfkJ\xf6z9\xd2\xeaP\xf0\
+U\x1e\xb2\xca\xdd\xb6\x08b\xd0\xb1\xfa\xd38\xb94\xc9\
+\x986\xc4\xc77\xb7\xf9\xbeC\x1al\x1c\xd1}E\xbd\
+\x8c\xc1%\xec\xf85\xcb)(U\xba\xe1\xab\x92\x88\xf4\
+FA\xc6\xf4\x1a\xd4\xae\xca<\xcas\xad\xed&\x0c\x18\
+TuIP \xb6\x09\xc4\xb9;\x22L\x05w(]\
+n\x93\x10~\xaa \xbb9Z\x09\x19\x19\xaf\x17,#\
+#;\xbf2\xd2fA\x1f\x9bM\x9b\xc3a\xef\xaf\x01\
+\x83\xf6ch!\xe0\xd1BV*o\x11\xf6\xc2\xf2\xe6\
+\x80\xb3\xc8\xc0\x806\x8c\x88\xc8\xb9\xa8w\x9e\xa9\xc64\
+y\x14\xa2Q\xa3\x04\xdd\x11PU*.\x988&\x13\
+m\x5c,i\x96a\x0c\x0f\xa1\xd3\x82 #\xcb\x17\x93\
+fw\xb1\xc6\x0eBt\x10\x01\xd6\x22\xe8@B7P\
+k\xd9\xbe\xe1\x0f\xc7\xe0\xeb\xb1<.\xae&.P\xcd\
+ax0O\xb3\x8d\xe2\x98m\xc5\x02\x87\x82I\x020\
+\xb3\x88\xb2Ha\x80\xfd\xc3s}ep\x9a`\xc6`\
+\xe6\xb7J\xe1$(\x96\xc6\xb1\xda7\xe0sx\x83\xc4\
+sp\xf3\xc4fg\x9f2F9\x12\xd4\xc4\xf7\xad\xb7\
+\x95z\xa8\xd3}\xad>J\xe2DX\xa5\xfac\xbe`\
+\xcfP\x84\xbai\xa3\x0ak\x98\xb6(\xe2\xed\x9d\xb0y\
+YI4\xfe\xb8\xd7\xeaZ+\xa2\xe0\xab\xdf\xae\xe5\xff\
+`\x03\x15\x97\xe0\x8f\x85S}4\xd8^]3\x926\
+\x9eK\x834z:\x02S\x8e\xdb\x08\x84|\x8b'\x1f\
+\x0c}\x9bs\x91E\x1f\x83_\x22NC\xd8\xd8\x91\x22\
+n\x04\x91\xbe&\x81\x06\x10\xbe\xe7%\xb06x\x5c\x13\
+\x98q\xf4|\xd4\x02(1\xdaBd\xb6<\x16\x12\xdb\
+1\x1e\x96y\xf8\xd1\xe4\x0c?K\x8e]\x87\xc0\x98*\
+\xaey\xb5\x9d\xa0\xaa\x9ay\xcc\xe3(O\xa8\x8f\x94\x02\
+\xf9\xe4\x84E1=n\x1fY\xdaLZIR\x90\x0b\
+\x8b\xd2\xd5\xfb\x04\x9c\xe283\xe3\x1a\xdd\xb4\x8d\xd2\xd9\
+\x05\xe3\xb4I\xd4\xe4 \xb3\x83\xd0?$\xe3\xc7`:\
+\x06-W5M[\xa1\x0d\x9cf\x0f\x1b\xf0\xc4c\xb1\
+\x8f2m'\xbb\x15\xe8\x0e({\xf4\x84\xec\x9b\xaf\x08\
+\x1a\x8a\x7f\x1e\x01\x1a>\xe5\xd0Ej\x1f\xd2\x0f\x07\xaf\
+\xba-V\xe0\xb4\x9f\x88\xe2 \xc1f\xc1i\xce\x0a<\
+\xb4>,\x96\xc0-\xaf\xe36\x9e\x80O\xfc3\x08#\
+\x04\xa7I|\x99\xa5\xc2+\x91\x83\xc5\x8337\xa5\xa4\
+#3\x1d\xc6*\xba\x1d\xa8\xa3bp\xe5\xd2\xf2f\x17\
+E\xd75\x1e6k\xd4\x16/9\xa3\xd42 \xd8\x05\
+\xfc1\x16D\xde&;\xfb\x0eR\xc8\xaa\x86c\xacO\
+\x91fN\xc69j\xc6\xe3J\xda\x828r(lg\
+\x1eRm\xfa/\xee\xec\x07\xf9\xd4\x80c\xff\xea\xb2C\
+\x06o\x0e\xb1D(\xeb\xe3\xfb\xc6\xe9Q-\xb4\xb6R\
+h\xcb\x85-\xb5\x96%\x1a.\xb8\x8f\xbd\xbd\x86\xd97\
+%\x1b\xc6\x82\x0e\xc8\xfe\xe3\xbd\xca_TC\xdb>\xac\
+\xbbM\xc9\x09-\x17A\x1agc\x94\xdd\xd71K\xa2\
+@\xb1F\x9c.\xc1\xc4\xc4\xe5\xf7\xf8\xb3M\x0e\x17\xf7\
+;g4\x89}@\xc49a-\x1e\x80A\xfd\xe4-\
+\xda\xe9\xe6p\xf0\xd9P(E\xafW\xf8\xb7G\xacT\
+\x88i\x1c\xff:\xcd\x8c4DS\xc9S\xc1iBc\
+qt\xf9g\xdbs\x19p\xde\xefp\x99z=\x12\x8f\
+U\x1d\xdb\xf4\x1f=$#\xa1%&\x95\x0a\xc1\xa3\xf9\
+\xcf\xa9\x04>\xd9\xc0\x1c\x81\x186\xe1Dj\x9e\xf3\x9a\
+\xb3\xaa\xc71p\xa36>\xcd\xce\xa6\x089Y\xd0\xa0\
+k-\xef\x92\x03j{i>Ug\x8anhJ\x97\
+=\x7ff\xcb\x9czl\x03B\xcf{JV\x99\xa8\xa8\
+sT/i\xd4\xb5K\x7f\xc4\x90\xe0\xd3&\xc6\xf7\x1d\
+(\x0f\x10o\xd9`\xbc\xc2\xed\xa9\x0b\xe3\xc1p\x0e\xec\
+\xd16\xca\x83\xa1\x0c\xa1\x86T\xb1cK\xcb\x19\x14 \
+\xb6\xa2\x9c|\x16\xfd\x9bK\x86S!C7D\xf1V\
+\x83\xdcw:C*\xf3\xb6Jh\xfeM\xc2\xd7\x0e\x01\
+\xd0\xa7# \x07U=\x90\x83\x8e\xaa\x9b\x9d[\x94X\
+\xf6\xf5M\x05\x86\xc5\x94\x5c\x02\x06g4\x8aW\x05\xd9\
+u\x90@\x0d\xab\xcc\xe5\xa0\xc2\xa6\xfb\xba\xb0\xc9\x07\xae\
+\x9etJ\xbe\xf0u\x0ay\x9e\xb1\x5cw\xab\x87\xf8\xfa\
+\x17K<\x09\x05\xa2\xc3B\xfa{+\x9b\xe0\xab\xbb\xf2\
+\x0d3\x22\xc3\xb6\xa3\xadA\x8b\xb2FO\x8e\xd0:$\
+\xd3\xcd\x97\x8b\xd5\xcd\xc8N\xa9d\xb7\xb3\x9b\x9d}s\
+[\xdc!\xbc\xd2Xh{\x87\xc4*oI@l\xb9\
+\xda\xcc6\x9f\xb5i\x82\xa6\xf7U\x14\x97\x1d$\xb1,\
+@\xe7r\x85\x07U\xbb1.\xf7\x09[_\xb6O7\
+T\x1b\xa74\x92\x95\xb7\xed\xc5\x88\xa5\x22)z\xfa\xdc\
+L\xc9\x0e\x22u\x1eb\x86\xf4\x05(\xce%/Dm\
+5y\xa8Sx\xdf\xb7\xd3\x9d\xba\x01+\xbd\xe4\xac\xc0\
+\x84\x0e\x9e\xb1\x88\xb0\x89\x96\x10\xa4\xcfV%s\xc7A\
+\xfa\xc4\x86\x94\xfcM\x99&\xaf\x8a\x90.\xb1\xa0\xe0\xa6\
+t\xd5{\x18\x13\x9d\xd0+F\x8aU\xce`\xa4.\x5c\
+&\x0f 2\xca\x0b\xbf\x95P5\x1e\x00=PC\xc6\
+\xbb\x0fv/\xb7\xc9\xd6\x03\x9a.\x9fl\xf5\x07\x98\x84\
+\xd4#\x9f\xca\x91\x10.nM\xea\xa7\xcf\xe5\xd3\xcb\xd2\
+\x07\xee\x93\x9bx\x05K@\xe8^l\xc4a\x8dGV\
+\xe5\x1ah\xf4.CO\xcf\xdb\xc5\xf4\x1d5\xd7\x06g\
+q\xb8pz\x8f\xd8\xea\xea\xc6\x0b,1\xc2\xd0\x18+\
+\x99\x92UZlDr\xb4H0s\x19\xf1uFB\
+\xc0\xa0u<`N\x0d\xd6\xb0\xcb94\xda\x01\x19\x8f\
+\x9e\x96X\xa0 S\xce\xcf\xb6z7`\x9ep\x5c\x96\
+\xe0\xe6'\xc2\x1f\xdd)\xa4\x0b\x9b\xd2$y\xd2;^\
+j\x89\x1fG\x18\x95\xf6\xd4\x9f\x0ev\x13\xec\xe6\xa9Z\
+\xfd[\x1fs=\x22\xa3\x1fG[\xcf\x9f\x969\xfc\x17\
+92Ov\xf3%\xcdZ\xbd\x1e5e\xeb.i\xb4\
+\x16\xd4\x81\xcd\x91W\x1b@\x87\xa7\xbb@\x00\xf8'\xc7\
+\x7fpcz\xa9\xf1\xc8\x8a\x1d\xfawNp\x1e%\x8b\
+\x9c\xcd\x9fm\x19\x19\x8axX\xd1>\xe0\xb8\xa5Y\x16\
+7x'b\xe0\x1d\xc8\xf3\xd8\x8cg\xecI\xc5f\x03\
+\xc1\x19\xa1\xa0d\x84'\x82\xb5\xd72\xb4\xc0\x88k\xab\
+\x97\x06\x1a\x96\xbd\xddu\xdc4\x91\xa4\xa5\xcf\xc9h\x80\
+\xa2\x13qWg?\xd1\xa3\xa3\xc3#\xact\x07\xad\xd8\
+\x0e?\xcd>\xe3\xdc\xbcU\xe2m\x10\x0d>\x15\x22\xae\
+\xc9>\x98\xc2\xbdr~g\x89\xd5\x22\x8b\xfc3\x02\x86\
+\x82\xff\xfdT\x5c\xe4\xe3\x11\x03\x85\xcb\x22\x11\xb6\x8e&\
+Ow\x05\xde}\xdb\xe7\xa7\xa63w\xd7\x93\x89\xc0\xb6\
+\xce\xe9Rf\x09E\xe7\xef\xe1g\x97\xe3T\x95\xdf\xb9\
+\x0d5\xcf\xcaF\x9e\xc0c\x9en\x1b\xfacS\xcal\
+J\x8ee\xe1k\x88^;DO|\xbd\x8d\x89\xd8\xab\
+\x02\x0fjWI\x04\xcf`\x86\x82'\xd7\x1e~Q\xf5\
+\xb3\x98:\xd4\xfd\xde\xc2\xf8m\xc23\xf0\x0f\xd6\x0c\x1c\
+\x84Kt\x1a\xf0\x94s\xedJ\xe8* \xe0TD\x5c\
+\x82\xcaYB\xc5-\xbb\x0fgo\x0b2\xcex\x09K\
+[\xaf\xd7\xc1%\xe7\xe0!\x07!O}P\xb0\xf8\xc4\
+\x1a\xbf]\x1dr\xdb\x00\xd4I\xe7$\xf0A:\xce\x8a\
+\x12\xcbM\xf9\xdc\x5c\xda\x87<\x01\xbd\x97E\xdb$\xe5\
+\x05f\xb3\xafX\xb2q+o\x80\xf1\xfe}\x9a\x1c\xce\
+\x8a\x12s$0R\xa4K\xb0\xc4\x03T\x0e,\x17\xdc\
+;\x8c\x92\xc5*\xdd|\x88\xb4e L\x18l\x89J\
+\x1d\x88o\xe6,W\xb4\xc2m[/x\xa27\x08O\
+z|d\xc9\xc8\xd1\xa3G\xee\xb5\xf2\xec\x0d\xbf\xc6\xe2\
+i\xdc6]\xd6\xea\x95\x1c\xa3\xc2\x193\xef\xf2\xae\x8d\
+\xaa:\x19/j@\x8e\xc4\xb1\x9c\x0c_\x8aK\x94T\
+\x94\xc2\x1a\xf0p\x15\x98\x8a>\xc3\xb2. ~4N\
+\xfc\x80t\xa5\x8d\xf6\xb6\xce\x853\x87\x95\xac\xed8\xc2\
+\xe1\xffu\x15\xea\x98mp\xb0\x14\xae\xf2\x82\xe7\xe7\x0b\
+\x8ab\xd8\xc8K\xd5T\x19b&\x81\xdbN1\x1d\x08\
+\xdb\xf9\x068\xedH\x00&\x227\x7f\xfc\x82\xd1T>\
+\xf0$S\xdb\x05D'q\x14%L>!\xbf\xe2\x13\
+\x91\x22U\xb5\x17\x9eM:J\xe2\xf0\xca\xaeTn6\
+<\x9dM\x91z\xea\x8a\x87\xa8\xdbl\xcc\xd7\x15Qh\
+\x18^Z\xf5[\xccN\xde\x19d\xba\xbd\x93o\x93\xd1\
+\xc7Xg7\xafY\x87\xd5\x95%\xb4^r\x18\xc4\xee\
+\xa3FC\xb4NX\xb6\x1a\x8bB\xd8\xed\x0e<{\x97\
+i\x0f\x951\x0f\x8b.\xe4\x95m\xab\xc6\xb2c\x8d\xb7\
+\x09\xabx\xf6\xfd\x82\xb1\xa4\x8fw\xd6\xd8)\x10)\x8f\
+\x97,)ip\x83I\xa3=\xf2\xe0\xc1-\x97$b\
+t\xf2\xb4\xf1\xd4\x8c\x9d\xfd\x0b\xebgO\x89\xe62\xbe\
+a\x89\x89f?sz\xa2\xfa\x1d\xf7\x1d\x09_sL\
+\xbfK\xfa\xcfo\x9c7\x1e~O\x0c\xad}\xdc%\xff\
+\x18\x84\xa5\x07\x81a\xf3WGS\xfb\xdb\xeaoz3\
+\xde\x1b\x1eB\xba'\xef\xf0\xbf=\xf7H\xcc\xa6h\xa1\
+\xb40y\xd6\x91\x85sY\xa9\xe6\x93\x0a\xbd\x9e\x04X\
+\xd5\xcf\xe3\xcc\x82\xb6x\xa5o\xde{S\x18U\xe6\xd0\
+/G\xe4\xb9K\xf4<\x96\xc8_!\xa8\x1b8\xd9\xf8\
+\xad\x10YR\x0b\xaa\xf2M\xe5u\xbb\xd35\x22\xe9\xe0\
+\x92\xfd\xdd\x0e\x9c;\xadz\xbb\x0e\xa8\xab\xf7\x903\x00\
+\xb3\xbf<&\xaa\x06\xc8\x9f=\xdb\xde\xd0\xa9\xcezO\
+l\xb8\xf5\xe2\xc8_U{\xb6pP\xf4\xb7/9\xca\
+\xfb\xe4m\xb5\xed\xc9\x91z0n\x14\xa8T\xdd\x9d\x04\
+l\x10\xaf\xee\x9dw\xd5?\x18\x85\x9c\xa2dB\xdd1\
+\xf3\xc8\x81\xf5u\x8b8\x9bs\xbf\xfbz\x1f\xfa^\x82\
+\xb3P\x1cC\xb7 .>,\xf1\x8b1\xe2\x10\xcdz\
+#\xe9\xd7e\x8c\x14\x93\x058\x1f\x8e7\x7f\x07\xe5b\
+\x95\xce20\xaa\x1f\xf1\xa7\x17\xc6\xc1\x9dFM\xadQ\
+nY1j:\xd0\x8aaG,\xa1\xd3\xf5\x1db\xba\
+\xf5\xb6 V\xb0\x98x\x0c\xbc\xaeE\xc1$$\x92I\
+~\x06\xc0I\xc1N\xb5x`\x8f\x119\xafS\xdac\
+\x1c\xa6\xf6\xa0\xf9\x90\xef;\x1c\xb8\xea[\x9a`?\xc7\
+\xc6\xe9\xa6\x0e_y/\x09\x14v\xcd*\x1d=w5\
+m_\xa5O\xbd\x0c\xcfJ\xc1\x0a\xbe\xa9\x0a\x92\xd5\x0d\
+\xc7\xea\x06n-}\xfaQ\xdfD\xed\xe2.\xf2\xb0\xb3\
+\x10A\x0d\x12\x141o\x0c\xda\xdff\xea\xd5\x82\xe6\xaf\
+N%\x88_\xc6\xb8\x95\x0e\x14\x9f\xd2\xf8\xbf\xa4\x02\x9b\
+\xdd\x07\xd7\x93c\x83\xd8\xbe\xa51_\xc3\x0a+\x85\xd9\
+c_\xbaj\xa7\xb2\x8cK\xeb\xec\xae\x9f\xb2(f\xd8\
+\x22\xad;\x0f\xac\x87\x1eB\xf65s\x83\xff\xcc\xcd\xa8\
+\x89!\xd6%k\x9f\xa4\xa0\x91\xe6e\x05l\x957U\
+\x0ft\x14\x15\xfe9U\xc1N\x17\xb0\xe7<\xb3y\x96\
+\xd9\xea \x8f\xa1\x8c\xd5\x9a/m\x1e|\xed\xaex\xb0\
+K\xddl\xe6\xfbS\x5c\x913\xb6d\xb4tj\x1el\
+\xe2\xab#\xe2\xfe\xb7\xbc\xc3\xecNSq\xae\xeeou\
+\x85\xc3Q\x0d\xa6+/\x83\x0d\xa8\xd3s\xa4\x8cM\x92\
+\xabk\xbftS\x16\x08\xcb\xb0d\xd6\xba'&\x96\xfb\
+,\xd6\xfe\x92B\xb4w\xc56\x22\x17\xff\xe3jo/\
+\xfar\x84_-\xc1h\x8c\x8c\xd2UR\xc6\xcbd3\
+\x1a\xf6\xa5\x92G\x06Lit\x8a#\xbe\xea9X\xba\
+\xbd\x00\xb5F\xdfA\x98\xcc\xa62\xf95\xeeq\x16&\
+\xab\x88\x15o\xf5\xd7\x82\x86\xaa\xbc\x83\xe6\xa1HUM\
+1\x14B\xeb\x06]\xfd\xed\x95\x8e\xcc\x8e\x9f\x87\xaa/\
+\x1dN{\xabSt\xd3\xd5\x1d\x7f\x1fB4\x9b\xe5\x03\
+\xf42\x87,\xb5E(\x95p\x03\x97\xa8\xcciV\xc8\
+p\xab\xdb\x0f\x9c\xf1\x1c\x0d\xca\xe7\xdb=\x1fn\xb7\xde\
+D\xd5Z\xdf\x11\x1c\xb2\x99\xc6\xcaT\x19\xcb\xfe]6\
+~`\xc2\x00\xdb\xc9\x06\x15\xdcE\xbc\xec\xe1\x8b\xbax\
+L\xa6.;;\xef\xeeN\xc9_\x1f\xeb\xebL\xf8\x81\
+'y-\x89\xf9+\xfbE\x93\xaaI\x9e\x07\x9e)\xde\
+\x22[\x7f\xdd\xdf\x22\xf3\x9c\xa7\x00r\xc8!,\x09h\
+~9\xb6\xf4\xdb\xa4\xf1H\xa9\xa7\xaet\xa9\xf7\x95\x91\
+Z\xb7\xb2\xd7\xa6$\xa8\x1e\xc32\xd8\xd0\xaao\xca\xd8\
+X\xdf)\xedt\x0b\xcf[2\xa4q\xe3\xb4\xd9I\xfb\
+\xdf\xea\xbbq\x10:\x82\xb7\x8a\xf7&\xb2\xc8q\xf3\xd3\
+lcuq\x09\xe3+\xf7G%\xad\x0f\x07\xfa\xbb\xe9\
+\x8f\xceM\xd4\xec T!\xcd{\xa7\xbfo\xcc/\xa8\
+{\xa6\x8e\xff\xaa(\x22\x0d\xc4\x17\xccDe\x0e\x1b\x89\
+/\x9a\xb1\xf9q5\xcf%\xbf\xe0\xa7\xc0\xe7,\x84\xf8\
+\x95\xe7\x9fm>0\xea\xb3\x02@\xb1\xd1DOs\xbe\
+\x0a\xadI\x86F6]\xf7\xe3\x86d\xbaZ5\xd8\xde\
+\x18\xa5\x15\xa0\xd4\x9c\xe3\xe1=\xebN\xbd\xb8A\xac\xbf\
+%LT\x1e\x02$;b)\xcd\xa2\xc6wv\x9a\xdf\
+\x7f\xa8g\xaa\x9e;*\xce\xcd\xcf\x9bvzl\xd2S\
+\xeb\xe0`\xa3\xfe\xd2\x99\xafhP\xd8\xe2G\xb7\xd7j\
+k5\x9cz\xe48\x125\xf4\x8a`\x94\x9c\x95\xf9F\
+\x1f\xcb*\x051\xe9\x90\xf4[Q\xa0[\x8a*\x12\xdc\
+N\x88\xfd\xda\xaeW\xba\x7f\x13\x1f\x9b\xf4})\x176\
+\x84\xc0\x8a\x06\xbf\x17\x89\x87)\x90\x8a\xd4wVX\x9f\
+\x8dt\xdfp\x08M~\xccx\x82\x06\x03\xa7r\x11\xb1\
+*:\xb9@4\xc8*\xfb7\xc7J\x7f\x12g\xd7q\
+\xc9v\xc47\xe8pl\x81\xdfk\x0b\xc5\xc7\xb6\xba6\
+\xa2\xf3\xfcW,o\x9b\x8cp\x8a\xd1g\xdd\x97\xa6\xc6\
+\xfd\xed\xbbR\xab\xf5\xcf\xbd\x1f\x19[\xff\xe7\xec\x86\xa5\
+\xf6}*\xdd\xfc\xb0C\xf5\xc2\x17\x9ft\xe5\xd2\xcc~\
+yu\x13\xc1z\xec\xfe0\xc9\xac\xbd\xedv<\xd1\xe8\
+\x9f\xc6\x91\x0d\xb6\xfd\xbd\xc5U\x8e_\x07.\xf1D\xbf\
+}v1\xd2\x15L)-\xf3\xf8\x06\xb8e\xf7/\x22\
+{,x'\x8e0\xfe\x16\xbf\x95\x12\xea\x99K}\x09\
+U\xee\xd0\x85`\x99R:\xf1\x83of\xa9=x\xc3\
+\x9c~\xdf\x90\x12\xfa[%9{\xee\x01t\xd5\xe5v\
+]hwT\xe1\xde\xe2V:6u3=\x02\x95\xea\
+\xae\xa3\xee\xfb\x5cL\xd5\xe7\xd6I\x8c\xdf\x92\xbc\xb8\xf5\
+\xe7\x0b\xe4\xa0\xde*x]P\xff_l3\xe3`\x1c\
+e\xd7'\xceB{'\x00\xcbU\x95i\xfb\xb8\x84\xed\
+\xe8H\x1e\xdf\xed(\xb7\xfb\xb6<\xb6\x9f]\xc1\xf3\x9d\
+\xae\xa8\xd4\xeb\xe8\xe0\xa1\xaa4Z\xd6\xc9+] \xcb\
+]\x9f\x8f*\xd1\xd6\xf5\xc4~v4o!t\xdf@\
+\xf8\x7f\xc4\x9b=\xf5\xbao:.!\xfe\x11l\xdd\xfb\
+\xf5\x22\xddZ\xd5\x91\xa0\x9b@kg\x1f\xf2\xe4\xd5\x0d\
+\xac@|\xe9\xad\xab*\xb2\xaf\x9c\x11\xdb\xe0\x92Fl\
+VY\xa3Z\x80Q~\xd6\xf8\x98{g\xf3\xd45\xde\
+\x0a@_\x01\xa4X\x9f\xa3\x08\xf2[\xde\x11\x82\x0f)\
+\xc5\xe9\x91s)\xc4-\x1fo\x98\x94\xfa\xaf\x99c\xfb\
+\x0f\x15\xd3\xcf\xad\xdfo[|c]\xc2m\xabxW\
+Ip\xbf*\x07\x07*\xa5\x89\xaf\x8eW3A\xe9\xad\
+\xf4\xfdl%\xfd\xaeAJ\xcb\xec=\xa9\x9c+\xf8\xb3\
+\xe7>h\xe7\xe1\xb0,+\xc3\x0a\x82\xbe\xd3:\xcb\x1f\
+\xf4\xed\xe6m\xdc\xc2\xaa\xb8O\x1f\xb5\xaa\xf3ly\xe2\
+\xba\xdd*m\xd8%\x8f\x1d\x9f\x8eN\xe2\xa5Gh\xea\
+\xc2\xbf\xdat\x00\xc7\xc3T @\xf5\xcb\xc3dM7\
+\xc5\xbb\xb6\xda\xa8\xbb\xe8{\xee]\xa3\xef\xa6Mj\x07\
+\xfds\xa8\x11\xdb\xce\xb9o\xedYjdt\xc23^\
+,i\xe8\x08J\xab\xce\x7f\xa4\x91\xbf\x8d\x19n\x8e\xf5\
+%\xb9u\xc4\xf9\xe9\xde\xff\x02\xdd\xd4\x16~\
+\x00\x00\x01\xc8\
+\x00\
+\x00\x061x\x9c\x95T\xc1N\xc30\x0c\xbd\xf7+L\
+O\x9b4J\x878U\x1a\xdc&8\x00\x1a\x83\x13B\
+U\xd8\xdc\x11\x91&U\xeaN\x14\xc4\xbf\x93\xb4\xa3\xeb\
+\xba\xa5\x83\x9cR\xdb\xef\xf9\xe5\xc5)O3\xa5\x09f\
+4+\xf8\xe2\x1d\xce\x83\x0b\x8foB\x85\x22\x8e\x92`\
+\x1c\x84\x9e7G\x22.W9|y`\x96F\xb6T\
+R\x94\x90i\x95\xa1\xa6\x12\xb8\xa9d\x92\xa7\x8c\xb8\x92\
+y\xbc,t\xb5\x8b\xd3<6\x84\x22\xaa`v\xad\x99\
+(p\xe0?\xdd\x9c\x1d.\xf7Gp\x11\x86CG\x9b\
+W\xa5\x04\xa0d\xaf\x02\xe3->\xeak\x0d\x97\x10\xfe\
+[\xf4Vo\x1f\xf3d\x02!\x5c\xc18\x84^\x05=\
+\xdd\x13\x96S\xec\x90\xe0\xa0\x8438\xf7\x1c\x949i\
+sK@\xbb\xbd\x1b\x17\xa2C\x8e\xda\
+\xf9\xffU\xb1\x9b\xf9\xa3\x90c\x92\x1a\xd2\x8a\xef\x98\x93\
+Q\xe3g\x85>6\x1e\xd7\xc8Wom\xd0[\x15\xe8\
+\x83\xd9\xc9\x98gla\x86\xb7\x85kE]\x03o%\
+j\x94K\xd41\x95\xd9f\x1e\xf7\xa6\xbcU\xe1\x8f\xfe\
+m!\x80\x7fg^\xf2\x1a\x1f*\x1a\xa3\xc6\xf9D\xec\
+\xff\xa1\xd5l\xfb2\x9e\xf7(F\xbf\xacv'\xeb\xdd\
+K\xc0M\xfa\xe3>\x19t\xcf4\x84\x93\x09\x9c\x8e\x1b\
+\xbe+x\xc4\x0f\x0a:\xa4\xe6\xb6\xab\xf0\x8c\x9a\x90\xf7\
+\xed\xfd\x00\x1fl\x07\xfb\
+\x00\x00\x05\xea\
+i\
+mport QtQuick 2.\
+0\x0a// import QtQu\
+ick.Controls 1.4\
+\x0a// import QtQui\
+ck.Layouts 1.1\x0a\x0a\
+Attachment {\x0a \
+ property var so\
+urceSize\x0a pro\
+perty url source\
+\x0a property va\
+r maxHeight\x0a \
+property bool au\
+toload\x0a\x0a // I\
+mage {\x0a // \
+ id: imageConte\
+nt\x0a // wi\
+dth: parent.widt\
+h\x0a // hei\
+ght: sourceSize.\
+height *\x0a // \
+ Math\
+.min(maxHeight /\
+ sourceSize.heig\
+ht * 0.9,\x0a //\
+ \
+ Math.min(w\
+idth / sourceSiz\
+e.width, 1))\x0a \
+ // fillMode\
+: Image.Preserve\
+AspectFit\x0a //\
+ horizontalA\
+lignment: Image.\
+AlignLeft\x0a\x0a /\
+/ source: pa\
+rent.source\x0a \
+// sourceSiz\
+e: parent.source\
+Size\x0a\x0a // \
+ TimelineMouseAr\
+ea {\x0a // \
+ anchors.fill\
+: parent\x0a // \
+ accepted\
+Buttons: Qt.Left\
+Button\x0a // \
+ hoverEnabl\
+ed: true\x0a\x0a //\
+ onConta\
+insMouseChanged:\
+\x0a // \
+ controller.s\
+howStatusMessage\
+(containsMouse\x0a \
+ // \
+ \
+ ?\
+ room.fileSource\
+(eventId) : \x22\x22)\x0a\
+ // o\
+nClicked: openEx\
+ternally()\x0a /\
+/ }\x0a\x0a // \
+ TimelineMous\
+eArea {\x0a // \
+ anchors.f\
+ill: parent\x0a \
+// accep\
+tedButtons: Qt.R\
+ightButton\x0a /\
+/ cursor\
+Shape: Qt.Pointi\
+ngHandCursor\x0a \
+ // onCl\
+icked: controlle\
+r.showMenu(index\
+, textFieldImpl.\
+hoveredLink,\x0a \
+ // \
+textFieldImpl.se\
+lectedText, show\
+ingDetails)\x0a \
+// }\x0a\x0a //\
+ Component.o\
+nCompleted:\x0a \
+// if (v\
+isible && autolo\
+ad && !downloade\
+d && !(progressI\
+nfo && progressI\
+nfo.isUpload))\x0a \
+ // \
+ room.downloadF\
+ile(eventId)\x0a \
+ // }\x0a\x0a}\x0a\
+\x00\x00\x00\x9f\
+i\
+mport QtQuick 2.\
+0\x0aimport Quotien\
+t 1.0\x0a\x0aNumberAni\
+mation {\x0a pro\
+perty var settin\
+gs: TimelineSett\
+ings { }\x0a dur\
+ation: settings.\
+fast_animations_\
+duration_ms\x0a}\x0a\
+\x00\x00\x02\x06\
+i\
+mport QtQuick 2.\
+9\x0aimport QtQuick\
+.Controls 2.2\x0a\x0aR\
+oundButton {\x0a \
+ height: setting\
+s.fontHeight * 2\
+\x0a width: heig\
+ht\x0a hoverEnab\
+led: true\x0a op\
+acity: visible *\
+ (0.7 + hovered \
+* 0.2)\x0a\x0a disp\
+lay: Button.Icon\
+Only\x0a icon.co\
+lor: defaultPale\
+tte.buttonText\x0a\x0a\
+ AnimationBeh\
+avior on opacity\
+ {\x0a Norma\
+lNumberAnimation\
+ {\x0a e\
+asing.type: Easi\
+ng.OutQuad\x0a \
+ }\x0a }\x0a A\
+nimationBehavior\
+ on anchors.bott\
+omMargin {\x0a \
+ NormalNumberA\
+nimation {\x0a \
+ easing.ty\
+pe: Easing.OutQu\
+ad\x0a }\x0a \
+ }\x0a}\x0a\
+\x00\x00\x02\x01\
+T\
+imelineMouseArea\
+ {\x0a property \
+var authorId\x0a\x0a \
+ enabled: paren\
+t.visible\x0a an\
+chors.fill: pare\
+nt\x0a cursorSha\
+pe: Qt.PointingH\
+andCursor\x0a ac\
+ceptedButtons: Q\
+t.LeftButton|Qt.\
+MiddleButton\x0a \
+ hoverEnabled: t\
+rue\x0a onEntere\
+d: controller.sh\
+owStatusMessage(\
+authorId)\x0a on\
+Exited: controll\
+er.showStatusMes\
+sage(\x22\x22)\x0a onC\
+licked:\x0a \
+controller.resou\
+rceRequested(aut\
+horId,\x0a \
+ \
+ mous\
+e.button === Qt.\
+LeftButton\x0a \
+ \
+ \
+? \x22mention\x22 : \x22_\
+interactive\x22)\x0a}\x0a\
+\
+\x00\x00\x1a8\
+\x00\
+\x00l\xe4x\x9c\xd5=iw\xdbF\x92\xdf\xfd+\xda\
+\xdc7\x09iK\xd0\xe1\x5cC\x8f\xec\x91,'\xf1\xae\
+e;\x96bO\xde\x9b\xb7N\x13h\x92\x18\x83h\x06\
+\x87df\xc6\xff}\xab\xfa\x02\xfa\x02);;\xfb\x16\
+\xcf\x96H\xa0\xbb\xd0]]wU\xb7\xf2\xd5\x9aW\x0d\
+\xf9\xa9\xf9\xa9\xcd\xd3\xf7\xe48\xf9\xeaNn\xddJ\x9e\
+\xf0\xb2\xa9xQ\xc3\xb3c\xf7\xd9s\xba\xe1mS\x93\
+\xa3\xe4\xe8\xce\xc1\x011O\x7f\xa8\xe8z\x99\xa7\xb4x\
+:\x9f\xb3T48$\xd0\xe0{^\x919-\xd3\x0d\
+Y\xe6\x8be\x01\xff\x9b\xbc\x5c\x18\xa8-orV6\
+\xd8\xfa\xce\x9d\xd7\xd0\x91\x96\x8b\x82\x91\x7f\xde!p\xe5\
+\xd9\x94T\x9c7w\xc4\xb7\xab|\xc5\x8a\xbcd\x97\xac\
+A\x10\xb5j\xa4\x1b\xd6\xea\xb6\xb9Y1\x9a\xf1\xb2\xd8\
+\x90u\xc5\xd7\xacj6d\xc6yA\xda\x9a\xbd\xab\x97\
+m\xd3\x14\xec]\x96\xd3bJ\xaei\xd1\xb2\xf1\xe8\xe7\
+g\x07\xee\xb3\xd1\x1ei\xaa\x96M\xee\x18\xa0O8\x0c\
+\xbc\x84\x11'\xbc\xc4\xcf\x05k\x18\xbc<\xe5e\xcd\x0b\
+\x96\x14|\x01\x80j\x18\x07i\xd4p\xc9\x1c\xb09%\
+#r_|\x9a\x08P\x1f\xc5\xcf\xcbM\xdd\xb0\xd5+\
+\x0a@\x1a\x98\xb3\x98F\xc6\xe6\xb4-\x1au\xf3!@\
+.x\xf5C\xc5\xdb\xf5\xd4n\x9f\x9c\xa6M~\xcd\x86\
+@\xe55\x9d\x15,\xdb\x01\xd6\xb9j\x0a\xd0\x048\xd1\
+\xd2\x1dL2\xa35\x13\x8f+\x9a\xe5m=%\xc7\xb2\
+\xf5\xbc-a,\xbc$\xcbvE\xcb\xcb\xfcw6\x9e\
+m\x1aV\xcb\xb9\xf6\x96iN\xc6w{O\xba\x95j\
+\xda\xaa$\xbf\xd5W\x15`\xaf|_\xf2\x9b\x12P\xaf\
+?\x12\xda44]\xae\x90Nj\x00>\x9aX\x10\x05\
+@\xf2\x17\xf2\xd5\xe1\xe1\xe1\x00\xdc?=/\x096\x1d\
+\xd7\x13\x84\x0d\xff\x9d\x91H8'\xe4\x826\xcb\x04\x90\
+Tf\x0a\xf4\x019\x02\xc8\xe2W\xf0\xc5\xc7\xdb^|\
+D\xde\x9f\x8d&\x09\xad\x16\xe3\x7f\xe3;/\x22\xef\xf4\
+\x1a\xfe\xa0\x1b\x0e\x8dB\xd3\xad\xbd\xe0\xab\xfc\xc3\x13$\
+\x95z\x8c\xb4!>\xee\xe1M\x96u\x9f_Sh\xea\
+\x92\x82\x1a\xc4OM\x02L\xdb\xf4z[s\xc2\x0b\x9a\
+T\x8b\x19\x1dwP\x13\xeb\x1d\xc9\xc2\xfa6\xeb\xbd\xd3\
+\x1a\xb3+\x5c\x04F\xa5\x80Y\xfd\x08\xa2\x82U\x1d\x97\
+\x83\xbcZ\xc2\xa4\x92\x82\xcd\x81u\xd7\xb4B\x86\xc7/\
+^\x8b\x0a\x05\x9ai\x22\xbeym\x1a\xbe6-\xe0\xb3\
+y\xbed\xb2\xf3R\xbc\xfd\x8a}h\x12y\x0bD\xc5\
+\xd7\xdd`\xc2\xbcx\x93\x97\x19\xbf\xe9\x08\x89W\x00#\
+\xd1mm\xceW\x8d\xf1\x0d\xdd\x02\x18\x16\xd6w\xae\xf3\
+:\x87N\x12#\xdd\xeb\x9f\xad\xe8\xa2\x8f\xb3>\xdeN\
+\xafiC+\xeb\x91\x9e\xf45H\x5cT\x07O`\xd6\
+\xac\xb2&i?\x0a\xf6\x8e#\xbe\xdfj\x05D\x9b\x97\
+\xd6$\x06\xf1j\xb5\x92\xca\xab\xc8\xd3\xbcy\x9bg\xcd\
+\x92\x009\xe7\xa0\xb7P\xe0d\x9c\xd5\xe5\x97\x0dPi\
+\xbd\x06\xb2!\xcd\x92\x91\x1aF\xcc\xe0\x09<\xe6s\x17\
+\x126\xa8X\xca@ g\x00\x1616n\x96\xb4!\
+\xb4X\xf1\x1a\x7f\xdd\xd0MM\x96t\xbdf\xa5#\xfd\
+n\xf0\xedS)\x02Vy9\xf6\xc9\xa1\x1b\xe9\x8f\xf2\
+\xc6={\xe8>\xd3\xf4/\x85E\xf1\x1a\x00u\x9c|\
+s\xf4\xdd\x04\x07\xfd\x03/2V\x02%\x00\xaf\x90}\
+\xf2\x8f\x16\x06:Gu\xdd\x96w,\x885o\xabT\
+\x11\x06\xf9\xe2\x0b\xf1;\xa1b\xed/\x18\xe8\xc9gY\
+p\x00\x8f\xc9H\xa0bzp\xb0j>\x1c\xa0\x06\xf4\
+{\x12P\x8d#\x17\x9d\x97t\xceH\x0b\xb8\xaaH\x91\
+\xaf\xf2\x86\x8ck\xc6\x00\x895'\xc0@y\xfa}\xce\
+\x8al\x12\x18#\xea\x9e)\x8a\x0c\xd4\x13\xe3\xfd\xa3=\
+c\x16$\xc2tX\xd3\x14u\xf3=\xf2\xe7\x89=\xc5\
+y^\x14\x17<\x83\xce\x82\xde\x93W\xb0\xf4\xac\xbaf\
+\xa7\x82\x00\xbe\xcf\x1b\xbb\xf9i\x093C\x09x\xc6\x96\
+\xf4:\x07\xa4\x01\xf5H\x0c\xff\xd3\xc3\xc5\x0b^\xadh\
+\xf1\xa2]\xcdXe:\x82\x8ef\x14\x0d\x85\xa4\xd9\xac\
+\xe1\xb5O\xe5\x97\x97-\xd8Y4SZ]_\xdd\xb7\
+\x8f}S\xa4hWe\x803;\x02\x1a\xe0\xad\x8e}\
+\x1d\xa9\xd5o\xaa\xa4\x1b0l\x0dC>\x152\xff\x0c\
+\xcc#^\xc6\x992&\xed\xfamz\x8ck/\xa2\x5c\
+\x1f\xef>\xce\xe5i\x06t\xe0#W\x8b\xa2\x17t\xc5\
+\xbc\x87\x8a\xb5\xf4\xf3\x0b\xd6TyZ'4\xbb\x86\x91\
+0\xc1;^\x1f-=\xdcN\x01\x11\x82WZ\xe40\
+_\xb4\x12\xefx\xcf\x22\x16\xe8\x92\xd6\x08\xd8\xe1'\x90\
+\xd9\xeb\x82n\xf0\x09\xb9{r\xe2r\x85F\x83\x1aN\
+\x00\x13.6T\xcb`;i\x93\xea\x96\x09~\x0d\xb6\
+\x03\x0b\x16yB\x08\xa3\xa7\xf8\xf9u\x10\x09\xa6\xe9[\
+\x89\xf0\x9e\x08\xbb\x09\x22\x19\xaf\x06\x1e\xf7\xc6\xa0\xb0\x02\
+R\xc3C\xc7T\x19+\xe3\x92\x93\x12nLF\x13\x0f\
+\xe2G\x1f\xfd\xf6\x0b\xf4:\x8aqf\x1ew\xe0\xa5\xb4\
+\xe7\xb8\x1b\x8a\xadt\x89\xa7Y'}\xd5\xea\xc1C\xb4\
+&3\x90\xb2\x8a@\x82\xcf\xe7t\x95\x17\x9b\xce\x7fI\
+zw\xc3=\xd6\x1cL&)\xe9\xecN\xe6A\x80\x12\
+K\x5c\x10!iL\x1fy\xf3\x1d\xca\x9f \xe9\xbe,\
+q\x5c\xc1\x91\xd7\xac\x00\xb1x\xb6\xf9/\xb6\x99qZ\
+\xc5&\xa8\x9b]p\xf0\xacb\x5cr\x05\xbc\xab\xb00\x1a\x91i\x10W\x91\
+\x97\x0e\xaf\x07\x00\x94\xb2\xe1j\x99\xd7rN@\x1bd\
+\xc6\xc0\xb0i\xd7\x0b\xb0rY\x96\x8c&C\xaf\xecM\
+\xfd\xb1\xf1A\xd5\x0d\x01P!\xf4.\x82\x09\x0dhG\
+!)X4o(\xd8m\xff\xff\xe4\xc1-\x98T\xeb\
+\xfe\x0a\x9cD\xda\xb0\xb0m(iB7\x0d\xb1]\xcc\
+\x02\xc2\xeb2\xadxQ\xbc\xc9\xd9M\x84\x07:+q\
+'\x92u\xdb\x80\x09zZ\x14\xfc\x86|C\x90\xc9\xc1\
+)\x98\x0b\x13_\x80%c0\xf7\x8e\x0f\xff\xa4oj\
+wFX/l\xf20\x04-\x17M+\xb0a\xe1\xff\
+\x8a\xc3\x0f\xf0\x0eJ\x0d\x1e\xac\xd4%\xbclI\x8b\xf9\
+\xbe\x08\x19\x01\x05S\xb2\xcc\x03JY['\xc6[\x10\
+C\x12\xa4\x97\xc2\x92\xc3\x94\xa4\x8b\xb0'\x02g\x9d\x03\
+\xf1\xf5\xb0\x8f \xae\x88\xa9\xfcM\xf2\xb5\xcf\xf8Cf\
+\x8f\x5c\x9d3\xb0,\xc1\xd8\xcb\x7f\x87a\xd1\x02h\x11\
+\xe8\x1e\xc8\xba{x*|\xa2\x97\xf3\xf9\x00\x00\x8d\xdb\
+P\xf7\xfa\x05c\xc0\xdd\xfe\xfb\x83\xc6\xb9\xc2D\x98v\
+?\xdfB\xc7+`\x82`\x00\xf4\xd9\xdf.\x9e\x82t\
+\x00J\x01\x99\x0d\xcb\xac\x8d\xd9\xfbB+\x0a\x8e\xcaq\
+\xbdW\xb4\xae1\xa0\x97\xf2\xf5f\x7fM\xeb\xc6\xe7B\
+\x007\xaf@\x22\xe9P\xe8\xb3\x86\xad\x92\xdfVEB\
+\xde2R\x02:\x80B\x01\xce{\xa0 XM q\
+\xe0?\x80\xa7B\x96Ap\x80\x1d\xa0\xcc\x1a\xfc\xd5\xd7\
+y\xba\xc4\xb1\x09\xb6y\xd9\xb9\x89;5\x16.\x8d\x91\xe5\
+\xd6\x22\x80\x14\xda\xc5w\x0a_A\xc7i`d=\xd5\
+\x15\x9d\xbfil\xb5\x03\x9f\x22\xderF\xd3\xf7\x0b\x91\
+`\x9e\x12\xf4]\xc0\xc5:\xb8G\xae^\x9e\xbf\x9cv\
+\xf5\x12+\xba&\xf7\x0e\xfa\xde\xd4\xf3\xbcn\x02\xce\xbe\
+\xa6%AE\xd6\x93\x15\x18\x81 rV\xd2>C\x93\
+\xb0\xb0\x9e\xc3w\xb6\x00CljyR\x01\x86Q\xfa\
+F\xbfE%\xd1\xf6I\x9dJ6B\x89\x17\x89!\x5c\
+C\xfb\xde\xf8\xdc\xc7+~-2\x1d\x06\xb4\xbc\x81\xfa\
+V\xad\xfa9,z\x22jSB\x8e\xdb\x7f|\xfb\xe0\
+\xdb\x8721\xc9\x8bV\xf8\xabsD,\xc9\xcbP\xf3\
+e\xd3\xac\xeb\xe9\xc1\xc1\xac]T\x0cKo\xea\xe4\xb7\
+&\xc9\xf9\xc1\xac\xe275;\xf8\xe9\xea\xc1\xf9\xe5\xfe\
+\xb7\xdf}\xe5u\xd6\xc8O\x00it\xf3\x9a\xc18]\
+\x9a\xc0\xcb\xb6N\xf5\x8a\xcbj\xa1\xf3\xbcbBnL\
+;hg\x82\xd3\xae\xf8\x95\x93\x22\x9a\xa3\xa6D\xa3\xa5\
+\xd7\xe9{}/y\xa3\xe0\x8a;~\xbfs\x96\xc2\xca\
+\x8a\x84&t\xfb\xee\xf0\xf0\xd0j3C\x04\xd5:*\
+\xd0\x87{\x09B\xe7\xb49\x13\xcf{$\x8c\xd7:\xff\
+\xc0\x8a\xd3\x22_\x94Ly)\x88\xcf'\x14\xf8\xb3\x96\
+\xfai\xbfDb\x02\x16\xa8\x01\xf9\x846\xbf<-\xed\
+\xb0SJ\xd3%X\xd1\xf39\x1aD\xc70(\x87\xe3\
+\xc54\x13\xed\xe7N\xc9H\xdd\x1a\xd9\x0d}\x8f\x18\xd8\
+\x94H\x91\x85Y\xe67\xd2*z\x06\xce\xd9\x07\xf40\
+dd\xfa\x90<\xf6\xd6T\x8e\x11$\xca!H\x86\x1c\
+\xdb\x9f6c\x15?\xfa\xdb\x1eQ\x9f~!\xf7u\xd8\
+d\x9f\x1c\xa1\xb1\xb3\x7f\xb4e@\xc2E/9\x0a\x98\
+\x0b^\xb1'\x12\x90o\xf6H\xb3L\xdb\x96\xec\x1a\x1a\
+\xd5?\x02i\xf0j\xf3\x9f|f\x1e\xd0\xa2Pw\x9f\
+s\xea\xc5z\x0e`\x1d0\xacR\x8a\xb8\x0d\x86\xe3$\
+ \x82yc5\xf0\xb6\xccq\xf4*\xff\x0e*,\xc7\
+\x85\xda2\x0b\xb8SHX\xe7\xac\x84\x1e\x1b\x7f\x02V\
+\xb0M`\x19l\xec\x1e\xca\xd5\xe7\x03\xa7\xe1\x94\x1c&\
+\x87\x0fB\x0c\x8a\xf71\x0e$\xb2\xf0\x14\xc8\xa8\x9a\xe5\
+ME+1\x9a\x9a\x8b\xe0\xaa\x9a\xa9\x8d\x05\x8b\x18\x0a\
+Z7\xc6T{*\xb0\x01\xaf\xdc\x81\x8a\xc0\xa1C\x05\
+\xe8\xf6\xf5\x03\xc7\xfd\xa0\x8b\xb7n\x8fC\x03\xc0IG\
+\x86|M+a\xa7\xa3\xab\xfe6o\x96\x97\xc2\xd3\xcf\
+\x1d\x07\xc7^\x17\x1c\xff\x05\xad\xde\xb3JQ\xd7+\x0e\
+s\x04]\xbc\xc8\xcb_vY\xd8\x0e\x00\x0a\x22\x94\x85\
+\x08\xc1\x9fi\xe0=\xe4/\x1do\x08\xe6\xd9\xad\xd7\xa3\
+\x00G=&\xa6\xd6\xa7\xeb\x03\xae&\xd31\xdb\x1da\
+\xef\x1b\xd8NA\x83.\xd1\x02\xcb\xe6\xfdk\xd3s\x1c\
+2\x0b\x83\x80O\xd0\x1d\x9f\x81p\x00\xad4\xd6\xd0\x82\
+\xdd%\x08Q\xcfu\xb7\xafr\x91\x8d\xfb\xdf\x93>\xe6\
+\xeb\x96\x16BR\xdd\xce\xb3~4 \xae\x06]\xd9\xc7\
+\x9aD\xc8\xb4\xbf\x1a\x16{\xfa!\x97@\xe0\xbb_\xe9\
+\x89x\x05{\x05g$\xf0\x0c>\x0bm\xe4\x08G\xc3\
+!\x83\xcf\xc4\xcb}22\xf3F\xa9\x86\xdf\xb4!\x11\
+Z\xcd\xe1\xc1\x8c`}\x0b`\x0c\x8c\x81\x81\x0c\xea\x03\
+SH\x03Gr\x7f\xcb\x8c\xdc.\x88\x5c}\xcbNM\
+\x0c\x8f\x85\x8alK\xc9o\xdc\xc0\xea\xc7\x08\x81\x83\x88\
+n+\xf6\xaab\xa0\xd4\xdbZ\xcd9H\xa8\xe8\x22y\
+\xca)L5\x92\x9e\x83\x01\xfd+\x8c\xaf\xa3\xe1\xa5d\
+&\xa9\xd7\x00q\x0f\xe8\x8b\xd0\xbanW\x8c\xdc\xc03\
+\x10\xe1\xd2H$\xdf\xe1\x07\x06\xa3<\xf0k1P\x00\
+^\xb3\x82\xa7\xa0d\x80\xe3\x94\x0d\xf8\x98\xeck\x03\xea\
+\x8d~\x18\x0fM\xaa+\xad\xda\x1c\x13\x14&\x7f\x91T\
+mY\x0ap\xb7b1\x1f\xce\xf5\xeec\xb0<;-\
+\xc9\xee\x91\xefBx|\xb2d\xe9{\x5c\x93\x1b\xf6%\
+f\xc6\xc0:k0\x7f1kWk\xd4I\x5c\x22\x99\
+\xe5\x05N\x22l\xd6\x1e\xa3\xf5\xc4\xc1h\x03\x95\x99!\
+\xac\x0df\xd3*\xa9\x80D\x7f\xba\x12\x8a\x18HJ\xb1\
+\x9c\xbf\x08\x00\x87\x95\xbc],\xf1\xedj\xd1(\xf6\xc6\
+\x8c2fP0I\xf2@\xa6\xed\xd4\xeb\x82\xa4e\xf0\
+d\xcc\x01%f\xf6\xcd\x1a\xdf;\x06\x15\xa2\x18$&\
+JC*\xf4\xa4\xa3\x92{\x96e\x02_}s\x02/\
+\xa1\xa0\x17\xacq\xf9\x22\x00}\xf7P3^X\xf7.\
+\xe7\xa5\x83\xe61\x0e\x0cw\x93B`k\xdf0\xaf\xd7\
+\xf4\x9ai\xad\x1deq\x11J\x8a#\xc5\x821\xa0M\
+v\x8d\xf9\x86\x0d\xf0\xddd\x17/\x85\x06x\xcd\xc0\xed\
+\x0f\xce\xc7R7R\xab\x1aG\x19K\x14\x9b@l/\
+\x8e\x830\xc1\x01y\xa7Lzj\xe30\xde\x80C\xd0\
+\x00\xd7\xf6\xb5\x95\xcd.y\xa3\xd9G&\xc4WA\x10\
+\xbb\xd0\x88\xbeP*\x22\x9dJQ\xf2J\x18\xed\x80\xac\
+\x93n\xfd2p\x1b\xfb\xd8\x8e\x00\xb2\xb0'\xa1\x89\xad\
+\x19\x5cy\x02\x1c\xb7\x19\xf8o\x0a\x03\xd3]\x90vN\
+\x1b\xf9Z\xbf\xef^\xe7\xe2\xe2$i\x1e\x806X\xda\
+\xd3\xd1\xba\x80\xfb\xf3z\x9cmb\x94\xeez!R\xe0\
+\xc6\x03\xc7B\x1a\xa9\xdd\x06+\xfaal\xb4{OT\
+\xc1\xdb\x06\xb8\xdf\x19\xdd9\xbf)\xff\xb7\xc6\x97\x97\xe3\
+\x9e)aA\xdaW\x90vO\xcb\xf4L>\x7f\x821\
+\xde|\xbbd\xac\x18\xdf\xe0\xcf\xd8\x0c\xc5\xc3D\xec+\
+8gEC\x93\x0f\xe4\xe4\xe4\x84\x1c\xc6\x84;\x92\xf6\
+F\xb4\x84yz\x9dQ\x9c\x1f\x1d\x82\xd7\xf8\xe0\x9bp\
+\x1e\x11_\xa9\xba\x83\x92\x89\x1b\xbc\x86vd\xe3p\xc3\
+h\x0e\xa0\x83 \xd6w\x7f\x08\x88\x9aC\x17\xb2\x0f\xa6\
+|U\x8c:\x8c\x13\x0f\x84\x1d\xf570\x06\xc8\x12X\
+\xad\x94\x9ec\xa82\xb6\xa1\x15h\xc2~\xd25h\xd7\
+\xbd<\x7f\x19\xba\xcf\xcbW`6\xfc\xbc\xc6\x92\xf0:\
+\x94\xc3Q\xf1\x22c\xed\x1a\xe4\x9b;\x91\xdaa;\xd0\
+\xd4\xbb\xf6uP\xe8\x8c\xc2\xbc\xaa$]\xe6E\x06\xb6\
+&\xeeaI4\x13\xc5\xc7\x8a\xabv\xcb\xd1\x8a\x85\xfe\
+\xe4\xf1\xee8\xe2\xe0\x80;{\xf1\x8a\xf7\xc2\xa1!i\
+\xab\x0cn\xf1e\x9b\x9cu\xb8:\xb2oo\x8b\xc2\xfd\
+\xe9\xe2\xb9\x88\xe3\x92B\x84\x9f\x02\xbaVD\x9a\x13\xf1\
+\xf3\x14\x0d\xd8+~\xc6\x846G\x9f\x07Irl\xfb\
+\xdfA$\x98\x12!08\xe1\x1fjT\x90?bO\
+\x89\x10w\xeb\x82\xe2\xf6\x0b\x13\x06_\xd1\x0d\x18\x91!\
+H\xf8\x04\xbb/AG\x92v\x9da\xb1\x01A\xb05\
+\xb0e\x0a.\x0d\xab1\xd1\xcf\xabL\xe9A|I\x19\
+\x08O#,am\xcc\x10`(|m\xe6mO\xb7\
+o\xd1\x0c\xae\x086\xbcf\xb8\xa9\xef)\xac'n\xe0\
+\xb4l;'b\xc4\xd7m!\x02\xf6\xd2-\x01\x03\x00\
+\xbcUe\x17\xf8\x8b\x888\x05\xcd\xec\x17t\xf5b\xac\
+\x1c\x0b\xdc\x9a\xcd\xe8\xa1(\xa9\x9a\x92\xc3\x87\x80\x8f)\
+9\x1a\xd6\xce4\xcb\xfe\xcf\xc7 #\xf0\x7f\xd8 \xf0\
+\xf5~1\xdb\xed\x86\xbf\xc3\xa8eA\x7f\xca>\x17\x7f\
+1\xa9d\xcf(\xd6j\xa0\xa2/\xd4\xe5\xdf\x80\x97~\
+\x91\xa2\xb1R\xfcY\x9a\xf2Z\x93 \x94w\xdeQ=\
+\x86\x1a}\xce\xbbN>)n \x89\xc6\xb1\x98\x81\x13\
+g\x17\xbf.W\x9c\x83\xb8\xc8bk\xd1\xd3\x0eb?\
+\xb6\x90\xeb\xa6q\xaci\xd6\xeatMW\x8c\x06\xf8\xed\
+M\xeb\x9dn\xf2n\x85;R\x1f\xc4 \x81A\x9b\xaf\
+\xda\x95\x5cT\xcc\xeb\xed\x06\xd2\xcd)\x98\x8f\xbc|-\
+\x91a|\xd5\xe8|\xc5\x9cE\x15\x83\xec1\x19j\x88\
+W\xa7}m\xa1\x17\xe9\xf6\xd1}0HQ\xc1\xfa\xd7\
+`\x8c7\xc8L\x7f\xc4v5w\xbcB\xb5\xa5XO\
+U\xcb\xa8\x0cV\x8a\xd0\x19\xdc\x10_\xf3\x86\xad\xa2n\
+\xa5\x0bKy\x99\xa8\xbd\xb0\xb2D\xd6`+\x14Zm\
+c\xe5px\xfd\xee%\xb3\xf0\x1a\xacX1\x8d\x02\x85\
+r\xa7E\xa1\xbez\xcd\x81\x8cX\x01\xb8s\xaa\xfe\xe6\
+b\
+\x87\xd4\x88\x1e4\xac\xcc\x1d\x19\xb2O\xc83\xbf~\xe2\
+@\xed\xe6E\xcf^6F\xe2\xc4\xde\xca$\x93\xc1\xdc\
+=\x17\x98\xa8\xe1\x0e\x99\xcc\x07\x86\xc2\xbf\xac\xd5\xe8\x1e\
+\x12\x14\xa4\xd5M^\xb3=x\xf2\xa5\xca\xfa\xcd\xd8\x86\
+\x97\x99h\xce\xb2\x05s\xb7\x19kp\x1a%\x98l\xc6\
+\xb6\x99N\x94[\x93\xd6\xa3B[Y\xecX\x80'\xe1\
+\xd1\xa9p\xd2\x9e\xe2\xbf.\xbe\x84f\xe3\x12O\xe9\x90\
+|\x18\xa8\x08\xc7\xfe\x7f\xadi8\xa5\xe6\xb5\xd6{\x17\
+\x02\x99\x8a^7\xcc\xd6\xbaD\x16%\xc3\xe1}\xe2x\
+\xe9-\xa0v\x8e-\x5c\xb2\x11\x16\x0aJ\x9bc\xea\xf6\
+(\xb0\xc7S\xed\xa3\x0f&\xf1\x02\xde\x85\xd8a\xd8\x1d\
+\x9b\xe0\xee\xd4\xc7\x83\x10\xf6\xdc\xbd\xfe\xe6\xe4\x94=\x18\
+\xc3\xd7C\xe1\x99]\xa4E7\xca\xcf\x13\x19\x81\x8d4\
+\xbbU\xc6\xb8\xb5WF\x84\xb9\xe5W\xfa\xd2\x84\xe3\x17\
+\xa7\xc0\x8a\x1d'_\xa3VxYb\x1d2\xb2\x00-\
+7\x9d\xab\xa4\xc4\xc6\x9e\xda{!$\x84\xf0P\xfd\xb9\
+\xe3f1<\x8bfJ~P\x9f\x22\xf1\x0a\xfd\x18\x8b\
+F\xd0\xf0S\x9e\xaa0\xdf\xd5\x02\x8f\x1a4m%j\
+F\x01+r\x10\xcc\x91\x01\x13\xa3\x83\xe0\xe6\x13\xfb\x9b\
+\xa7\x82Q\x02\x81\x8c\x14[ML\x8c\x0b\xac \x10D\
+r3\x125&\xec\x1e\xa99\xb4\xd7\xc7 \xb8\x90\xa0\
+Y\xd6\xa6\xb8\xc7\x04\xfdc\x95\x22\x96\xb5H\xeb\x9c\xc1\
+\x83\x9b\x1cH\xc1\x8e\xd8\xe1&\x14V\xd6`\xf5\xd7\xb6\
+\x18\xf1\x93\xf4\x18\x22kq\x83AA7\x00\x18+\xb3\
+\xa6n\xbd\xc5\x89\xce\x9e\x076\x80\xca\x89\x86\xcbY\xec\
+ \x85)n9v\x8fR\x09\xd6\xb5X\x9d\xdfhN\
+q\x86\x1a\x1a\xd1\xf8\xae\xd3(Q\xa0\x14\x10L\x94\xbb\
+-6\xbdZ\x83\xc9\xedL\x02k\x9c!\x96y`3\
+\x8ca\x96Z\x87\xd0\xc4\xd1\x15\x22\x86\xf0\xfb\xc9\xf1g\
+\x08\xdf]\xad\x0aSf\xd7\x0b\x1aI\x81r\x9f\xf8\xef\
+\xef\xf4\x88\x17c\x0a\xb6\xf7\x85Zh%c\xb2z\xf8\
+\xdc\x15}\xf5\x94\x84\x9f\xec\x8c\xed\xd6\xc5k\xdb\x1eq\
+\xd3\xe6V\xfbBM\xafO\xd8\x1b\xbau:x}\xd2\
+\xbe Y\x92oV\xc1\xe5\x9cmy\xeaX\xc7\xc4D\
+\x02\xd5\xba\xee\xb6\xa1\xda\xfe\xa4\xccn`\x0c\x8c\xdf_\
+\xf5\x8bQ\xe1\x86~\x86\xbb\x9a\xf0\x04\xb1L\xca8a\
+\x0f\xcajM\xa3\xf8D\xb6\x18Cf\xa2O\xec\xd4!\
+Q\x12\xa8\xf2\xafg\xb4\xa7\x8d\xd0\x92E\xb9\xf3\xa1'\
+}\x10\x22\xf5\x1ci\xab~\xab\xd2)]U\x8d%!\
+\x8b\xdd\xe9v\x08\xb8S\xec\xc1\x8a/\xd36\xe2DF\
+\xdf\xe2;\xc71?2x\x98\x89]\xa2\x05\x92\xa7\xa2\
+\x0b\xf6\xf4Z\x09\xfc\xd8\x14\xc4\x8b\xef\x0f\x0e\xcb\xb5\x5c\
+z^F\xd0F; \xe3\xdb\xc0\xb7\x15G\xdf7\x09\
+\x22\xd0\x9d\x98\x8f\xb5pp)\x883-]\xbbBt\
+\xbd\xe9\xa6_A\xec>\xf5\xbak3\xcc)\xde\x88\x94\
+\xdd\xcb\xdb\xca\xf7\xb3C\xa3\xee\xdc\xeeu\xa8\x0e\xe7\xc8\
+Mw%\xff\xfb\xc3\xeeJ\xc5\xdf\xea\x83\x8bLs\xcf\
+\x91\x08\x83\x07\x0dz\x18(\xd3s\xc7i=\xbc\xe7-\
+\xff\xfe\xb6\xd7t$`\x14\x8dU\xc1\xad\xd4\x8bi\x15\
+\xd6+\xab\xa0\xbe\xff%\xf6\
+H\xde\xf0\xea\xfdCu\xa8\x09m\xd4\xd6\x19\x09\x0c_\
+\xb6\xc0\x0cT\x09\xba`\xb6\xe9\x83\x19H\xa9bY\xb1\
+\x90\xa6X\xd0=c\xe6d\x0eX\x90\x05\x13\xb13t\
+\x04\x87\xc6\xd4\xb1\xe5\xb6\x04\xaa\xf0\x0b\xdd\x8c\xac\xd5\xc2\
+\x142\x85v\x15\xf6\x12\xd0:pd;\xc1\xba\x84x\
+\xea\xef0\x04\x81\x1f\xba7\xe4*\x0d\xa5[#\x99V\
+k*<\x909\xbaO\xc6\xfe0\x1e\x05\xadxC\x87\
+\xc1=\x06}\xab=T}\xa4\xf2\xb4!L\xdc=q\
+\xb7+\xf1\x12C\xb4b\xdb\xf9\xb6\xbc\xadM\xe7\xa7\x1d\
+\x1d\x81J\xfe\xb2Q\x159\xe4\xd7\x86\xff\xaaXK\x98\
+\xe1@\x14\x1b\x91\xf4T\xc3zHj\xde\x87\xa3\x1b\x19\
+\xba\x92}S\xc1\xd35\xa9\xf3E)\xc8\x10\x0b\xe8g\
+,\xe5+4@~g\x15\xc7\xfa\xc4<\xed\x9d\x0f\x8b\
+\xf4\xd9V0\x1eh\xba\x87,\xf8[\x9b\xc3\x80Z\xdc\
+u\x22_\x83;\xf0\x17\xa28\xa8\xf7>*\xf4\xef\xc4\
+\xca!\x01(\xb3\xcc\xc2w\xc0\xd2}Q\x94\x0cT\x0a\
+\xceC\xd2\x13BBl\x19\x11\x14\xa8:\x90\x9dz\x88\
+\x8c\xd5k\x09v\xc53\xf3\x14k\xe3IY\x96\xc3c\
+\xd1\x84\xa2+\xf5fS\xa3\x14\x1d\xc0$\x0c\xc6\x22\xad\
+O\x00fE\x98BY\xe9\xae^Bj\xa0\xcf\xf7\xa5\
+v\xf2\xe5\x87l|m\x89m\xb5\xb3\xd5\xf5xg\xa3\
+\x8d`\x9c\xe4\xb6\x9eJ(\xe9\xfe\x82\xab\xd3P:\x97\
+\x09\xe5\xfbS\xed#\x98x\xea\x96\x13\xc0u\xc2\x13\x0f\
+\xf5\xa9cH\xb2\x0c\x84\xe0q\x03\xdb\xd6h(\xc8\xdd\
+\x15\xb5\x0c\x07\xb7\xbbva\xcb\x87\x16`>\x95 c\
+\xceh\xaf\x82\xd6Ic,\xf9\x8d\xe3\x05\x8c\xb7ER\
+\x1e\x9d\x84\x0cBQ\xa1\x82\x07\xce\x0cXO\xb8\x9f\xdd\
+\xae\x04\xd25F\xa1\xa3g\xa0\xf5\x96\x00\xa5\xed\xb4\x81\
+h\xc6\xd9\x0cX7b\xb6!\xd3fN3\xf6\xb2m\
+\xceqW{R\xc3B\x05\xf6\x10h\x9b\xee\xc4\x8b\x83\
+\x7f\x0cWO[@}\xa9\xd6\x19D\xa80\xab\x80\xfe\
+\xef\x03\xb0\x1f\xa2a|\x8d\x7f\xae\xe2\xd8\xdd\xd6\xce\xcb\
++ \xd2\x85%\
+\xa5\x11Me\xdc*\x85!S\x17nl0\x94\x16'\
+(\x0b\xb72\x94\xd8$\x104h\xcciC\xcf1Y\
+\xda\xa8\x98]\xe44 s\x00\x11\xfeu\x0b\x13\xddK\
+\xdfK\x87\xb3\xe47\xa3\xbd\xd1\xae\xfb6#\xd1\xcd\xe8\
+&\x83\xfbd\xf4\xf7\x12\x0fu\xf7F \xc3t\xea\xaf\
+l\xd8(\x0bB\x9bFN;\xba\xdfGx\x5c\x1aD\
+\xb0\x18X,<\x08\x0a\xc7\x1c{\xa1z\xab7!\x13\
+\x11\xed\x1cyq:|\xb5\x05\xb7\xc4\xc1@p\x0e\xd1\
+\x95u\xc7\xd8\xb9\xc9\xe28\x8a\x95\xa8U\x10\x0c\x97\x88\
+\x1b\xf6\xb1\xb3\x96\xd1!U\xd2\x15\xf7\x0e\x19\xeb,\x0f\
+x(\x08\xc0\xd5\xa9\xb7\xd6\x83VT\xd0\x0f\xfc\x0d\x9f\
+\xb8\x13L3\x98\xf3\xbe\x1ec\xfd\x0b\xf8%z\x8b<\
+\xd9_:\xd1)\x13\xf5\xeb\x0aa\xd5\x81\x1c\xdd\x94S\
+\xcf\xf3*\xc5q\xec\xa3\x05\xdf\x97\xef\xb5\xfd&\xfd\xe7\
+\x0fF\xbfU\xe9\xf4\xe0\xe0@\xa2\x00\xff\x12DR_\
+/F!1\x18=\xad.\xb2\xbf\xe1\x8c\xc1 textEdit.\
+selectionStart\x0a \
+ if (hasSe\
+lection && contr\
+oller.getModifie\
+rKeys() & Qt.Shi\
+ftModifier) {\x0a \
+ textEd\
+it.moveCursorSel\
+ection(textEdit.\
+positionAt(x, y)\
+, selectionMode)\
+\x0a } else \
+{\x0a te\
+xtEdit.cursorPos\
+ition = textEdit\
+.positionAt(x, y\
+)\x0a if\
+ (chatView.textE\
+ditWithSelection\
+)\x0a \
+ chatView.textE\
+ditWithSelection\
+.deselect()\x0a \
+ }\x0a }\x0a \
+onClicked: {\x0a \
+ if (textEdi\
+t.hoveredLink)\x0a \
+ textE\
+dit.onLinkActiva\
+ted(textEdit.hov\
+eredLink)\x0a }\x0a\
+ onDoubleClic\
+ked: {\x0a s\
+electionMode = T\
+extEdit.SelectWo\
+rds\x0a text\
+Edit.selectWord(\
+)\x0a }\x0a onRe\
+leased: {\x0a \
+ selectionMode \
+= TextEdit.Selec\
+tCharacters\x0a \
+ controller.s\
+etGlobalSelectio\
+nBuffer(textEdit\
+.selectedText)\x0a \
+ chatView.\
+textEditWithSele\
+ction = textEdit\
+\x0a\x0a contro\
+ller.focusInput(\
+)\x0a }\x0a onPo\
+sitionChanged: {\
+\x0a var x =\
+ mouse.x\x0a \
+ var y = mouse.y\
+\x0a if (tex\
+tEdit.flickableI\
+tem) {\x0a \
+ x += textEdit\
+.flickableItem.c\
+ontentX\x0a \
+ y += textEdi\
+t.flickableItem.\
+contentY\x0a \
+ }\x0a textE\
+dit.moveCursorSe\
+lection(textEdit\
+.positionAt(x, y\
+), selectionMode\
+)\x0a }\x0a}\x0a\
+\x00\x00\x02y\
+i\
+mport QtQuick 2.\
+6\x0aimport QtQuick\
+.Controls 2.1\x0a//\
+ import QtQuick.\
+Controls.Styles \
+2.1\x0a\x0aToolButton \
+{\x0a width: vis\
+ible * implicitW\
+idth\x0a height:\
+ visible * paren\
+t.height\x0a anc\
+hors.top: parent\
+.top\x0a anchors\
+.rightMargin: 2\x0a\
+\x0a // style: B\
+uttonStyle {\x0a \
+ // label: T\
+ext {\x0a // \
+ text: contr\
+ol.text\x0a // \
+ font: set\
+tings.font\x0a /\
+/ fontSi\
+zeMode: Text.Ver\
+ticalFit\x0a // \
+ minimumP\
+ointSize: settin\
+gs.font.pointSiz\
+e - 3\x0a // \
+ verticalAli\
+gnment: Text.Ali\
+gnVCenter\x0a //\
+ horizon\
+talAlignment: Te\
+xt.AlignHCenter\x0a\
+ // r\
+enderType: setti\
+ngs.render_type\x0a\
+ // }\x0a \
+ // }\x0a}\x0a\
+"
+
+qt_resource_name = b"\
+\x00\x03\
+\x00\x00x<\
+\x00q\
+\x00m\x00l\
+\x00\x19\
+\x0a\xbe\xd5\x1c\
+\x00N\
+\x00o\x00r\x00m\x00a\x00l\x00N\x00u\x00m\x00b\x00e\x00r\x00A\x00n\x00i\x00m\x00a\
+\x00t\x00i\x00o\x00n\x00.\x00q\x00m\x00l\
+\x00\x0e\
+\x0f\x18||\
+\x00A\
+\x00t\x00t\x00a\x00c\x00h\x00m\x00e\x00n\x00t\x00.\x00q\x00m\x00l\
+\x00\x0d\
+\x02\xd5\x9d\x5c\
+\x00M\
+\x00y\x00T\x00o\x00o\x00l\x00T\x00i\x00p\x00.\x00q\x00m\x00l\
+\x00\x15\
+\x07d\xb1\xbc\
+\x00T\
+\x00i\x00m\x00e\x00l\x00i\x00n\x00e\x00M\x00o\x00u\x00s\x00e\x00A\x00r\x00e\x00a\
+\x00.\x00q\x00m\x00l\
+\x00\x16\
+\x03g[\x9c\
+\x00A\
+\x00n\x00i\x00m\x00a\x00t\x00e\x00d\x00T\x00r\x00a\x00n\x00s\x00i\x00t\x00i\x00o\
+\x00n\x00.\x00q\x00m\x00l\
+\x00\x10\
+\x02\xf2\x04\xdc\
+\x00T\
+\x00i\x00m\x00e\x00l\x00i\x00n\x00e\x00I\x00t\x00e\x00m\x00.\x00q\x00m\x00l\
+\x00\x14\
+\x01/\xc1\x9c\
+\x00T\
+\x00i\x00m\x00e\x00l\x00i\x00n\x00e\x00S\x00e\x00t\x00t\x00i\x00n\x00g\x00s\x00.\
+\x00q\x00m\x00l\
+\x00\x10\
+\x03\x7f\x92\x9c\
+\x00I\
+\x00m\x00a\x00g\x00e\x00C\x00o\x00n\x00t\x00e\x00n\x00t\x00.\x00q\x00m\x00l\
+\x00\x17\
+\x05\xdbD\x5c\
+\x00F\
+\x00a\x00s\x00t\x00N\x00u\x00m\x00b\x00e\x00r\x00A\x00n\x00i\x00m\x00a\x00t\x00i\
+\x00o\x00n\x00.\x00q\x00m\x00l\
+\x00\x12\
+\x0el9\xbc\
+\x00S\
+\x00c\x00r\x00o\x00l\x00l\x00T\x00o\x00B\x00u\x00t\x00t\x00o\x00n\x00.\x00q\x00m\
+\x00l\
+\x00\x19\
+\x0a\xd8\x0c\xdc\
+\x00A\
+\x00u\x00t\x00h\x00o\x00r\x00I\x00n\x00t\x00e\x00r\x00a\x00c\x00t\x00i\x00o\x00n\
+\x00A\x00r\x00e\x00a\x00.\x00q\x00m\x00l\
+\x00\x0c\
+\x07(0|\
+\x00T\
+\x00i\x00m\x00e\x00l\x00i\x00n\x00e\x00.\x00q\x00m\x00l\
+\x00\x0f\
+\x04\x874\xbc\
+\x00T\
+\x00o\x00o\x00l\x00T\x00i\x00p\x00A\x00r\x00e\x00a\x00.\x00q\x00m\x00l\
+\x00\x15\
+\x0d\x93\x85\xbc\
+\x00A\
+\x00n\x00i\x00m\x00a\x00t\x00i\x00o\x00n\x00B\x00e\x00h\x00a\x00v\x00i\x00o\x00r\
+\x00.\x00q\x00m\x00l\
+\x00\x1c\
+\x0a\x96\xbd\xfc\
+\x00T\
+\x00i\x00m\x00e\x00l\x00i\x00n\x00e\x00T\x00e\x00x\x00t\x00E\x00d\x00i\x00t\x00S\
+\x00e\x00l\x00e\x00c\x00t\x00o\x00r\x00.\x00q\x00m\x00l\
+\x00\x1a\
+\x0e\xec^\xdc\
+\x00T\
+\x00i\x00m\x00e\x00l\x00i\x00n\x00e\x00I\x00t\x00e\x00m\x00T\x00o\x00o\x00l\x00B\
+\x00u\x00t\x00t\x00o\x00n\x00.\x00q\x00m\x00l\
+"
+
+qt_resource_struct = b"\
+\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\
+\x00\x00\x00\x00\x00\x00\x00\x00\
+\x00\x00\x00\x00\x00\x02\x00\x00\x00\x10\x00\x00\x00\x02\
+\x00\x00\x00\x00\x00\x00\x00\x00\
+\x00\x00\x01\x0e\x00\x01\x00\x00\x00\x01\x00\x00 \xa0\
+\x00\x00\x01z\x7f\x7f\xa9\x0b\
+\x00\x00\x00f\x00\x00\x00\x00\x00\x01\x00\x00\x05a\
+\x00\x00\x01z\x7f\x7f\xa9\x0b\
+\x00\x00\x00\xe8\x00\x01\x00\x00\x00\x01\x00\x00\x09N\
+\x00\x00\x01{,_rC\
+\x00\x00\x00\xb6\x00\x00\x00\x00\x00\x01\x00\x00\x08\xbb\
+\x00\x00\x01z\x7f\x7f\xa9\x0b\
+\x00\x00\x01<\x00\x00\x00\x00\x00\x01\x00\x00\x22l\
+\x00\x00\x01{*8,\x94\
+\x00\x00\x02\x16\x00\x00\x00\x00\x00\x01\x00\x00GH\
+\x00\x00\x01z\x7f\x7f\xa9\x0b\
+\x00\x00\x01b\x00\x00\x00\x00\x00\x01\x00\x00(Z\
+\x00\x00\x01z\x7f\x7f\xa9\x0b\
+\x00\x00\x01\xf8\x00\x01\x00\x00\x00\x01\x00\x00-\x0c\
+\x00\x00\x01{,isv\
+\x00\x00\x00\x86\x00\x00\x00\x00\x00\x01\x00\x00\x08H\
+\x00\x00\x01z\x7f\x7f\xa9\x0b\
+\x00\x00\x02j\x00\x00\x00\x00\x00\x01\x00\x00H\xd2\
+\x00\x00\x01z\x7f\x7f\xa9\x0b\
+\x00\x00\x00\x0c\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\
+\x00\x00\x01z\x7f\x7f\xa9\x0b\
+\x00\x00\x01\xc0\x00\x00\x00\x00\x00\x01\x00\x00+\x07\
+\x00\x00\x01z\x7f\x7f\xa9\x0b\
+\x00\x00\x02:\x00\x00\x00\x00\x00\x01\x00\x00HA\
+\x00\x00\x01z\x7f\x7f\xa9\x0b\
+\x00\x00\x01\x96\x00\x00\x00\x00\x00\x01\x00\x00(\xfd\
+\x00\x00\x01z\x7f\x7f\xa9\x0b\
+\x00\x00\x02\xa8\x00\x00\x00\x00\x00\x01\x00\x00O\xc1\
+\x00\x00\x01{*:Y\xd8\
+\x00\x00\x00D\x00\x00\x00\x00\x00\x01\x00\x00\x00\x9e\
+\x00\x00\x01z\x7f\x7f\xa9\x0b\
+"
+
+def qInitResources():
+ QtCore.qRegisterResourceData(0x03, qt_resource_struct, qt_resource_name, qt_resource_data)
+
+def qCleanupResources():
+ QtCore.qUnregisterResourceData(0x03, qt_resource_struct, qt_resource_name, qt_resource_data)
+
+qInitResources()
diff --git a/demo/resources.qrc b/demo/resources.qrc
new file mode 100644
index 0000000..6e3803e
--- /dev/null
+++ b/demo/resources.qrc
@@ -0,0 +1,20 @@
+
+
+ qml/Timeline.qml
+ qml/TimelineItem.qml
+ qml/TimelineMouseArea.qml
+ qml/TimelineTextEditSelector.qml
+ qml/TimelineItemToolButton.qml
+ qml/TimelineSettings.qml
+ qml/NormalNumberAnimation.qml
+ qml/FastNumberAnimation.qml
+ qml/AnimationBehavior.qml
+ qml/AnimatedTransition.qml
+ qml/ToolTipArea.qml
+ qml/MyToolTip.qml
+ qml/ScrollToButton.qml
+ qml/AuthorInteractionArea.qml
+ qml/ImageContent.qml
+ qml/Attachment.qml
+
+
diff --git a/demo/roomlistdock.py b/demo/roomlistdock.py
index 5295efe..6610d39 100644
--- a/demo/roomlistdock.py
+++ b/demo/roomlistdock.py
@@ -1,7 +1,6 @@
from demo.models.abstractroomordering import RoomGroup
from PySide6 import QtCore, QtWidgets, QtGui
from PyQuotient import Quotient
-from demo.pyquaternionroom import PyquaternionRoom
from demo.models.roomlistmodel import RoomListModel, Roles
from demo.models.orderbytag import OrderByTag
from __feature__ import snake_case, true_property
@@ -34,7 +33,7 @@ def paint(self, painter: QtGui.QPainter, option: QtWidgets.QStyleOptionViewItem,
super().paint(painter, new_option, index)
class RoomListDock(QtWidgets.QDockWidget):
- roomSelected = QtCore.Signal(PyquaternionRoom)
+ roomSelected = QtCore.Signal(Quotient.Room)
def __init__(self, parent=None) -> None:
super().__init__("Rooms", parent)
@@ -66,7 +65,7 @@ def add_connection(self, connection: Quotient.Connection):
self.model.add_connection(connection)
@QtCore.Slot()
- def set_selected_room(self, room: PyquaternionRoom):
+ def set_selected_room(self, room: Quotient.Room):
if self.get_selected_room() == room:
return
diff --git a/poetry.lock b/poetry.lock
index 396be57..6c8f41d 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -8,17 +8,17 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
[[package]]
name = "attrs"
-version = "21.2.0"
+version = "21.4.0"
description = "Classes Without Boilerplate"
category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
[package.extras]
-dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit"]
+dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit", "cloudpickle"]
docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"]
-tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface"]
-tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins"]
+tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "cloudpickle"]
+tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "cloudpickle"]
[[package]]
name = "colorama"
@@ -38,7 +38,7 @@ python-versions = "*"
[[package]]
name = "lxml"
-version = "4.6.3"
+version = "4.7.1"
description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API."
category = "main"
optional = false
@@ -52,49 +52,53 @@ source = ["Cython (>=0.29.7)"]
[[package]]
name = "packaging"
-version = "21.0"
+version = "21.3"
description = "Core utilities for Python packages"
category = "dev"
optional = false
python-versions = ">=3.6"
[package.dependencies]
-pyparsing = ">=2.0.2"
+pyparsing = ">=2.0.2,<3.0.5 || >3.0.5"
[[package]]
name = "pluggy"
-version = "0.13.1"
+version = "1.0.0"
description = "plugin and hook calling mechanisms for python"
category = "dev"
optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+python-versions = ">=3.6"
[package.extras]
dev = ["pre-commit", "tox"]
+testing = ["pytest", "pytest-benchmark"]
[[package]]
name = "py"
-version = "1.10.0"
+version = "1.11.0"
description = "library with cross-python path, ini-parsing, io, code, log facilities"
category = "dev"
optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
[[package]]
name = "pyparsing"
-version = "2.4.7"
+version = "3.0.6"
description = "Python parsing module"
category = "dev"
optional = false
-python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
+python-versions = ">=3.6"
+
+[package.extras]
+diagrams = ["jinja2", "railroad-diagrams"]
[[package]]
name = "pyside6"
-version = "6.1.2"
+version = "6.2.2.1"
description = "Python / C++ bindings helper module"
category = "main"
optional = false
-python-versions = ">=3.6, <3.10"
+python-versions = ">=3.6, <3.11"
[package.source]
type = "legacy"
@@ -103,7 +107,7 @@ reference = "qt"
[[package]]
name = "pytest"
-version = "6.2.4"
+version = "6.2.5"
description = "pytest: simple powerful testing with Python"
category = "dev"
optional = false
@@ -115,7 +119,7 @@ attrs = ">=19.2.0"
colorama = {version = "*", markers = "sys_platform == \"win32\""}
iniconfig = "*"
packaging = "*"
-pluggy = ">=0.12,<1.0.0a1"
+pluggy = ">=0.12,<2.0"
py = ">=1.8.2"
toml = "*"
@@ -138,11 +142,11 @@ dev = ["pre-commit", "tox", "pytest-asyncio"]
[[package]]
name = "shiboken6"
-version = "6.1.2"
+version = "6.2.2.1"
description = "Python / C++ bindings helper module"
category = "main"
optional = false
-python-versions = ">=3.6, <3.10"
+python-versions = ">=3.6, <3.11"
[package.source]
type = "legacy"
@@ -151,14 +155,14 @@ reference = "qt"
[[package]]
name = "shiboken6-generator"
-version = "6.1.2"
+version = "6.2.2.1"
description = "Python / C++ bindings generator"
category = "main"
optional = false
-python-versions = ">=3.6, <3.10"
+python-versions = ">=3.6, <3.11"
[package.dependencies]
-shiboken6 = "6.1.2"
+shiboken6 = "6.2.2.1"
[package.source]
type = "legacy"
@@ -176,7 +180,7 @@ python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
[metadata]
lock-version = "1.1"
python-versions = ">= 3.8, < 3.10"
-content-hash = "fab23b01f64dac94e1708045d18e89ae42b3f8227fcdf7f3c27b242ba00d6003"
+content-hash = "48c21fee334eeef76aa33f51a14f80988ccaf1bf30d62270f3504f37f01c0a00"
[metadata.files]
atomicwrites = [
@@ -184,8 +188,8 @@ atomicwrites = [
{file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"},
]
attrs = [
- {file = "attrs-21.2.0-py2.py3-none-any.whl", hash = "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1"},
- {file = "attrs-21.2.0.tar.gz", hash = "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"},
+ {file = "attrs-21.4.0-py2.py3-none-any.whl", hash = "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4"},
+ {file = "attrs-21.4.0.tar.gz", hash = "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"},
]
colorama = [
{file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"},
@@ -196,97 +200,111 @@ iniconfig = [
{file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"},
]
lxml = [
- {file = "lxml-4.6.3-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:df7c53783a46febb0e70f6b05df2ba104610f2fb0d27023409734a3ecbb78fb2"},
- {file = "lxml-4.6.3-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:1b7584d421d254ab86d4f0b13ec662a9014397678a7c4265a02a6d7c2b18a75f"},
- {file = "lxml-4.6.3-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:079f3ae844f38982d156efce585bc540c16a926d4436712cf4baee0cce487a3d"},
- {file = "lxml-4.6.3-cp27-cp27m-win32.whl", hash = "sha256:bc4313cbeb0e7a416a488d72f9680fffffc645f8a838bd2193809881c67dd106"},
- {file = "lxml-4.6.3-cp27-cp27m-win_amd64.whl", hash = "sha256:8157dadbb09a34a6bd95a50690595e1fa0af1a99445e2744110e3dca7831c4ee"},
- {file = "lxml-4.6.3-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:7728e05c35412ba36d3e9795ae8995e3c86958179c9770e65558ec3fdfd3724f"},
- {file = "lxml-4.6.3-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:4bff24dfeea62f2e56f5bab929b4428ae6caba2d1eea0c2d6eb618e30a71e6d4"},
- {file = "lxml-4.6.3-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:74f7d8d439b18fa4c385f3f5dfd11144bb87c1da034a466c5b5577d23a1d9b51"},
- {file = "lxml-4.6.3-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:f90ba11136bfdd25cae3951af8da2e95121c9b9b93727b1b896e3fa105b2f586"},
- {file = "lxml-4.6.3-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:4c61b3a0db43a1607d6264166b230438f85bfed02e8cff20c22e564d0faff354"},
- {file = "lxml-4.6.3-cp35-cp35m-manylinux2014_x86_64.whl", hash = "sha256:5c8c163396cc0df3fd151b927e74f6e4acd67160d6c33304e805b84293351d16"},
- {file = "lxml-4.6.3-cp35-cp35m-win32.whl", hash = "sha256:f2380a6376dfa090227b663f9678150ef27543483055cc327555fb592c5967e2"},
- {file = "lxml-4.6.3-cp35-cp35m-win_amd64.whl", hash = "sha256:c4f05c5a7c49d2fb70223d0d5bcfbe474cf928310ac9fa6a7c6dddc831d0b1d4"},
- {file = "lxml-4.6.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d2e35d7bf1c1ac8c538f88d26b396e73dd81440d59c1ef8522e1ea77b345ede4"},
- {file = "lxml-4.6.3-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:289e9ca1a9287f08daaf796d96e06cb2bc2958891d7911ac7cae1c5f9e1e0ee3"},
- {file = "lxml-4.6.3-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:bccbfc27563652de7dc9bdc595cb25e90b59c5f8e23e806ed0fd623755b6565d"},
- {file = "lxml-4.6.3-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:d916d31fd85b2f78c76400d625076d9124de3e4bda8b016d25a050cc7d603f24"},
- {file = "lxml-4.6.3-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:820628b7b3135403540202e60551e741f9b6d3304371712521be939470b454ec"},
- {file = "lxml-4.6.3-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:c47ff7e0a36d4efac9fd692cfa33fbd0636674c102e9e8d9b26e1b93a94e7617"},
- {file = "lxml-4.6.3-cp36-cp36m-win32.whl", hash = "sha256:5a0a14e264069c03e46f926be0d8919f4105c1623d620e7ec0e612a2e9bf1c04"},
- {file = "lxml-4.6.3-cp36-cp36m-win_amd64.whl", hash = "sha256:92e821e43ad382332eade6812e298dc9701c75fe289f2a2d39c7960b43d1e92a"},
- {file = "lxml-4.6.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:efd7a09678fd8b53117f6bae4fa3825e0a22b03ef0a932e070c0bdbb3a35e654"},
- {file = "lxml-4.6.3-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:efac139c3f0bf4f0939f9375af4b02c5ad83a622de52d6dfa8e438e8e01d0eb0"},
- {file = "lxml-4.6.3-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:0fbcf5565ac01dff87cbfc0ff323515c823081c5777a9fc7703ff58388c258c3"},
- {file = "lxml-4.6.3-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:36108c73739985979bf302006527cf8a20515ce444ba916281d1c43938b8bb96"},
- {file = "lxml-4.6.3-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:122fba10466c7bd4178b07dba427aa516286b846b2cbd6f6169141917283aae2"},
- {file = "lxml-4.6.3-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:cdaf11d2bd275bf391b5308f86731e5194a21af45fbaaaf1d9e8147b9160ea92"},
- {file = "lxml-4.6.3-cp37-cp37m-win32.whl", hash = "sha256:3439c71103ef0e904ea0a1901611863e51f50b5cd5e8654a151740fde5e1cade"},
- {file = "lxml-4.6.3-cp37-cp37m-win_amd64.whl", hash = "sha256:4289728b5e2000a4ad4ab8da6e1db2e093c63c08bdc0414799ee776a3f78da4b"},
- {file = "lxml-4.6.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b007cbb845b28db4fb8b6a5cdcbf65bacb16a8bd328b53cbc0698688a68e1caa"},
- {file = "lxml-4.6.3-cp38-cp38-manylinux1_i686.whl", hash = "sha256:76fa7b1362d19f8fbd3e75fe2fb7c79359b0af8747e6f7141c338f0bee2f871a"},
- {file = "lxml-4.6.3-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:26e761ab5b07adf5f555ee82fb4bfc35bf93750499c6c7614bd64d12aaa67927"},
- {file = "lxml-4.6.3-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:e1cbd3f19a61e27e011e02f9600837b921ac661f0c40560eefb366e4e4fb275e"},
- {file = "lxml-4.6.3-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:66e575c62792c3f9ca47cb8b6fab9e35bab91360c783d1606f758761810c9791"},
- {file = "lxml-4.6.3-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:1b38116b6e628118dea5b2186ee6820ab138dbb1e24a13e478490c7db2f326ae"},
- {file = "lxml-4.6.3-cp38-cp38-win32.whl", hash = "sha256:89b8b22a5ff72d89d48d0e62abb14340d9e99fd637d046c27b8b257a01ffbe28"},
- {file = "lxml-4.6.3-cp38-cp38-win_amd64.whl", hash = "sha256:2a9d50e69aac3ebee695424f7dbd7b8c6d6eb7de2a2eb6b0f6c7db6aa41e02b7"},
- {file = "lxml-4.6.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ce256aaa50f6cc9a649c51be3cd4ff142d67295bfc4f490c9134d0f9f6d58ef0"},
- {file = "lxml-4.6.3-cp39-cp39-manylinux1_i686.whl", hash = "sha256:7610b8c31688f0b1be0ef882889817939490a36d0ee880ea562a4e1399c447a1"},
- {file = "lxml-4.6.3-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:f8380c03e45cf09f8557bdaa41e1fa7c81f3ae22828e1db470ab2a6c96d8bc23"},
- {file = "lxml-4.6.3-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:3082c518be8e97324390614dacd041bb1358c882d77108ca1957ba47738d9d59"},
- {file = "lxml-4.6.3-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:884ab9b29feaca361f7f88d811b1eea9bfca36cf3da27768d28ad45c3ee6f969"},
- {file = "lxml-4.6.3-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:6f12e1427285008fd32a6025e38e977d44d6382cf28e7201ed10d6c1698d2a9a"},
- {file = "lxml-4.6.3-cp39-cp39-win32.whl", hash = "sha256:33bb934a044cf32157c12bfcfbb6649807da20aa92c062ef51903415c704704f"},
- {file = "lxml-4.6.3-cp39-cp39-win_amd64.whl", hash = "sha256:542d454665a3e277f76954418124d67516c5f88e51a900365ed54a9806122b83"},
- {file = "lxml-4.6.3.tar.gz", hash = "sha256:39b78571b3b30645ac77b95f7c69d1bffc4cf8c3b157c435a34da72e78c82468"},
+ {file = "lxml-4.7.1-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:d546431636edb1d6a608b348dd58cc9841b81f4116745857b6cb9f8dadb2725f"},
+ {file = "lxml-4.7.1-cp27-cp27m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6308062534323f0d3edb4e702a0e26a76ca9e0e23ff99be5d82750772df32a9e"},
+ {file = "lxml-4.7.1-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:f76dbe44e31abf516114f6347a46fa4e7c2e8bceaa4b6f7ee3a0a03c8eba3c17"},
+ {file = "lxml-4.7.1-cp27-cp27m-win32.whl", hash = "sha256:d5618d49de6ba63fe4510bdada62d06a8acfca0b4b5c904956c777d28382b419"},
+ {file = "lxml-4.7.1-cp27-cp27m-win_amd64.whl", hash = "sha256:9393a05b126a7e187f3e38758255e0edf948a65b22c377414002d488221fdaa2"},
+ {file = "lxml-4.7.1-cp27-cp27mu-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:50d3dba341f1e583265c1a808e897b4159208d814ab07530202b6036a4d86da5"},
+ {file = "lxml-4.7.1-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:44f552e0da3c8ee3c28e2eb82b0b784200631687fc6a71277ea8ab0828780e7d"},
+ {file = "lxml-4.7.1-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:e662c6266e3a275bdcb6bb049edc7cd77d0b0f7e119a53101d367c841afc66dc"},
+ {file = "lxml-4.7.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:4c093c571bc3da9ebcd484e001ba18b8452903cd428c0bc926d9b0141bcb710e"},
+ {file = "lxml-4.7.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:3e26ad9bc48d610bf6cc76c506b9e5ad9360ed7a945d9be3b5b2c8535a0145e3"},
+ {file = "lxml-4.7.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:a5f623aeaa24f71fce3177d7fee875371345eb9102b355b882243e33e04b7175"},
+ {file = "lxml-4.7.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7b5e2acefd33c259c4a2e157119c4373c8773cf6793e225006a1649672ab47a6"},
+ {file = "lxml-4.7.1-cp310-cp310-win32.whl", hash = "sha256:67fa5f028e8a01e1d7944a9fb616d1d0510d5d38b0c41708310bd1bc45ae89f6"},
+ {file = "lxml-4.7.1-cp310-cp310-win_amd64.whl", hash = "sha256:b1d381f58fcc3e63fcc0ea4f0a38335163883267f77e4c6e22d7a30877218a0e"},
+ {file = "lxml-4.7.1-cp35-cp35m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:38d9759733aa04fb1697d717bfabbedb21398046bd07734be7cccc3d19ea8675"},
+ {file = "lxml-4.7.1-cp35-cp35m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:dfd0d464f3d86a1460683cd742306d1138b4e99b79094f4e07e1ca85ee267fe7"},
+ {file = "lxml-4.7.1-cp35-cp35m-win32.whl", hash = "sha256:534e946bce61fd162af02bad7bfd2daec1521b71d27238869c23a672146c34a5"},
+ {file = "lxml-4.7.1-cp35-cp35m-win_amd64.whl", hash = "sha256:6ec829058785d028f467be70cd195cd0aaf1a763e4d09822584ede8c9eaa4b03"},
+ {file = "lxml-4.7.1-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:ade74f5e3a0fd17df5782896ddca7ddb998845a5f7cd4b0be771e1ffc3b9aa5b"},
+ {file = "lxml-4.7.1-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:41358bfd24425c1673f184d7c26c6ae91943fe51dfecc3603b5e08187b4bcc55"},
+ {file = "lxml-4.7.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:6e56521538f19c4a6690f439fefed551f0b296bd785adc67c1777c348beb943d"},
+ {file = "lxml-4.7.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5b0f782f0e03555c55e37d93d7a57454efe7495dab33ba0ccd2dbe25fc50f05d"},
+ {file = "lxml-4.7.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:490712b91c65988012e866c411a40cc65b595929ececf75eeb4c79fcc3bc80a6"},
+ {file = "lxml-4.7.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:34c22eb8c819d59cec4444d9eebe2e38b95d3dcdafe08965853f8799fd71161d"},
+ {file = "lxml-4.7.1-cp36-cp36m-win32.whl", hash = "sha256:2a906c3890da6a63224d551c2967413b8790a6357a80bf6b257c9a7978c2c42d"},
+ {file = "lxml-4.7.1-cp36-cp36m-win_amd64.whl", hash = "sha256:36b16fecb10246e599f178dd74f313cbdc9f41c56e77d52100d1361eed24f51a"},
+ {file = "lxml-4.7.1-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:a5edc58d631170de90e50adc2cc0248083541affef82f8cd93bea458e4d96db8"},
+ {file = "lxml-4.7.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:87c1b0496e8c87ec9db5383e30042357b4839b46c2d556abd49ec770ce2ad868"},
+ {file = "lxml-4.7.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:0a5f0e4747f31cff87d1eb32a6000bde1e603107f632ef4666be0dc065889c7a"},
+ {file = "lxml-4.7.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:bf6005708fc2e2c89a083f258b97709559a95f9a7a03e59f805dd23c93bc3986"},
+ {file = "lxml-4.7.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fc15874816b9320581133ddc2096b644582ab870cf6a6ed63684433e7af4b0d3"},
+ {file = "lxml-4.7.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:0b5e96e25e70917b28a5391c2ed3ffc6156513d3db0e1476c5253fcd50f7a944"},
+ {file = "lxml-4.7.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ec9027d0beb785a35aa9951d14e06d48cfbf876d8ff67519403a2522b181943b"},
+ {file = "lxml-4.7.1-cp37-cp37m-win32.whl", hash = "sha256:9fbc0dee7ff5f15c4428775e6fa3ed20003140560ffa22b88326669d53b3c0f4"},
+ {file = "lxml-4.7.1-cp37-cp37m-win_amd64.whl", hash = "sha256:1104a8d47967a414a436007c52f533e933e5d52574cab407b1e49a4e9b5ddbd1"},
+ {file = "lxml-4.7.1-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:fc9fb11b65e7bc49f7f75aaba1b700f7181d95d4e151cf2f24d51bfd14410b77"},
+ {file = "lxml-4.7.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:317bd63870b4d875af3c1be1b19202de34c32623609ec803b81c99193a788c1e"},
+ {file = "lxml-4.7.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:610807cea990fd545b1559466971649e69302c8a9472cefe1d6d48a1dee97440"},
+ {file = "lxml-4.7.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:09b738360af8cb2da275998a8bf79517a71225b0de41ab47339c2beebfff025f"},
+ {file = "lxml-4.7.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6a2ab9d089324d77bb81745b01f4aeffe4094306d939e92ba5e71e9a6b99b71e"},
+ {file = "lxml-4.7.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:eed394099a7792834f0cb4a8f615319152b9d801444c1c9e1b1a2c36d2239f9e"},
+ {file = "lxml-4.7.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:735e3b4ce9c0616e85f302f109bdc6e425ba1670a73f962c9f6b98a6d51b77c9"},
+ {file = "lxml-4.7.1-cp38-cp38-win32.whl", hash = "sha256:772057fba283c095db8c8ecde4634717a35c47061d24f889468dc67190327bcd"},
+ {file = "lxml-4.7.1-cp38-cp38-win_amd64.whl", hash = "sha256:13dbb5c7e8f3b6a2cf6e10b0948cacb2f4c9eb05029fe31c60592d08ac63180d"},
+ {file = "lxml-4.7.1-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:718d7208b9c2d86aaf0294d9381a6acb0158b5ff0f3515902751404e318e02c9"},
+ {file = "lxml-4.7.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:5bee1b0cbfdb87686a7fb0e46f1d8bd34d52d6932c0723a86de1cc532b1aa489"},
+ {file = "lxml-4.7.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:e410cf3a2272d0a85526d700782a2fa92c1e304fdcc519ba74ac80b8297adf36"},
+ {file = "lxml-4.7.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:585ea241ee4961dc18a95e2f5581dbc26285fcf330e007459688096f76be8c42"},
+ {file = "lxml-4.7.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a555e06566c6dc167fbcd0ad507ff05fd9328502aefc963cb0a0547cfe7f00db"},
+ {file = "lxml-4.7.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:adaab25be351fff0d8a691c4f09153647804d09a87a4e4ea2c3f9fe9e8651851"},
+ {file = "lxml-4.7.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:82d16a64236970cb93c8d63ad18c5b9f138a704331e4b916b2737ddfad14e0c4"},
+ {file = "lxml-4.7.1-cp39-cp39-win32.whl", hash = "sha256:59e7da839a1238807226f7143c68a479dee09244d1b3cf8c134f2fce777d12d0"},
+ {file = "lxml-4.7.1-cp39-cp39-win_amd64.whl", hash = "sha256:a1bbc4efa99ed1310b5009ce7f3a1784698082ed2c1ef3895332f5df9b3b92c2"},
+ {file = "lxml-4.7.1-pp37-pypy37_pp73-macosx_10_14_x86_64.whl", hash = "sha256:0607ff0988ad7e173e5ddf7bf55ee65534bd18a5461183c33e8e41a59e89edf4"},
+ {file = "lxml-4.7.1-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:6c198bfc169419c09b85ab10cb0f572744e686f40d1e7f4ed09061284fc1303f"},
+ {file = "lxml-4.7.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:a58d78653ae422df6837dd4ca0036610b8cb4962b5cfdbd337b7b24de9e5f98a"},
+ {file = "lxml-4.7.1-pp38-pypy38_pp73-macosx_10_14_x86_64.whl", hash = "sha256:e18281a7d80d76b66a9f9e68a98cf7e1d153182772400d9a9ce855264d7d0ce7"},
+ {file = "lxml-4.7.1-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:8e54945dd2eeb50925500957c7c579df3cd07c29db7810b83cf30495d79af267"},
+ {file = "lxml-4.7.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:447d5009d6b5447b2f237395d0018901dcc673f7d9f82ba26c1b9f9c3b444b60"},
+ {file = "lxml-4.7.1.tar.gz", hash = "sha256:a1613838aa6b89af4ba10a0f3a972836128801ed008078f8c1244e65958f1b24"},
]
packaging = [
- {file = "packaging-21.0-py3-none-any.whl", hash = "sha256:c86254f9220d55e31cc94d69bade760f0847da8000def4dfe1c6b872fd14ff14"},
- {file = "packaging-21.0.tar.gz", hash = "sha256:7dc96269f53a4ccec5c0670940a4281106dd0bb343f47b7471f779df49c2fbe7"},
+ {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"},
+ {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"},
]
pluggy = [
- {file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"},
- {file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"},
+ {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"},
+ {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"},
]
py = [
- {file = "py-1.10.0-py2.py3-none-any.whl", hash = "sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a"},
- {file = "py-1.10.0.tar.gz", hash = "sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3"},
+ {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"},
+ {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"},
]
pyparsing = [
- {file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"},
- {file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"},
+ {file = "pyparsing-3.0.6-py3-none-any.whl", hash = "sha256:04ff808a5b90911829c55c4e26f75fa5ca8a2f5f36aa3a51f68e27033341d3e4"},
+ {file = "pyparsing-3.0.6.tar.gz", hash = "sha256:d9bdec0013ef1eb5a84ab39a3b3868911598afa494f5faa038647101504e2b81"},
]
pyside6 = [
- {file = "PySide6-6.1.2-6.1.2-cp36.cp37.cp38.cp39-abi3-macosx_10_14_x86_64.whl", hash = "sha256:d95286a6d59f29f5cad18c4346166f94bfedff9a61056d9b30d641e7a6ee0b28"},
- {file = "PySide6-6.1.2-6.1.2-cp36.cp37.cp38.cp39-abi3-manylinux1_x86_64.whl", hash = "sha256:dab04edbde131d09a885fc2c945d9ea3f8e90e6d73546e66de5c2265c4731cf9"},
- {file = "PySide6-6.1.2-6.1.2-cp36.cp37.cp38.cp39-none-win_amd64.whl", hash = "sha256:2dda3467d56ec76031b36ce2571e931cf5f3b6682497edc8a004e0360436da5d"},
- {file = "shiboken6-6.1.2-6.1.2-cp36.cp37.cp38.cp39-abi3-macosx_10_14_x86_64.whl", hash = "sha256:acdbf0f085d8d07125ca5acc2ee6b1c56c97c91bd924ebfbcee7ac7d166e7bbe"},
- {file = "shiboken6-6.1.2-6.1.2-cp36.cp37.cp38.cp39-abi3-manylinux1_x86_64.whl", hash = "sha256:745428c03831f5d60c3ad08b2cbcf25342aef5abfba9d7e79b3a25b38d4bff31"},
- {file = "shiboken6-6.1.2-6.1.2-cp36.cp37.cp38.cp39-none-win_amd64.whl", hash = "sha256:6b60c5f7ca1a272526c49a9e5f4cc9a4f5657e89c2c36eaa81f24b4c027847d9"},
+ {file = "PySide6-6.2.2.1-6.2.2-cp36.cp37.cp38.cp39.cp310-abi3-macosx_10_14_universal2.whl", hash = "sha256:f9fa755b30bd8098ba3734a8a7e7664b9bf6bf53d7c0462c7e9657f8a8d3392f"},
+ {file = "PySide6-6.2.2.1-6.2.2-cp36.cp37.cp38.cp39.cp310-abi3-manylinux1_x86_64.whl", hash = "sha256:5d35206897edc00bfab3c67dcf180e45406a93c8694e68c12364c3f9e370dfc6"},
+ {file = "PySide6-6.2.2.1-6.2.2-cp36.cp37.cp38.cp39.cp310-none-win_amd64.whl", hash = "sha256:227597e427f3c516a237e714de8dc30e0a4747607ac2283dd350bd4314605803"},
+ {file = "shiboken6-6.2.2.1-6.2.2-cp36.cp37.cp38.cp39.cp310-abi3-macosx_10_14_universal2.whl", hash = "sha256:cbffaefe676f5cce4204d16c2d8f794521cb8c66abbe6470d6e5cb2b889c4b88"},
+ {file = "shiboken6-6.2.2.1-6.2.2-cp36.cp37.cp38.cp39.cp310-abi3-manylinux1_x86_64.whl", hash = "sha256:325eb45d8ffc59e0cb94d5fb9cdd0a8c28ed1705f3e7fd37c239a1dca50b1c22"},
+ {file = "shiboken6-6.2.2.1-6.2.2-cp36.cp37.cp38.cp39.cp310-none-win_amd64.whl", hash = "sha256:a4f267789c18caa60afd23818743a5abe164ed4f89f4eafaf4577d4fa646c293"},
]
pytest = [
- {file = "pytest-6.2.4-py3-none-any.whl", hash = "sha256:91ef2131a9bd6be8f76f1f08eac5c5317221d6ad1e143ae03894b862e8976890"},
- {file = "pytest-6.2.4.tar.gz", hash = "sha256:50bcad0a0b9c5a72c8e4e7c9855a3ad496ca6a881a3641b4260605450772c54b"},
+ {file = "pytest-6.2.5-py3-none-any.whl", hash = "sha256:7310f8d27bc79ced999e760ca304d69f6ba6c6649c0b60fb0e04a4a77cacc134"},
+ {file = "pytest-6.2.5.tar.gz", hash = "sha256:131b36680866a76e6781d13f101efb86cf674ebb9762eb70d3082b6f29889e89"},
]
pytest-mock = [
{file = "pytest-mock-3.6.1.tar.gz", hash = "sha256:40217a058c52a63f1042f0784f62009e976ba824c418cced42e88d5f40ab0e62"},
{file = "pytest_mock-3.6.1-py3-none-any.whl", hash = "sha256:30c2f2cc9759e76eee674b81ea28c9f0b94f8f0445a1b87762cadf774f0df7e3"},
]
shiboken6 = [
- {file = "PySide6-6.1.2-6.1.2-cp36.cp37.cp38.cp39-abi3-macosx_10_14_x86_64.whl", hash = "sha256:d95286a6d59f29f5cad18c4346166f94bfedff9a61056d9b30d641e7a6ee0b28"},
- {file = "PySide6-6.1.2-6.1.2-cp36.cp37.cp38.cp39-abi3-manylinux1_x86_64.whl", hash = "sha256:dab04edbde131d09a885fc2c945d9ea3f8e90e6d73546e66de5c2265c4731cf9"},
- {file = "PySide6-6.1.2-6.1.2-cp36.cp37.cp38.cp39-none-win_amd64.whl", hash = "sha256:2dda3467d56ec76031b36ce2571e931cf5f3b6682497edc8a004e0360436da5d"},
- {file = "shiboken6-6.1.2-6.1.2-cp36.cp37.cp38.cp39-abi3-macosx_10_14_x86_64.whl", hash = "sha256:acdbf0f085d8d07125ca5acc2ee6b1c56c97c91bd924ebfbcee7ac7d166e7bbe"},
- {file = "shiboken6-6.1.2-6.1.2-cp36.cp37.cp38.cp39-abi3-manylinux1_x86_64.whl", hash = "sha256:745428c03831f5d60c3ad08b2cbcf25342aef5abfba9d7e79b3a25b38d4bff31"},
- {file = "shiboken6-6.1.2-6.1.2-cp36.cp37.cp38.cp39-none-win_amd64.whl", hash = "sha256:6b60c5f7ca1a272526c49a9e5f4cc9a4f5657e89c2c36eaa81f24b4c027847d9"},
+ {file = "PySide6-6.2.2.1-6.2.2-cp36.cp37.cp38.cp39.cp310-abi3-macosx_10_14_universal2.whl", hash = "sha256:f9fa755b30bd8098ba3734a8a7e7664b9bf6bf53d7c0462c7e9657f8a8d3392f"},
+ {file = "PySide6-6.2.2.1-6.2.2-cp36.cp37.cp38.cp39.cp310-abi3-manylinux1_x86_64.whl", hash = "sha256:5d35206897edc00bfab3c67dcf180e45406a93c8694e68c12364c3f9e370dfc6"},
+ {file = "PySide6-6.2.2.1-6.2.2-cp36.cp37.cp38.cp39.cp310-none-win_amd64.whl", hash = "sha256:227597e427f3c516a237e714de8dc30e0a4747607ac2283dd350bd4314605803"},
+ {file = "shiboken6-6.2.2.1-6.2.2-cp36.cp37.cp38.cp39.cp310-abi3-macosx_10_14_universal2.whl", hash = "sha256:cbffaefe676f5cce4204d16c2d8f794521cb8c66abbe6470d6e5cb2b889c4b88"},
+ {file = "shiboken6-6.2.2.1-6.2.2-cp36.cp37.cp38.cp39.cp310-abi3-manylinux1_x86_64.whl", hash = "sha256:325eb45d8ffc59e0cb94d5fb9cdd0a8c28ed1705f3e7fd37c239a1dca50b1c22"},
+ {file = "shiboken6-6.2.2.1-6.2.2-cp36.cp37.cp38.cp39.cp310-none-win_amd64.whl", hash = "sha256:a4f267789c18caa60afd23818743a5abe164ed4f89f4eafaf4577d4fa646c293"},
]
shiboken6-generator = [
- {file = "shiboken6_generator-6.1.2-6.1.2-cp36.cp37.cp38.cp39-abi3-macosx_10_14_x86_64.whl", hash = "sha256:e8cf068e21aad66571f67e58129655d296796ee9107f172c3b4958a8efefcdbe"},
- {file = "shiboken6_generator-6.1.2-6.1.2-cp36.cp37.cp38.cp39-abi3-manylinux1_x86_64.whl", hash = "sha256:0fc0a39e82522a2cc71ce11dc1d324deb7100e86aaf387c021c04457d8394531"},
- {file = "shiboken6_generator-6.1.2-6.1.2-cp36.cp37.cp38.cp39-none-win_amd64.whl", hash = "sha256:43bae2454784ca58058f8311903791c01adaec48349d4e1aa23c8aafdff24f52"},
+ {file = "shiboken6_generator-6.2.2.1-6.2.2-cp36.cp37.cp38.cp39.cp310-abi3-macosx_10_14_universal2.whl", hash = "sha256:7be6dcffcf56ea21f6f4dc47f0fa1c222201138b4063521e6af03f0f81ecb9f4"},
+ {file = "shiboken6_generator-6.2.2.1-6.2.2-cp36.cp37.cp38.cp39.cp310-abi3-manylinux1_x86_64.whl", hash = "sha256:6c5d393f7668e1e4ff0f4237f4e54ba67d60f4e331a1ef72fa208a8738213f6a"},
+ {file = "shiboken6_generator-6.2.2.1-6.2.2-cp36.cp37.cp38.cp39.cp310-none-win_amd64.whl", hash = "sha256:48f1cc17adc40de95674b18b670bee9d0ec6248cfd72e1d9b72ccbd43c0dc174"},
]
toml = [
{file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"},
diff --git a/pyproject.toml b/pyproject.toml
index f68dcbe..033e1d3 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -11,9 +11,9 @@ packages = [
[tool.poetry.dependencies]
python = ">= 3.8, < 3.10"
-shiboken6 = "^6.1.2"
-PySide6 = "^6.1.2"
-shiboken6-generator = "^6.1.2"
+shiboken6 = "^6.2.2.1"
+PySide6 = "^6.2.2.1"
+shiboken6-generator = "^6.2.2.1"
lxml = "^4.6.3"
[tool.poetry.dev-dependencies]