Deprecated QT-based UI for Cwtch: For the new Flutter UI go to: https://git.openprivacy.ca/cwtch.im/cwtch-ui https://cwtch.im
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
This repo is archived. You can view files and clone it, but cannot push or open issues/pull-requests.
 
 
 
 
 
 

223 lines
6.1 KiB

package ui
import (
"cwtch.im/cwtch/model"
"cwtch.im/ui/go/the"
"encoding/hex"
"fmt"
"git.openprivacy.ca/openprivacy/log"
"github.com/therecipe/qt/core"
"reflect"
)
type MessageModel struct {
core.QAbstractTableModel
ackIdx int
handle string
_ func(string) `signal:"setHandle,auto"`
_ map[int]*core.QByteArray `property:"roles"`
_ func() `constructor:"init"`
_ func(int) *MessageWrapper `slot:"getMessage,auto"`
_ func(int) `signal:"addMessage,auto"`
_ func(int) `signal:"editMessage,auto"`
_ func() `signal:"requestEIR,auto"`
_ func(string) string `slot:"getNick,auto"`
_ func(string) string `slot:"getImage,auto"`
}
type MessageWrapper struct {
core.QObject
model.Message
_ int64 `property:"timestamp"`
_ string `property:"peerID"`
_ bool `property:"acknowledged"`
_ string `property:"rawMessage"`
_ string `property:"error"`
_ string `property:"day"`
_ string `property:"signature"`
_ bool `property:"ackd"`
}
type OverlayJSONObject struct {
Overlay int `json:"o"`
Data string `json:"d"`
}
func (this *MessageModel) Handle() string {
return this.handle
}
func (this *MessageModel) setHandle(handle string) {
this.handle = handle
}
func (this *MessageModel) init() {
sacrificialObject := NewMessageWrapper(nil)
mdt := reflect.TypeOf(*sacrificialObject)
roles := make(map[int]*core.QByteArray)
for i := 0; i < mdt.NumField(); i++ {
fieldName := mdt.Field(i).Name
if fieldName == "_" {
fieldName = mdt.Field(i).Tag.Get("property")
}
if fieldName == "acknowledged" {
this.ackIdx = int(core.Qt__UserRole) + 1 + i
}
roles[int(core.Qt__UserRole)+1+i] = core.NewQByteArray2(fieldName, -1)
}
roles[int(core.Qt__DisplayRole)] = core.NewQByteArray2("display", -1)
this.SetRoles(roles)
this.ConnectData(this.data)
this.ConnectRowCount(this.rowCount)
this.ConnectColumnCount(this.columnCount)
this.ConnectHeaderData(this.headerData)
this.ConnectRoleNames(this.roleNames)
}
func (this *MessageModel) roleNames() map[int]*core.QByteArray {
return this.Roles()
}
func (this *MessageModel) isGroup() bool {
return len(this.Handle()) == 32
}
func (this *MessageModel) getNick(handle string) string {
return GetNick(handle)
}
func (this *MessageModel) getImage(handle string) string {
return GetProfilePic(handle)
}
func (this *MessageModel) num() int {
if this.Handle() == "" || the.Peer == nil {
log.Debugf("MessageModel.num: early returning 0")
return 0
}
if this.isGroup() {
group := the.Peer.GetGroup(this.Handle())
if group != nil {
return len(group.Timeline.Messages) + len(group.UnacknowledgedMessages)
}
} else {
contact := the.Peer.GetContact(this.Handle())
if contact != nil {
return len(contact.Timeline.Messages)
}
}
log.Warnf("MessageModel.num: group/contact was nil, returning 0")
return 0
}
// caveat emptor: accessing messages this way returns a MessageWrapper QObject whose properties have not yet been
// initialized (but soon will be). accessing .Message parent class properties instead should work right away.
func (this *MessageModel) getMessage(idx int) *MessageWrapper {
modelmsg := model.Message{Message: "[an unexpected cwtch error occurred]"}
var ackd bool
if this.isGroup() {
group := the.Peer.GetGroup(this.Handle())
if idx >= len(group.Timeline.Messages) {
modelmsg = group.UnacknowledgedMessages[idx-len(group.Timeline.Messages)]
} else {
modelmsg = group.Timeline.Messages[idx]
ackd = true
}
} else {
if this.Handle() != "" && the.Peer != nil {
contact := the.Peer.GetContact(this.Handle())
if contact != nil {
if idx >= len(contact.Timeline.Messages) {
log.Errorf("requested message[%d] of only %d", idx, len(contact.Timeline.Messages))
} else {
modelmsg = contact.Timeline.Messages[idx]
ackd = modelmsg.Acknowledged
}
}
}
}
mw := NewMessageWrapper(nil)
mw.Message = modelmsg
mw.SetTimestamp(modelmsg.Timestamp.Unix())
mw.SetPeerID(modelmsg.PeerID)
mw.SetError(modelmsg.Error)
mw.SetAcknowledged(ackd)
mw.SetAckd(ackd)//??why both??
mw.SetDay(modelmsg.Timestamp.Format("January 2, 2006"))
mw.SetSignature(hex.EncodeToString(modelmsg.Signature))
mw.SetRawMessage(modelmsg.Message)
return mw
}
func (this *MessageModel) data(index *core.QModelIndex, role int) *core.QVariant {
if !index.IsValid() {
return core.NewQVariant()
}
if index.Row() >= this.num() {
return core.NewQVariant()
}
if role == int(core.Qt__DisplayRole) {
role = index.Column() + int(core.Qt__UserRole) + 1
}
if role == this.ackIdx {
return core.NewQVariant1((*this.getMessage(index.Row())).Acknowledged)
}
return core.NewQVariant1(fmt.Sprintf("unimplemented role %s (%d)", this.roleNames()[role].ConstData(), role))
}
func (this *MessageModel) headerData(section int, orientation core.Qt__Orientation, role int) *core.QVariant {
if role != int(core.Qt__DisplayRole) || orientation == core.Qt__Vertical {
return this.HeaderDataDefault(section, orientation, role)
}
mdt := reflect.TypeOf([]MessageWrapper{}).Elem()
return core.NewQVariant12(mdt.Field(section).Name)
}
func (this *MessageModel) rowCount(parent *core.QModelIndex) int {
return this.num()
}
func (this *MessageModel) columnCount(parent *core.QModelIndex) int {
return reflect.TypeOf(MessageWrapper{}).NumField()
}
// perform this.BeginInsertRows() on the gui thread
// important:
// 1. idx MUST be set to this.num()'s value *before* calling addMessage()
// 2. insert the message yourself
// 3. this.RequestEIR() *must* be called afterward
func (this *MessageModel) addMessage(idx int) {
this.BeginInsertRows(core.NewQModelIndex(), idx, idx)
}
// perform this.EndInsertRows() on the gui thread after an AddMessage()
func (this *MessageModel) requestEIR() {
this.EndInsertRows()
}
// notify the gui that the message acknowledgement at index idx has been modified
func (this *MessageModel) editMessage(idx int) {
if idx < 0 || idx >= this.num() {
log.Debugf("cant edit message %v. probably fine", idx)
return
}
indexObject := this.Index(idx, 0, core.NewQModelIndex())
// replace third param with []int{} to update all attributes instead
this.DataChanged(indexObject, indexObject, []int{this.ackIdx})
}