diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..f20ebf5
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/spectral.iml b/.idea/spectral.iml
new file mode 100644
index 0000000..bc2cd87
--- /dev/null
+++ b/.idea/spectral.iml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..b2c4215
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/workspace.xml b/.idea/workspace.xml
new file mode 100644
index 0000000..ef2b780
--- /dev/null
+++ b/.idea/workspace.xml
@@ -0,0 +1,153 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1558287890866
+
+
+ 1558287890866
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/imports/Spectral/Component/AutoRectangle.qml b/imports/Spectral/Component/AutoRectangle.qml
new file mode 100644
index 0000000..975e491
--- /dev/null
+++ b/imports/Spectral/Component/AutoRectangle.qml
@@ -0,0 +1,71 @@
+import QtQuick 2.12
+
+Rectangle {
+ property alias topLeftRadius: topLeftRect.radius
+ property alias topRightRadius: topRightRect.radius
+ property alias bottomLeftRadius: bottomLeftRect.radius
+ property alias bottomRightRadius: bottomRightRect.radius
+
+ property alias topLeftVisible: topLeftRect.visible
+ property alias topRightVisible: topRightRect.visible
+ property alias bottomLeftVisible: bottomLeftRect.visible
+ property alias bottomRightVisible: bottomRightRect.visible
+
+ antialiasing: true
+
+ Rectangle {
+ anchors.top: parent.top
+ anchors.left: parent.left
+
+ width: parent.width / 2
+ height: parent.height / 2
+
+ id: topLeftRect
+
+ antialiasing: true
+
+ color: parent.color
+ }
+
+ Rectangle {
+ anchors.top: parent.top
+ anchors.right: parent.right
+
+ width: parent.width / 2
+ height: parent.height / 2
+
+ id: topRightRect
+
+ antialiasing: true
+
+ color: parent.color
+ }
+
+ Rectangle {
+ anchors.bottom: parent.bottom
+ anchors.left: parent.left
+
+ width: parent.width / 2
+ height: parent.height / 2
+
+ id: bottomLeftRect
+
+ antialiasing: true
+
+ color: parent.color
+ }
+
+ Rectangle {
+ anchors.bottom: parent.bottom
+ anchors.right: parent.right
+
+ width: parent.width / 2
+ height: parent.height / 2
+
+ id: bottomRightRect
+
+ antialiasing: true
+
+ color: parent.color
+ }
+}
diff --git a/imports/Spectral/Component/Avatar.qml b/imports/Spectral/Component/Avatar.qml
index b9c2c8d..caf0f85 100644
--- a/imports/Spectral/Component/Avatar.qml
+++ b/imports/Spectral/Component/Avatar.qml
@@ -2,6 +2,8 @@ import QtQuick 2.12
import QtQuick.Controls 2.12
import QtGraphicalEffects 1.0
+import Spectral.Setting 0.1
+
Item {
property string hint: "H"
property string source: ""
@@ -35,7 +37,7 @@ Item {
visible: !realSource || image.status != Image.Ready
radius: height / 2
- color: stringToColor(hint)
+ color: MPalette.accent
antialiasing: true
Label {
@@ -50,16 +52,16 @@ Item {
}
}
- function stringToColor(str) {
- var hash = 0;
- for (var i = 0; i < str.length; i++) {
- hash = str.charCodeAt(i) + ((hash << 5) - hash);
- }
- var colour = '#';
- for (var j = 0; j < 3; j++) {
- var value = (hash >> (j * 8)) & 0xFF;
- colour += ('00' + value.toString(16)).substr(-2);
- }
- return colour;
- }
+// function stringToColor(str) {
+// var hash = 0;
+// for (var i = 0; i < str.length; i++) {
+// hash = str.charCodeAt(i) + ((hash << 5) - hash);
+// }
+// var colour = '#';
+// for (var j = 0; j < 3; j++) {
+// var value = (hash >> (j * 8)) & 0xFF;
+// colour += ('00' + value.toString(16)).substr(-2);
+// }
+// return colour;
+// }
}
diff --git a/imports/Spectral/Component/Timeline/MessageDelegate.qml b/imports/Spectral/Component/Timeline/MessageDelegate.qml
index 2219993..1566ae1 100644
--- a/imports/Spectral/Component/Timeline/MessageDelegate.qml
+++ b/imports/Spectral/Component/Timeline/MessageDelegate.qml
@@ -67,65 +67,25 @@ ColumnLayout {
Control {
Layout.maximumWidth: messageListView.width - (!sentByMe ? 36 + messageRow.spacing : 0) - 48
- verticalPadding: 8
- horizontalPadding: 16
+ padding: 0
+
+ background: AutoRectangle {
+ readonly property int minorRadius: 2
+
+ id: bubbleBackground
- background: Rectangle {
color: sentByMe ? MPalette.background : eventType === "notice" ? MPalette.primary : MPalette.accent
radius: 18
- antialiasing: true
- Rectangle {
- anchors.top: parent.top
- anchors.left: parent.left
+ topLeftVisible: true
+ topRightVisible: true
+ bottomLeftVisible: true
+ bottomRightVisible: true
- width: parent.width / 2
- height: parent.height / 2
-
- visible: true
-
- color: sentByMe ? MPalette.background : eventType === "notice" ? MPalette.primary : MPalette.accent
- radius: 5
- }
-
- Rectangle {
- anchors.top: parent.top
- anchors.right: parent.right
-
- width: parent.width / 2
- height: parent.height / 2
-
- visible: true
-
- color: sentByMe ? MPalette.background : eventType === "notice" ? MPalette.primary : MPalette.accent
- radius: 5
- }
-
- Rectangle {
- anchors.bottom: parent.bottom
- anchors.left: parent.left
-
- width: parent.width / 2
- height: parent.height / 2
-
- visible: true
-
- color: sentByMe ? MPalette.background : eventType === "notice" ? MPalette.primary : MPalette.accent
- radius: 5
- }
-
- Rectangle {
- anchors.bottom: parent.bottom
- anchors.right: parent.right
-
- width: parent.width / 2
- height: parent.height / 2
-
- visible: true
-
- color: sentByMe ? MPalette.background : eventType === "notice" ? MPalette.primary : MPalette.accent
- radius: 5
- }
+ topLeftRadius: minorRadius
+ topRightRadius: minorRadius
+ bottomLeftRadius: minorRadius
+ bottomRightRadius: minorRadius
AutoMouseArea {
anchors.fill: parent
@@ -166,63 +126,72 @@ ColumnLayout {
}
contentItem: ColumnLayout {
- RowLayout {
+ spacing: 0
+
+ Control {
Layout.fillWidth: true
+ Layout.topMargin: 8
+ Layout.leftMargin: 8
+ Layout.rightMargin: 8
+
+ padding: 4
+ rightPadding: 12
+
visible: replyVisible
- Avatar {
- Layout.preferredWidth: 28
- Layout.preferredHeight: 28
- Layout.alignment: Qt.AlignTop
+ contentItem: RowLayout {
+ Avatar {
+ Layout.preferredWidth: 28
+ Layout.preferredHeight: 28
+ Layout.alignment: Qt.AlignTop
- source: replyVisible ? replyAuthor.avatarMediaId : ""
- hint: replyVisible ? replyAuthor.displayName : "H"
+ source: replyVisible ? replyAuthor.avatarMediaId : ""
+ hint: replyVisible ? replyAuthor.displayName : "H"
- RippleEffect {
- anchors.fill: parent
+ RippleEffect {
+ anchors.fill: parent
- circular: true
+ circular: true
- onClicked: userDetailDialog.createObject(ApplicationWindow.overlay, {"room": currentRoom, "user": replyAuthor}).open()
- }
- }
-
- Control {
- Layout.fillWidth: true
-
- visible: replyVisible
-
- padding: 0
-
- background: RippleEffect {
- onClicked: goToEvent(replyEventId)
+ onClicked: userDetailDialog.createObject(ApplicationWindow.overlay, {"room": currentRoom, "user": replyAuthor}).open()
+ }
}
- contentItem: Label {
- color: darkBackground ? "white" : MPalette.lighter
- text: "" + (replyDisplay || "")
+ Label {
+ Layout.fillWidth: true
+
+ color: !sentByMe ? MPalette.foreground : "white"
+ text: "" + (replyDisplay || "")
wrapMode: Label.Wrap
textFormat: Label.RichText
}
}
- }
- Rectangle {
- Layout.fillWidth: true
- Layout.preferredHeight: 1
+ background: Rectangle {
+ color: sentByMe ? MPalette.accent : MPalette.background
+ radius: 18
- visible: replyVisible
- color: darkBackground ? "white" : MPalette.lighter
+ AutoMouseArea {
+ anchors.fill: parent
+
+ onClicked: goToEvent(replyEventId)
+ }
+ }
}
TextEdit {
Layout.fillWidth: true
+ Layout.leftMargin: 16
+ Layout.rightMargin: 16
+ Layout.topMargin: 8
+ Layout.bottomMargin: 8
+
id: contentLabel
- text: "" + display
+ text: "" + display
color: darkBackground ? "white" : MPalette.foreground
diff --git a/imports/Spectral/Component/qmldir b/imports/Spectral/Component/qmldir
index 4cbc3dd..dd821c5 100644
--- a/imports/Spectral/Component/qmldir
+++ b/imports/Spectral/Component/qmldir
@@ -7,3 +7,4 @@ AutoListView 2.0 AutoListView.qml
AutoTextField 2.0 AutoTextField.qml
Avatar 2.0 Avatar.qml
FullScreenImage 2.0 FullScreenImage.qml
+AutoRectangle 2.0 AutoRectangle.qml
diff --git a/imports/Spectral/Dialog/UserDetailDialog.qml b/imports/Spectral/Dialog/UserDetailDialog.qml
index 373b1a1..786fc33 100644
--- a/imports/Spectral/Dialog/UserDetailDialog.qml
+++ b/imports/Spectral/Dialog/UserDetailDialog.qml
@@ -27,8 +27,8 @@ Dialog {
Layout.preferredWidth: 72
Layout.preferredHeight: 72
- hint: user ? user.displayName : "No name"
- source: user ? user.avatarMediaId : null
+ hint: user.displayName
+ source: user.avatarMediaId
RippleEffect {
anchors.fill: parent
@@ -50,10 +50,19 @@ Dialog {
elide: Text.ElideRight
wrapMode: Text.NoWrap
- text: user ? user.displayName : "No Name"
+ text: user.displayName
color: MPalette.foreground
}
+ Label {
+ Layout.fillWidth: true
+
+ visible: user.bridgeName
+
+ text: user.bridgeName
+ color: MPalette.lighter
+ }
+
Label {
Layout.fillWidth: true
@@ -89,7 +98,7 @@ Dialog {
elide: Text.ElideRight
wrapMode: Text.NoWrap
- text: user ? user.id : "No ID"
+ text: user.id
color: MPalette.accent
}
diff --git a/imports/Spectral/Dialog/qmldir b/imports/Spectral/Dialog/qmldir
index df7f062..51a130a 100644
--- a/imports/Spectral/Dialog/qmldir
+++ b/imports/Spectral/Dialog/qmldir
@@ -11,3 +11,4 @@ FontFamilyDialog 2.0 FontFamilyDialog.qml
AccountDetailDialog 2.0 AccountDetailDialog.qml
OpenFileDialog 2.0 OpenFileDialog.qml
OpenFolderDialog 2.0 OpenFolderDialog.qml
+ImageClipboardDialog 2.0 ImageClipboardDialog.qml
diff --git a/imports/Spectral/Panel/RoomListPanel.qml b/imports/Spectral/Panel/RoomListPanel.qml
index 00cf0b1..254078a 100644
--- a/imports/Spectral/Panel/RoomListPanel.qml
+++ b/imports/Spectral/Panel/RoomListPanel.qml
@@ -31,7 +31,7 @@ Item {
connection: root.connection
- onNewMessage: if (!window.active && MSettings.showNotification) spectralController.postNotification(roomId, eventId, roomName, senderName, text, icon)
+ onNewMessage: if (!window.active && MSettings.showNotification) notificationsManager.postNotification(roomId, eventId, roomName, senderName, text, icon)
}
SortFilterProxyModel {
@@ -54,6 +54,16 @@ Item {
sorters: [
RoleSorter { roleName: "category" },
+ ExpressionSorter {
+ expression: {
+ return modelLeft.highlightCount > 0;
+ }
+ },
+ ExpressionSorter {
+ expression: {
+ return modelLeft.notificationCount > 0;
+ }
+ },
RoleSorter {
roleName: "lastActiveTime"
sortOrder: Qt.DescendingOrder
diff --git a/imports/Spectral/Panel/RoomPanel.qml b/imports/Spectral/Panel/RoomPanel.qml
index a142495..4aa2154 100644
--- a/imports/Spectral/Panel/RoomPanel.qml
+++ b/imports/Spectral/Panel/RoomPanel.qml
@@ -3,10 +3,12 @@ import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
import QtQuick.Controls.Material 2.12
import Qt.labs.qmlmodels 1.0
+import Qt.labs.platform 1.0
import Spectral.Component 2.0
import Spectral.Component.Emoji 2.0
import Spectral.Component.Timeline 2.0
+import Spectral.Dialog 2.0
import Spectral.Effect 2.0
import Spectral 0.1
@@ -23,6 +25,121 @@ Item {
room: currentRoom
}
+ DropArea {
+ anchors.fill: parent
+
+ enabled: currentRoom
+
+ onDropped: {
+ if (!drop.hasUrls) return
+
+ roomPanelInput.attach(drop.urls[0])
+ }
+ }
+
+ ImageClipboard {
+ id: imageClipboard
+ }
+
+ Popup {
+ anchors.centerIn: parent
+
+ id: attachDialog
+
+ padding: 16
+
+ contentItem: RowLayout {
+ Control {
+ Layout.preferredWidth: 160
+ Layout.fillHeight: true
+
+ padding: 16
+
+ contentItem: ColumnLayout {
+ spacing: 16
+
+ MaterialIcon {
+ Layout.alignment: Qt.AlignHCenter
+
+ icon: "\ue2c8"
+ font.pixelSize: 64
+ color: MPalette.lighter
+ }
+
+ Label {
+ Layout.alignment: Qt.AlignHCenter
+
+ text: "Choose local file"
+ }
+ }
+
+ background: RippleEffect {
+ onClicked: {
+ attachDialog.close()
+
+ var fileDialog = openFileDialog.createObject(ApplicationWindow.overlay)
+
+ fileDialog.chosen.connect(function(path) {
+ if (!path) return
+
+ roomPanelInput.attach(path)
+ })
+
+ fileDialog.open()
+ }
+ }
+ }
+
+ Rectangle {
+ Layout.preferredWidth: 1
+ Layout.fillHeight: true
+
+ color: MPalette.banner
+ }
+
+ Control {
+ Layout.preferredWidth: 160
+ Layout.fillHeight: true
+
+ padding: 16
+
+ contentItem: ColumnLayout {
+ spacing: 16
+
+ MaterialIcon {
+ Layout.alignment: Qt.AlignHCenter
+
+ icon: "\ue410"
+ font.pixelSize: 64
+ color: MPalette.lighter
+ }
+
+ Label {
+ Layout.alignment: Qt.AlignHCenter
+
+ text: "Clipboard image"
+ color: MPalette.foreground
+ }
+ }
+
+ background: RippleEffect {
+ onClicked: {
+ var localPath = StandardPaths.writableLocation(StandardPaths.CacheLocation) + "/screenshots/" + (new Date()).getTime() + ".png"
+ if (!imageClipboard.saveImage(localPath)) return
+ roomPanelInput.attach(localPath)
+ attachDialog.close()
+ }
+ }
+ }
+ }
+ }
+
+ Component {
+ id: openFileDialog
+
+ OpenFileDialog {}
+ }
+
Column {
anchors.centerIn: parent
diff --git a/imports/Spectral/Panel/RoomPanelInput.qml b/imports/Spectral/Panel/RoomPanelInput.qml
index 6dc897d..6930290 100644
--- a/imports/Spectral/Panel/RoomPanelInput.qml
+++ b/imports/Spectral/Panel/RoomPanelInput.qml
@@ -5,6 +5,7 @@ import QtQuick.Controls.Material 2.12
import Spectral.Component 2.0
import Spectral.Component.Emoji 2.0
+import Spectral.Dialog 2.0
import Spectral.Effect 2.0
import Spectral.Setting 0.1
@@ -21,6 +22,9 @@ Control {
property int autoCompleteBeginPosition
property int autoCompleteEndPosition
+ property bool hasAttachment: false
+ property url attachmentPath
+
id: root
padding: 0
@@ -171,13 +175,13 @@ Control {
Layout.alignment: Qt.AlignBottom
id: uploadButton
- visible: !isReply
+ visible: !isReply && !hasAttachment
contentItem: MaterialIcon {
icon: "\ue226"
}
- onClicked: currentRoom.chooseAndUploadFile()
+ onClicked: attachDialog.open()
BusyIndicator {
anchors.fill: parent
@@ -202,6 +206,51 @@ Control {
onClicked: clearReply()
}
+ Control {
+ Layout.margins: 6
+ Layout.preferredHeight: 36
+ Layout.alignment: Qt.AlignVCenter
+
+ visible: hasAttachment
+
+ rightPadding: 8
+
+ background: Rectangle {
+ color: MPalette.accent
+ radius: height / 2
+ antialiasing: true
+ }
+
+ contentItem: RowLayout {
+ spacing: 0
+
+ ToolButton {
+ Layout.preferredWidth: height
+ Layout.fillHeight: true
+
+ id: cancelAttachmentButton
+
+ contentItem: MaterialIcon {
+ icon: "\ue5cd"
+ color: "white"
+ font.pixelSize: 18
+ }
+
+ onClicked: {
+ hasAttachment = false
+ attachmentPath = ""
+ }
+ }
+
+ Label {
+ Layout.alignment: Qt.AlignVCenter
+
+ text: attachmentPath != "" ? attachmentPath.toString().substring(attachmentPath.toString().lastIndexOf('/') + 1, attachmentPath.length) : ""
+ color: "white"
+ }
+ }
+ }
+
TextArea {
property real progress: 0
@@ -252,7 +301,7 @@ Control {
Keys.onReturnPressed: {
if (event.modifiers & Qt.ShiftModifier) {
insert(cursorPosition, "\n")
- } else if (text) {
+ } else {
postMessage(text)
text = ""
closeAll()
@@ -304,9 +353,16 @@ Control {
}
function postMessage(text) {
- if (text.trim().length === 0) { return }
if(!currentRoom) { return }
+ if (hasAttachment) {
+ currentRoom.uploadFile(attachmentPath, text)
+ clearAttachment()
+ return
+ }
+
+ if (text.trim().length === 0) { return }
+
var PREFIX_ME = '/me '
var PREFIX_NOTICE = '/notice '
var PREFIX_RAINBOW = '/rainbow '
@@ -372,6 +428,10 @@ Control {
}
}
+ ImageClipboard {
+ id: imageClipboard
+ }
+
function insert(str) {
inputField.insert(inputField.cursorPosition, str)
}
@@ -396,4 +456,14 @@ Control {
autoCompleteListView.visible = false
emojiPicker.visible = false
}
+
+ function attach(localPath) {
+ hasAttachment = true
+ attachmentPath = localPath
+ }
+
+ function clearAttachment() {
+ hasAttachment = false
+ attachmentPath = ""
+ }
}
diff --git a/include/libqmatrixclient b/include/libqmatrixclient
index 52a81df..d6f39dc 160000
--- a/include/libqmatrixclient
+++ b/include/libqmatrixclient
@@ -1 +1 @@
-Subproject commit 52a81dfa8a5415be369d819837f445479b833cde
+Subproject commit d6f39dcb0de69322479f287514a8c36afcb3fe7b
diff --git a/qml/main.qml b/qml/main.qml
index 350b241..e28743d 100644
--- a/qml/main.qml
+++ b/qml/main.qml
@@ -56,12 +56,17 @@ ApplicationWindow {
quitOnLastWindowClosed: !MSettings.showTray
+ onErrorOccured: errorControl.show(error + ": " + detail, 3000)
+ }
+
+ NotificationsManager {
+ id: notificationsManager
+
onNotificationClicked: {
roomListForm.enteredRoom = spectralController.connection.room(roomId)
roomForm.goToEvent(eventId)
showWindow()
}
- onErrorOccured: errorControl.show(error + ": " + detail, 3000)
}
Shortcut {
diff --git a/res.qrc b/res.qrc
index a07c611..8ab4cf4 100644
--- a/res.qrc
+++ b/res.qrc
@@ -58,5 +58,6 @@
imports/Spectral/Dialog/OpenFileDialog.qml
imports/Spectral/Dialog/OpenFolderDialog.qml
imports/Spectral/Component/Timeline/VideoDelegate.qml
+ imports/Spectral/Component/AutoRectangle.qml
diff --git a/spectral.pro b/spectral.pro
index fe7103f..dfcab20 100644
--- a/spectral.pro
+++ b/spectral.pro
@@ -117,7 +117,6 @@ mac {
HEADERS += \
src/controller.h \
src/roomlistmodel.h \
- src/imageprovider.h \
src/messageeventmodel.h \
src/emojimodel.h \
src/spectralroom.h \
@@ -125,19 +124,22 @@ HEADERS += \
src/accountlistmodel.h \
src/spectraluser.h \
src/notifications/manager.h \
- src/utils.h
+ src/utils.h \
+ src/imageclipboard.h \
+ src/matriximageprovider.h
SOURCES += src/main.cpp \
src/controller.cpp \
src/roomlistmodel.cpp \
- src/imageprovider.cpp \
src/messageeventmodel.cpp \
src/emojimodel.cpp \
src/spectralroom.cpp \
src/userlistmodel.cpp \
src/accountlistmodel.cpp \
src/spectraluser.cpp \
- src/utils.cpp
+ src/utils.cpp \
+ src/imageclipboard.cpp \
+ src/matriximageprovider.cpp
unix:!mac {
SOURCES += src/notifications/managerlinux.cpp
diff --git a/spectral.pro.user b/spectral.pro.user
new file mode 100644
index 0000000..9164e25
--- /dev/null
+++ b/spectral.pro.user
@@ -0,0 +1,223 @@
+
+
+
+
+
+ EnvironmentId
+ {7614c50b-52db-49bf-a585-31417a3c0f62}
+
+
+ ProjectExplorer.Project.ActiveTarget
+ 0
+
+
+ ProjectExplorer.Project.EditorSettings
+
+ true
+ false
+ true
+
+ Cpp
+
+ CppGlobal
+
+
+
+ QmlJS
+
+ QmlJSGlobal
+
+
+ 2
+ UTF-8
+ false
+ 4
+ false
+ 80
+ true
+ true
+ 1
+ true
+ false
+ 0
+ true
+ true
+ 0
+ 8
+ true
+ 1
+ true
+ true
+ true
+ false
+
+
+
+ ProjectExplorer.Project.PluginSettings
+
+
+ true
+
+
+
+ ProjectExplorer.Project.Target.0
+
+ Qt 5.12.1 (System)
+ Qt 5.12.1 (System)
+ {97a6fc6e-df2b-4272-9664-99a23fd81def}
+ 0
+ 0
+ 0
+
+ /home/tanuj/code/spectral/build
+
+
+ true
+ qmake
+
+ QtProjectManager.QMakeBuildStep
+ false
+
+ false
+ false
+ false
+
+
+ true
+ Make
+
+ Qt4ProjectManager.MakeStep
+
+ false
+
+
+ false
+
+ 2
+ Build
+
+ ProjectExplorer.BuildSteps.Build
+
+
+
+ true
+ Make
+
+ Qt4ProjectManager.MakeStep
+
+ true
+ clean
+
+ false
+
+ 1
+ Clean
+
+ ProjectExplorer.BuildSteps.Clean
+
+ 2
+ false
+
+ Release
+ Release
+ Qt4ProjectManager.Qt4BuildConfiguration
+ 0
+ true
+
+ 1
+
+
+ 0
+ Deploy
+
+ ProjectExplorer.BuildSteps.Deploy
+
+ 1
+ Deploy Configuration
+
+ ProjectExplorer.DefaultDeployConfiguration
+
+ 1
+
+
+ dwarf
+
+ cpu-cycles
+
+
+ 250
+ -F
+ true
+ 4096
+ false
+ false
+ 1000
+
+ true
+
+ false
+ false
+ false
+ false
+ true
+ 0.01
+ 10
+ true
+ kcachegrind
+ 1
+ 25
+
+ 1
+ true
+ false
+ true
+ valgrind
+
+ 0
+ 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+ 10
+ 11
+ 12
+ 13
+ 14
+
+ 2
+
+ spectral
+
+ Qt4ProjectManager.Qt4RunConfiguration:/home/tanuj/code/spectral/spectral.pro
+
+ 3768
+ false
+ true
+ true
+ false
+ false
+ true
+
+ /home/tanuj/code/spectral/build
+
+ 1
+
+
+
+ ProjectExplorer.Project.TargetCount
+ 1
+
+
+ ProjectExplorer.Project.Updater.FileVersion
+ 21
+
+
+ Version
+ 21
+
+
diff --git a/src/controller.cpp b/src/controller.cpp
index d305f38..4f52ebd 100644
--- a/src/controller.cpp
+++ b/src/controller.cpp
@@ -31,13 +31,9 @@
#include
#include
-Controller::Controller(QObject* parent)
- : QObject(parent), notificationsManager(this) {
+Controller::Controller(QObject* parent) : QObject(parent) {
QApplication::setQuitOnLastWindowClosed(false);
- connect(¬ificationsManager, &NotificationsManager::notificationClicked,
- this, &Controller::notificationClicked);
-
Connection::setRoomType();
Connection::setUserType();
@@ -232,10 +228,6 @@ void Controller::createDirectChat(Connection* c, const QString& userID) {
});
}
-void Controller::copyToClipboard(const QString& text) {
- m_clipboard->setText(text);
-}
-
void Controller::playAudio(QUrl localFile) {
QMediaPlayer* player = new QMediaPlayer;
player->setMedia(localFile);
@@ -243,16 +235,6 @@ void Controller::playAudio(QUrl localFile) {
connect(player, &QMediaPlayer::stateChanged, [=] { player->deleteLater(); });
}
-void Controller::postNotification(const QString& roomId,
- const QString& eventId,
- const QString& roomName,
- const QString& senderName,
- const QString& text,
- const QImage& icon) {
- notificationsManager.postNotification(roomId, eventId, roomName, senderName,
- text, icon);
-}
-
int Controller::dpi() {
return SettingsGroup("Interface").value("dpi", 100).toInt();
}
diff --git a/src/controller.h b/src/controller.h
index 2e73c21..cb25b1b 100644
--- a/src/controller.h
+++ b/src/controller.h
@@ -67,8 +67,6 @@ class Controller : public QObject {
}
private:
- QClipboard* m_clipboard = QApplication::clipboard();
- NotificationsManager notificationsManager;
QVector m_connections;
QPointer m_connection;
@@ -99,14 +97,7 @@ class Controller : public QObject {
void joinRoom(Connection* c, const QString& alias);
void createRoom(Connection* c, const QString& name, const QString& topic);
void createDirectChat(Connection* c, const QString& userID);
- void copyToClipboard(const QString& text);
void playAudio(QUrl localFile);
- void postNotification(const QString& roomId,
- const QString& eventId,
- const QString& roomName,
- const QString& senderName,
- const QString& text,
- const QImage& icon);
};
#endif // CONTROLLER_H
diff --git a/src/imageclipboard.cpp b/src/imageclipboard.cpp
new file mode 100644
index 0000000..d825835
--- /dev/null
+++ b/src/imageclipboard.cpp
@@ -0,0 +1,40 @@
+#include "imageclipboard.h"
+
+#include
+#include
+#include
+#include
+#include
+
+ImageClipboard::ImageClipboard(QObject* parent)
+ : QObject(parent), m_clipboard(QGuiApplication::clipboard()) {
+ connect(m_clipboard, &QClipboard::changed, this,
+ &ImageClipboard::imageChanged);
+}
+
+bool ImageClipboard::hasImage() {
+ return !image().isNull();
+}
+
+QImage ImageClipboard::image() {
+ return m_clipboard->image();
+}
+
+bool ImageClipboard::saveImage(const QUrl& localPath) {
+ if (!localPath.isLocalFile())
+ return false;
+
+ auto i = image();
+
+ if (i.isNull())
+ return false;
+
+ QString path = QFileInfo(localPath.toString()).absolutePath();
+ QDir dir;
+ if (!dir.exists(path))
+ dir.mkpath(path);
+
+ i.save(localPath.toLocalFile());
+
+ return true;
+}
diff --git a/src/imageclipboard.h b/src/imageclipboard.h
new file mode 100644
index 0000000..a8cc181
--- /dev/null
+++ b/src/imageclipboard.h
@@ -0,0 +1,28 @@
+#ifndef IMAGECLIPBOARD_H
+#define IMAGECLIPBOARD_H
+
+#include
+#include
+#include
+
+class ImageClipboard : public QObject {
+ Q_OBJECT
+ Q_PROPERTY(bool hasImage READ hasImage NOTIFY imageChanged)
+ Q_PROPERTY(QImage image READ image NOTIFY imageChanged)
+
+ public:
+ explicit ImageClipboard(QObject* parent = nullptr);
+
+ bool hasImage();
+ QImage image();
+
+ Q_INVOKABLE bool saveImage(const QUrl& localPath);
+
+ private:
+ QClipboard* m_clipboard;
+
+ signals:
+ void imageChanged();
+};
+
+#endif // IMAGECLIPBOARD_H
diff --git a/src/main.cpp b/src/main.cpp
index 60727f3..b6ee77f 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -8,8 +8,10 @@
#include "accountlistmodel.h"
#include "controller.h"
#include "emojimodel.h"
-#include "imageprovider.h"
+#include "imageclipboard.h"
+#include "matriximageprovider.h"
#include "messageeventmodel.h"
+#include "notifications/manager.h"
#include "room.h"
#include "roomlistmodel.h"
#include "spectralroom.h"
@@ -55,6 +57,9 @@ int main(int argc, char* argv[]) {
qmlRegisterType("Spectral", 0, 1, "UserListModel");
qmlRegisterType("Spectral", 0, 1, "MessageEventModel");
qmlRegisterType("Spectral", 0, 1, "EmojiModel");
+ qmlRegisterType("Spectral", 0, 1,
+ "NotificationsManager");
+ qmlRegisterType("Spectral", 0, 1, "ImageClipboard");
qmlRegisterUncreatableType("Spectral", 0, 1,
"RoomMessageEvent", "ENUM");
qmlRegisterUncreatableType("Spectral", 0, 1, "RoomType", "ENUM");
@@ -72,9 +77,10 @@ int main(int argc, char* argv[]) {
QQmlApplicationEngine engine;
engine.addImportPath("qrc:/imports");
- ImageProvider* m_provider = new ImageProvider();
- engine.rootContext()->setContextProperty("imageProvider", m_provider);
- engine.addImageProvider(QLatin1String("mxc"), m_provider);
+ MatrixImageProvider* matrixImageProvider = new MatrixImageProvider();
+ engine.rootContext()->setContextProperty("imageProvider",
+ matrixImageProvider);
+ engine.addImageProvider(QLatin1String("mxc"), matrixImageProvider);
engine.load(QUrl(QStringLiteral("qrc:/qml/main.qml")));
if (engine.rootObjects().isEmpty())
diff --git a/src/imageprovider.cpp b/src/matriximageprovider.cpp
similarity index 97%
rename from src/imageprovider.cpp
rename to src/matriximageprovider.cpp
index be98de2..13c4ca5 100644
--- a/src/imageprovider.cpp
+++ b/src/matriximageprovider.cpp
@@ -1,4 +1,4 @@
-#include "imageprovider.h"
+#include "matriximageprovider.h"
#include
#include
@@ -108,7 +108,7 @@ void ThumbnailResponse::cancel() {
Qt::QueuedConnection);
}
-QQuickImageResponse* ImageProvider::requestImageResponse(
+QQuickImageResponse* MatrixImageProvider::requestImageResponse(
const QString& id,
const QSize& requestedSize) {
return new ThumbnailResponse(m_connection.load(), id, requestedSize);
diff --git a/src/imageprovider.h b/src/matriximageprovider.h
similarity index 87%
rename from src/imageprovider.h
rename to src/matriximageprovider.h
index ed31e94..980ed3f 100644
--- a/src/imageprovider.h
+++ b/src/matriximageprovider.h
@@ -1,5 +1,5 @@
-#ifndef IMAGEPROVIDER_H
-#define IMAGEPROVIDER_H
+#ifndef MatrixImageProvider_H
+#define MatrixImageProvider_H
#pragma once
#include
@@ -43,12 +43,12 @@ class ThumbnailResponse : public QQuickImageResponse {
void cancel() override;
};
-class ImageProvider : public QObject, public QQuickAsyncImageProvider {
+class MatrixImageProvider : public QObject, public QQuickAsyncImageProvider {
Q_OBJECT
Q_PROPERTY(QMatrixClient::Connection* connection READ connection WRITE
setConnection NOTIFY connectionChanged)
public:
- explicit ImageProvider() = default;
+ explicit MatrixImageProvider() = default;
QQuickImageResponse* requestImageResponse(
const QString& id,
@@ -67,4 +67,4 @@ class ImageProvider : public QObject, public QQuickAsyncImageProvider {
QAtomicPointer m_connection;
};
-#endif // IMAGEPROVIDER_H
+#endif // MatrixImageProvider_H
diff --git a/src/notifications/manager.h b/src/notifications/manager.h
index 0115713..5e7888f 100644
--- a/src/notifications/manager.h
+++ b/src/notifications/manager.h
@@ -19,11 +19,7 @@ struct roomEventId {
class NotificationsManager : public QObject {
Q_OBJECT
public:
- NotificationsManager(QObject *parent = nullptr);
-
- void postNotification(const QString &roomId, const QString &eventId,
- const QString &roomName, const QString &senderName,
- const QString &text, const QImage &icon);
+ NotificationsManager(QObject* parent = nullptr);
signals:
void notificationClicked(const QString roomId, const QString eventId);
@@ -31,7 +27,8 @@ class NotificationsManager : public QObject {
private:
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
QDBusInterface dbus;
- uint showNotification(const QString summary, const QString text,
+ uint showNotification(const QString summary,
+ const QString text,
const QImage image);
#endif
@@ -43,9 +40,16 @@ class NotificationsManager : public QObject {
public slots:
void actionInvoked(uint id, QString action);
void notificationClosed(uint id, uint reason);
+
+ void postNotification(const QString& roomId,
+ const QString& eventId,
+ const QString& roomName,
+ const QString& senderName,
+ const QString& text,
+ const QImage& icon);
};
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
-QDBusArgument &operator<<(QDBusArgument &arg, const QImage &image);
-const QDBusArgument &operator>>(const QDBusArgument &arg, QImage &);
+QDBusArgument& operator<<(QDBusArgument& arg, const QImage& image);
+const QDBusArgument& operator>>(const QDBusArgument& arg, QImage&);
#endif
diff --git a/src/spectralroom.cpp b/src/spectralroom.cpp
index ca5e207..ddbdb95 100644
--- a/src/spectralroom.cpp
+++ b/src/spectralroom.cpp
@@ -44,33 +44,33 @@ inline QSize getImageSize(const QUrl& imageUrl) {
return reader.size();
}
-void SpectralRoom::chooseAndUploadFile() {
- auto localFile = QFileDialog::getOpenFileUrl(Q_NULLPTR, tr("Save File as"));
- if (!localFile.isEmpty()) {
- QString txnID = postFile(localFile.fileName(), localFile, false);
- setHasFileUploading(true);
- connect(this, &Room::fileTransferCompleted,
- [=](QString id, QUrl localFile, QUrl mxcUrl) {
- if (id == txnID) {
- setFileUploadingProgress(0);
- setHasFileUploading(false);
- }
- });
- connect(this, &Room::fileTransferFailed, [=](QString id, QString error) {
- if (id == txnID) {
- setFileUploadingProgress(0);
- setHasFileUploading(false);
- }
- });
- connect(
- this, &Room::fileTransferProgress,
- [=](QString id, qint64 progress, qint64 total) {
- if (id == txnID) {
- qDebug() << "Progress:" << progress << total;
- setFileUploadingProgress(int(float(progress) / float(total) * 100));
- }
- });
- }
+void SpectralRoom::uploadFile(const QUrl& url, const QString& body) {
+ if (url.isEmpty())
+ return;
+
+ QString txnID = postFile(body.isEmpty() ? url.fileName() : body, url, false);
+ setHasFileUploading(true);
+ connect(this, &Room::fileTransferCompleted,
+ [=](QString id, QUrl localFile, QUrl mxcUrl) {
+ if (id == txnID) {
+ setFileUploadingProgress(0);
+ setHasFileUploading(false);
+ }
+ });
+ connect(this, &Room::fileTransferFailed, [=](QString id, QString error) {
+ if (id == txnID) {
+ setFileUploadingProgress(0);
+ setHasFileUploading(false);
+ }
+ });
+ connect(
+ this, &Room::fileTransferProgress,
+ [=](QString id, qint64 progress, qint64 total) {
+ if (id == txnID) {
+ qDebug() << "Progress:" << progress << total;
+ setFileUploadingProgress(int(float(progress) / float(total) * 100));
+ }
+ });
}
void SpectralRoom::acceptInvitation() {
diff --git a/src/spectralroom.h b/src/spectralroom.h
index 205c3f6..10d7d37 100644
--- a/src/spectralroom.h
+++ b/src/spectralroom.h
@@ -254,7 +254,7 @@ class SpectralRoom : public Room {
void fileUploadingProgressChanged();
public slots:
- void chooseAndUploadFile();
+ void uploadFile(const QUrl& url, const QString& body = "");
void acceptInvitation();
void forget();
void sendTypingNotification(bool isTyping);
diff --git a/src/spectraluser.cpp b/src/spectraluser.cpp
index cf0e67d..613aebf 100644
--- a/src/spectraluser.cpp
+++ b/src/spectraluser.cpp
@@ -1 +1,5 @@
#include "spectraluser.h"
+
+QColor SpectralUser::color() {
+ return QColor::fromHslF(hueF(), 0.7, 0.5, 1);
+}
diff --git a/src/spectraluser.h b/src/spectraluser.h
index e62e51e..3cb7734 100644
--- a/src/spectraluser.h
+++ b/src/spectraluser.h
@@ -10,9 +10,12 @@ using namespace QMatrixClient;
class SpectralUser : public User {
Q_OBJECT
+ Q_PROPERTY(QColor color READ color CONSTANT)
public:
SpectralUser(QString userId, Connection* connection)
: User(userId, connection) {}
+
+ QColor color();
};
#endif // SpectralUser_H