2018-03-25 13:21:59 +02:00
|
|
|
// gomuks - A terminal Matrix client written in Go.
|
2020-04-19 17:10:14 +02:00
|
|
|
// Copyright (C) 2020 Tulir Asokan
|
2018-03-25 13:21:59 +02:00
|
|
|
//
|
|
|
|
// This program is free software: you can redistribute it and/or modify
|
2019-01-17 13:13:25 +01:00
|
|
|
// it under the terms of the GNU Affero General Public License as published by
|
2018-03-25 13:21:59 +02:00
|
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
|
|
// (at your option) any later version.
|
|
|
|
//
|
|
|
|
// This program is distributed in the hope that it will be useful,
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
2019-01-17 13:13:25 +01:00
|
|
|
// GNU Affero General Public License for more details.
|
2018-03-25 13:21:59 +02:00
|
|
|
//
|
2019-01-17 13:13:25 +01:00
|
|
|
// You should have received a copy of the GNU Affero General Public License
|
|
|
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
2018-03-25 13:21:59 +02:00
|
|
|
|
2018-04-09 22:45:54 +02:00
|
|
|
package ui
|
2018-03-25 13:21:59 +02:00
|
|
|
|
|
|
|
import (
|
2019-01-17 13:13:25 +01:00
|
|
|
"math"
|
2018-04-24 01:13:17 +02:00
|
|
|
"regexp"
|
2020-02-22 00:17:52 +01:00
|
|
|
"sort"
|
2018-04-24 01:13:17 +02:00
|
|
|
"strings"
|
2019-04-27 14:02:52 +02:00
|
|
|
|
|
|
|
sync "github.com/sasha-s/go-deadlock"
|
2018-03-26 16:22:47 +02:00
|
|
|
|
2020-04-16 18:27:35 +02:00
|
|
|
"maunium.net/go/mautrix/id"
|
2019-03-25 23:37:35 +01:00
|
|
|
"maunium.net/go/mauview"
|
2019-01-17 13:13:25 +01:00
|
|
|
"maunium.net/go/tcell"
|
2018-10-18 16:02:38 +02:00
|
|
|
|
2018-04-24 21:46:16 +02:00
|
|
|
"maunium.net/go/gomuks/debug"
|
2018-03-25 13:21:59 +02:00
|
|
|
"maunium.net/go/gomuks/matrix/rooms"
|
|
|
|
)
|
|
|
|
|
2020-02-22 00:17:52 +01:00
|
|
|
var tagOrder = map[string]int{
|
|
|
|
"net.maunium.gomuks.fake.invite": 4,
|
2021-03-11 21:53:13 +01:00
|
|
|
"m.favourite": 3,
|
2020-02-22 00:17:52 +01:00
|
|
|
"net.maunium.gomuks.fake.direct": 2,
|
2021-03-11 21:53:13 +01:00
|
|
|
"": 1,
|
|
|
|
"m.lowpriority": -1,
|
|
|
|
"m.server_notice": -2,
|
|
|
|
"net.maunium.gomuks.fake.leave": -3,
|
2020-02-22 00:17:52 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// TagNameList is a list of Matrix tag names where default names are sorted in a hardcoded way.
|
|
|
|
type TagNameList []string
|
|
|
|
|
|
|
|
func (tnl TagNameList) Len() int {
|
|
|
|
return len(tnl)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (tnl TagNameList) Less(i, j int) bool {
|
|
|
|
orderI, _ := tagOrder[tnl[i]]
|
|
|
|
orderJ, _ := tagOrder[tnl[j]]
|
|
|
|
if orderI != orderJ {
|
|
|
|
return orderI > orderJ
|
|
|
|
}
|
|
|
|
return strings.Compare(tnl[i], tnl[j]) > 0
|
|
|
|
}
|
|
|
|
|
|
|
|
func (tnl TagNameList) Swap(i, j int) {
|
|
|
|
tnl[i], tnl[j] = tnl[j], tnl[i]
|
|
|
|
}
|
|
|
|
|
2018-03-25 13:21:59 +02:00
|
|
|
type RoomList struct {
|
2019-04-27 14:02:21 +02:00
|
|
|
sync.RWMutex
|
|
|
|
|
2019-03-25 23:37:35 +01:00
|
|
|
parent *MainView
|
2018-03-25 13:21:59 +02:00
|
|
|
|
2018-04-24 01:13:17 +02:00
|
|
|
// The list of tags in display order.
|
2020-02-22 00:17:52 +01:00
|
|
|
tags TagNameList
|
2018-04-23 22:22:18 +02:00
|
|
|
// The list of rooms, in reverse order.
|
2018-05-22 17:30:25 +02:00
|
|
|
items map[string]*TagRoomList
|
2018-04-23 22:22:18 +02:00
|
|
|
// The selected room.
|
2018-04-24 01:13:17 +02:00
|
|
|
selected *rooms.Room
|
|
|
|
selectedTag string
|
2018-03-25 13:21:59 +02:00
|
|
|
|
2018-04-30 11:40:28 +02:00
|
|
|
scrollOffset int
|
2019-03-25 23:37:35 +01:00
|
|
|
height int
|
|
|
|
width int
|
2018-04-30 11:40:28 +02:00
|
|
|
|
2018-03-25 13:21:59 +02:00
|
|
|
// The item main text color.
|
|
|
|
mainTextColor tcell.Color
|
|
|
|
// The text color for selected items.
|
|
|
|
selectedTextColor tcell.Color
|
|
|
|
// The background color for selected items.
|
|
|
|
selectedBackgroundColor tcell.Color
|
|
|
|
}
|
|
|
|
|
2019-03-25 23:37:35 +01:00
|
|
|
func NewRoomList(parent *MainView) *RoomList {
|
2018-05-03 23:55:28 +02:00
|
|
|
list := &RoomList{
|
2019-03-25 23:37:35 +01:00
|
|
|
parent: parent,
|
|
|
|
|
2018-05-22 17:30:25 +02:00
|
|
|
items: make(map[string]*TagRoomList),
|
2020-03-03 19:58:33 +01:00
|
|
|
tags: []string{},
|
2018-03-25 13:21:59 +02:00
|
|
|
|
2018-04-30 11:40:28 +02:00
|
|
|
scrollOffset: 0,
|
|
|
|
|
2018-03-25 13:21:59 +02:00
|
|
|
mainTextColor: tcell.ColorWhite,
|
|
|
|
selectedTextColor: tcell.ColorWhite,
|
|
|
|
selectedBackgroundColor: tcell.ColorDarkGreen,
|
|
|
|
}
|
2018-05-03 23:55:28 +02:00
|
|
|
for _, tag := range list.tags {
|
2018-05-22 17:30:25 +02:00
|
|
|
list.items[tag] = NewTagRoomList(list, tag)
|
2018-05-03 23:55:28 +02:00
|
|
|
}
|
|
|
|
return list
|
2018-03-25 13:21:59 +02:00
|
|
|
}
|
|
|
|
|
2020-04-16 18:27:35 +02:00
|
|
|
func (list *RoomList) Contains(roomID id.RoomID) bool {
|
2019-04-27 14:02:21 +02:00
|
|
|
list.RLock()
|
|
|
|
defer list.RUnlock()
|
2018-05-22 17:30:25 +02:00
|
|
|
for _, trl := range list.items {
|
|
|
|
for _, room := range trl.All() {
|
2018-04-24 01:13:17 +02:00
|
|
|
if room.ID == roomID {
|
|
|
|
return true
|
|
|
|
}
|
2018-04-23 21:49:17 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2018-03-25 13:21:59 +02:00
|
|
|
func (list *RoomList) Add(room *rooms.Room) {
|
2020-02-20 00:10:26 +01:00
|
|
|
if room.IsReplaced() {
|
|
|
|
debug.Print(room.ID, "is replaced by", room.ReplacedBy(), "-> not adding to room list")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
debug.Print("Adding room to list", room.ID, room.GetTitle(), room.IsDirect, room.ReplacedBy(), room.Tags())
|
2018-04-24 01:13:17 +02:00
|
|
|
for _, tag := range room.Tags() {
|
2018-05-03 23:55:28 +02:00
|
|
|
list.AddToTag(tag, room)
|
2018-04-24 01:13:17 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-27 14:02:21 +02:00
|
|
|
func (list *RoomList) checkTag(tag string) {
|
|
|
|
index := list.indexTag(tag)
|
2018-04-24 01:13:17 +02:00
|
|
|
|
2018-05-22 17:30:25 +02:00
|
|
|
trl, ok := list.items[tag]
|
2018-04-24 01:13:17 +02:00
|
|
|
|
2018-05-22 17:30:25 +02:00
|
|
|
if ok && trl.IsEmpty() {
|
2020-03-03 19:58:33 +01:00
|
|
|
delete(list.items, tag)
|
2018-04-24 01:13:17 +02:00
|
|
|
ok = false
|
|
|
|
}
|
|
|
|
|
|
|
|
if ok && index == -1 {
|
|
|
|
list.tags = append(list.tags, tag)
|
2020-02-22 00:17:52 +01:00
|
|
|
sort.Sort(list.tags)
|
|
|
|
} else if !ok && index != -1 {
|
2018-04-24 01:13:17 +02:00
|
|
|
list.tags = append(list.tags[0:index], list.tags[index+1:]...)
|
2020-02-22 00:17:52 +01:00
|
|
|
}
|
2018-04-24 01:13:17 +02:00
|
|
|
}
|
|
|
|
|
2018-05-03 23:55:28 +02:00
|
|
|
func (list *RoomList) AddToTag(tag rooms.RoomTag, room *rooms.Room) {
|
2019-04-27 14:02:21 +02:00
|
|
|
list.Lock()
|
|
|
|
defer list.Unlock()
|
2018-05-22 17:30:25 +02:00
|
|
|
trl, ok := list.items[tag.Tag]
|
2018-04-24 01:13:17 +02:00
|
|
|
if !ok {
|
2020-03-01 21:00:42 +01:00
|
|
|
list.items[tag.Tag] = NewTagRoomList(list, tag.Tag, NewOrderedRoom(tag.Order, room))
|
2020-02-22 00:17:52 +01:00
|
|
|
} else {
|
|
|
|
trl.Insert(tag.Order, room)
|
2018-04-24 01:13:17 +02:00
|
|
|
}
|
2019-04-27 14:02:21 +02:00
|
|
|
list.checkTag(tag.Tag)
|
2018-03-25 13:21:59 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (list *RoomList) Remove(room *rooms.Room) {
|
2018-05-16 19:09:09 +02:00
|
|
|
for _, tag := range list.tags {
|
|
|
|
list.RemoveFromTag(tag, room)
|
2018-04-24 01:13:17 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (list *RoomList) RemoveFromTag(tag string, room *rooms.Room) {
|
2019-04-27 14:02:21 +02:00
|
|
|
list.Lock()
|
|
|
|
defer list.Unlock()
|
2018-05-22 17:30:25 +02:00
|
|
|
trl, ok := list.items[tag]
|
2018-04-24 01:13:17 +02:00
|
|
|
if !ok {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-05-22 17:30:25 +02:00
|
|
|
index := trl.Index(room)
|
2018-04-24 01:13:17 +02:00
|
|
|
if index == -1 {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-05-22 17:30:25 +02:00
|
|
|
trl.RemoveIndex(index)
|
2018-04-24 01:13:17 +02:00
|
|
|
|
2018-05-22 17:30:25 +02:00
|
|
|
if trl.IsEmpty() {
|
2018-05-17 15:30:02 +02:00
|
|
|
// delete(list.items, tag)
|
2018-04-24 01:13:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if room == list.selected {
|
|
|
|
if index > 0 {
|
2018-05-22 17:30:25 +02:00
|
|
|
list.selected = trl.All()[index-1].Room
|
|
|
|
} else if trl.Length() > 0 {
|
|
|
|
list.selected = trl.Visible()[0].Room
|
2018-04-24 01:13:17 +02:00
|
|
|
} else if len(list.items) > 0 {
|
|
|
|
for _, tag := range list.tags {
|
|
|
|
moreItems := list.items[tag]
|
2018-05-03 23:55:28 +02:00
|
|
|
if moreItems.Length() > 0 {
|
|
|
|
list.selected = moreItems.Visible()[0].Room
|
2018-04-24 01:13:17 +02:00
|
|
|
list.selectedTag = tag
|
|
|
|
}
|
2018-04-23 21:49:17 +02:00
|
|
|
}
|
2018-04-24 01:13:17 +02:00
|
|
|
} else {
|
|
|
|
list.selected = nil
|
|
|
|
list.selectedTag = ""
|
2018-04-23 21:49:17 +02:00
|
|
|
}
|
2018-03-25 13:21:59 +02:00
|
|
|
}
|
2019-04-27 14:02:21 +02:00
|
|
|
list.checkTag(tag)
|
2018-04-23 21:49:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (list *RoomList) Bump(room *rooms.Room) {
|
2019-04-27 14:02:21 +02:00
|
|
|
list.RLock()
|
|
|
|
defer list.RUnlock()
|
2018-04-24 01:13:17 +02:00
|
|
|
for _, tag := range room.Tags() {
|
2018-05-22 17:30:25 +02:00
|
|
|
trl, ok := list.items[tag.Tag]
|
2018-05-03 23:55:28 +02:00
|
|
|
if !ok {
|
|
|
|
return
|
2018-04-23 21:49:17 +02:00
|
|
|
}
|
2018-05-22 17:30:25 +02:00
|
|
|
trl.Bump(room)
|
2018-04-24 01:13:17 +02:00
|
|
|
}
|
2018-03-25 13:21:59 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (list *RoomList) Clear() {
|
2019-04-27 14:02:21 +02:00
|
|
|
list.Lock()
|
|
|
|
defer list.Unlock()
|
2018-05-22 17:30:25 +02:00
|
|
|
list.items = make(map[string]*TagRoomList)
|
2020-03-03 19:58:33 +01:00
|
|
|
list.tags = []string{}
|
2018-05-03 23:55:28 +02:00
|
|
|
for _, tag := range list.tags {
|
2018-05-22 17:30:25 +02:00
|
|
|
list.items[tag] = NewTagRoomList(list, tag)
|
2018-05-03 23:55:28 +02:00
|
|
|
}
|
2018-03-25 13:21:59 +02:00
|
|
|
list.selected = nil
|
2018-04-24 01:13:17 +02:00
|
|
|
list.selectedTag = ""
|
2018-03-25 13:21:59 +02:00
|
|
|
}
|
|
|
|
|
2018-04-24 01:13:17 +02:00
|
|
|
func (list *RoomList) SetSelected(tag string, room *rooms.Room) {
|
2018-03-25 13:21:59 +02:00
|
|
|
list.selected = room
|
2018-04-24 15:51:40 +02:00
|
|
|
list.selectedTag = tag
|
2018-04-30 11:40:28 +02:00
|
|
|
pos := list.index(tag, room)
|
2018-04-30 11:50:12 +02:00
|
|
|
if pos <= list.scrollOffset {
|
2018-05-03 23:55:28 +02:00
|
|
|
list.scrollOffset = pos - 1
|
2019-03-25 23:37:35 +01:00
|
|
|
} else if pos >= list.scrollOffset+list.height {
|
|
|
|
list.scrollOffset = pos - list.height + 1
|
2018-05-03 23:55:28 +02:00
|
|
|
}
|
|
|
|
if list.scrollOffset < 0 {
|
|
|
|
list.scrollOffset = 0
|
2018-04-30 11:40:28 +02:00
|
|
|
}
|
2018-04-25 06:01:00 +02:00
|
|
|
debug.Print("Selecting", room.GetTitle(), "in", list.GetTagDisplayName(tag))
|
2018-03-25 13:21:59 +02:00
|
|
|
}
|
|
|
|
|
2018-04-23 21:49:17 +02:00
|
|
|
func (list *RoomList) HasSelected() bool {
|
|
|
|
return list.selected != nil
|
|
|
|
}
|
|
|
|
|
2018-04-24 01:13:17 +02:00
|
|
|
func (list *RoomList) Selected() (string, *rooms.Room) {
|
|
|
|
return list.selectedTag, list.selected
|
|
|
|
}
|
|
|
|
|
|
|
|
func (list *RoomList) SelectedRoom() *rooms.Room {
|
2018-04-23 21:49:17 +02:00
|
|
|
return list.selected
|
|
|
|
}
|
|
|
|
|
2018-04-30 11:40:28 +02:00
|
|
|
func (list *RoomList) AddScrollOffset(offset int) {
|
|
|
|
list.scrollOffset += offset
|
|
|
|
contentHeight := list.ContentHeight()
|
2019-03-25 23:37:35 +01:00
|
|
|
if list.scrollOffset > contentHeight-list.height {
|
|
|
|
list.scrollOffset = contentHeight - list.height
|
2018-04-30 11:40:28 +02:00
|
|
|
}
|
2018-05-03 23:55:28 +02:00
|
|
|
if list.scrollOffset < 0 {
|
|
|
|
list.scrollOffset = 0
|
|
|
|
}
|
2018-04-30 11:40:28 +02:00
|
|
|
}
|
|
|
|
|
2018-04-24 01:13:17 +02:00
|
|
|
func (list *RoomList) First() (string, *rooms.Room) {
|
2019-04-27 14:02:21 +02:00
|
|
|
list.RLock()
|
|
|
|
defer list.RUnlock()
|
2019-06-15 00:11:51 +02:00
|
|
|
return list.first()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (list *RoomList) first() (string, *rooms.Room) {
|
2018-04-24 01:13:17 +02:00
|
|
|
for _, tag := range list.tags {
|
2018-05-22 17:30:25 +02:00
|
|
|
trl := list.items[tag]
|
|
|
|
if trl.HasVisibleRooms() {
|
|
|
|
return tag, trl.FirstVisible()
|
2018-04-24 01:13:17 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return "", nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (list *RoomList) Last() (string, *rooms.Room) {
|
2019-04-27 14:02:21 +02:00
|
|
|
list.RLock()
|
|
|
|
defer list.RUnlock()
|
2019-06-15 00:11:51 +02:00
|
|
|
return list.last()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (list *RoomList) last() (string, *rooms.Room) {
|
2018-04-24 01:13:17 +02:00
|
|
|
for tagIndex := len(list.tags) - 1; tagIndex >= 0; tagIndex-- {
|
|
|
|
tag := list.tags[tagIndex]
|
2018-05-22 17:30:25 +02:00
|
|
|
trl := list.items[tag]
|
|
|
|
if trl.HasVisibleRooms() {
|
|
|
|
return tag, trl.LastVisible()
|
2018-04-24 01:13:17 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return "", nil
|
|
|
|
}
|
|
|
|
|
2019-04-27 14:02:21 +02:00
|
|
|
func (list *RoomList) indexTag(tag string) int {
|
2018-04-24 01:13:17 +02:00
|
|
|
for index, entry := range list.tags {
|
|
|
|
if tag == entry {
|
|
|
|
return index
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return -1
|
|
|
|
}
|
|
|
|
|
|
|
|
func (list *RoomList) Previous() (string, *rooms.Room) {
|
2019-04-27 14:02:21 +02:00
|
|
|
list.RLock()
|
|
|
|
defer list.RUnlock()
|
2018-04-23 21:49:17 +02:00
|
|
|
if len(list.items) == 0 {
|
2018-04-24 01:13:17 +02:00
|
|
|
return "", nil
|
2018-04-23 21:49:17 +02:00
|
|
|
} else if list.selected == nil {
|
2019-06-15 00:11:51 +02:00
|
|
|
return list.first()
|
2018-04-21 20:53:52 +02:00
|
|
|
}
|
2018-04-23 21:49:17 +02:00
|
|
|
|
2018-05-22 17:30:25 +02:00
|
|
|
trl := list.items[list.selectedTag]
|
|
|
|
index := trl.IndexVisible(list.selected)
|
|
|
|
indexInvisible := trl.Index(list.selected)
|
2018-05-15 14:03:22 +02:00
|
|
|
if index == -1 && indexInvisible >= 0 {
|
2018-05-22 17:30:25 +02:00
|
|
|
num := trl.TotalLength() - indexInvisible
|
|
|
|
trl.maxShown = int(math.Ceil(float64(num)/10.0) * 10.0)
|
|
|
|
index = trl.IndexVisible(list.selected)
|
2018-05-15 14:03:22 +02:00
|
|
|
}
|
2018-05-03 23:55:28 +02:00
|
|
|
|
2018-05-22 17:30:25 +02:00
|
|
|
if index == trl.Length()-1 {
|
2019-04-27 14:02:21 +02:00
|
|
|
tagIndex := list.indexTag(list.selectedTag)
|
2018-04-24 21:46:16 +02:00
|
|
|
tagIndex--
|
|
|
|
for ; tagIndex >= 0; tagIndex-- {
|
|
|
|
prevTag := list.tags[tagIndex]
|
2018-05-22 17:30:25 +02:00
|
|
|
prevTRL := list.items[prevTag]
|
|
|
|
if prevTRL.HasVisibleRooms() {
|
|
|
|
return prevTag, prevTRL.LastVisible()
|
2018-04-24 01:13:17 +02:00
|
|
|
}
|
|
|
|
}
|
2019-06-15 00:11:51 +02:00
|
|
|
return list.last()
|
2018-05-15 14:03:22 +02:00
|
|
|
} else if index >= 0 {
|
2018-05-22 17:30:25 +02:00
|
|
|
return list.selectedTag, trl.Visible()[index+1].Room
|
2018-05-03 23:55:28 +02:00
|
|
|
}
|
2019-06-15 00:11:51 +02:00
|
|
|
return list.first()
|
2018-04-23 21:49:17 +02:00
|
|
|
}
|
|
|
|
|
2018-04-24 01:13:17 +02:00
|
|
|
func (list *RoomList) Next() (string, *rooms.Room) {
|
2019-04-27 14:02:21 +02:00
|
|
|
list.RLock()
|
|
|
|
defer list.RUnlock()
|
2018-04-23 21:49:17 +02:00
|
|
|
if len(list.items) == 0 {
|
2018-04-24 01:13:17 +02:00
|
|
|
return "", nil
|
2018-04-23 21:49:17 +02:00
|
|
|
} else if list.selected == nil {
|
2019-06-15 00:11:51 +02:00
|
|
|
return list.first()
|
2018-04-23 21:49:17 +02:00
|
|
|
}
|
|
|
|
|
2018-05-22 17:30:25 +02:00
|
|
|
trl := list.items[list.selectedTag]
|
|
|
|
index := trl.IndexVisible(list.selected)
|
|
|
|
indexInvisible := trl.Index(list.selected)
|
2018-05-15 14:03:22 +02:00
|
|
|
if index == -1 && indexInvisible >= 0 {
|
2018-05-22 17:30:25 +02:00
|
|
|
num := trl.TotalLength() - indexInvisible + 1
|
|
|
|
trl.maxShown = int(math.Ceil(float64(num)/10.0) * 10.0)
|
|
|
|
index = trl.IndexVisible(list.selected)
|
2018-05-15 14:03:22 +02:00
|
|
|
}
|
2018-05-03 23:55:28 +02:00
|
|
|
|
2018-05-15 14:03:22 +02:00
|
|
|
if index == 0 {
|
2019-04-27 14:02:21 +02:00
|
|
|
tagIndex := list.indexTag(list.selectedTag)
|
2018-04-24 21:46:16 +02:00
|
|
|
tagIndex++
|
|
|
|
for ; tagIndex < len(list.tags); tagIndex++ {
|
|
|
|
nextTag := list.tags[tagIndex]
|
2018-05-22 17:30:25 +02:00
|
|
|
nextTRL := list.items[nextTag]
|
|
|
|
if nextTRL.HasVisibleRooms() {
|
|
|
|
return nextTag, nextTRL.FirstVisible()
|
2018-04-24 01:13:17 +02:00
|
|
|
}
|
|
|
|
}
|
2019-06-15 00:11:51 +02:00
|
|
|
return list.first()
|
2018-05-15 14:03:22 +02:00
|
|
|
} else if index > 0 {
|
2018-05-22 17:30:25 +02:00
|
|
|
return list.selectedTag, trl.Visible()[index-1].Room
|
2018-04-23 21:49:17 +02:00
|
|
|
}
|
2019-06-15 00:11:51 +02:00
|
|
|
return list.last()
|
2018-04-23 21:49:17 +02:00
|
|
|
}
|
|
|
|
|
2018-10-18 16:02:38 +02:00
|
|
|
// NextWithActivity Returns next room with activity.
|
|
|
|
//
|
|
|
|
// Sorted by (in priority):
|
|
|
|
//
|
|
|
|
// - Highlights
|
|
|
|
// - Messages
|
|
|
|
// - Other traffic (joins, parts, etc)
|
|
|
|
//
|
|
|
|
// TODO: Sorting. Now just finds first room with new messages.
|
|
|
|
func (list *RoomList) NextWithActivity() (string, *rooms.Room) {
|
2019-04-27 14:02:21 +02:00
|
|
|
list.RLock()
|
|
|
|
defer list.RUnlock()
|
2018-10-18 16:02:38 +02:00
|
|
|
for tag, trl := range list.items {
|
|
|
|
for _, room := range trl.All() {
|
|
|
|
if room.HasNewMessages() {
|
|
|
|
return tag, room.Room
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// No room with activity found
|
|
|
|
return "", nil
|
|
|
|
}
|
|
|
|
|
2018-04-30 11:40:28 +02:00
|
|
|
func (list *RoomList) index(tag string, room *rooms.Room) int {
|
2019-04-27 14:02:21 +02:00
|
|
|
tagIndex := list.indexTag(tag)
|
2018-04-30 11:40:28 +02:00
|
|
|
if tagIndex == -1 {
|
|
|
|
return -1
|
|
|
|
}
|
|
|
|
|
2018-05-22 17:30:25 +02:00
|
|
|
trl, ok := list.items[tag]
|
2018-05-03 23:55:28 +02:00
|
|
|
localIndex := -1
|
|
|
|
if ok {
|
2018-05-22 17:30:25 +02:00
|
|
|
localIndex = trl.IndexVisible(room)
|
2018-05-03 23:55:28 +02:00
|
|
|
}
|
2018-04-30 11:40:28 +02:00
|
|
|
if localIndex == -1 {
|
|
|
|
return -1
|
|
|
|
}
|
2018-05-22 17:30:25 +02:00
|
|
|
localIndex = trl.Length() - 1 - localIndex
|
2018-04-30 11:40:28 +02:00
|
|
|
|
|
|
|
// Tag header
|
2018-05-22 17:43:00 +02:00
|
|
|
localIndex++
|
2018-04-30 11:40:28 +02:00
|
|
|
|
|
|
|
if tagIndex > 0 {
|
|
|
|
for i := 0; i < tagIndex; i++ {
|
2018-05-22 17:30:25 +02:00
|
|
|
prevTag := list.tags[i]
|
2019-04-27 14:02:21 +02:00
|
|
|
|
2018-05-22 17:30:25 +02:00
|
|
|
prevTRL := list.items[prevTag]
|
|
|
|
localIndex += prevTRL.RenderHeight()
|
2018-04-30 11:40:28 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return localIndex
|
|
|
|
}
|
|
|
|
|
|
|
|
func (list *RoomList) ContentHeight() (height int) {
|
2019-04-27 14:02:21 +02:00
|
|
|
list.RLock()
|
2018-04-30 11:40:28 +02:00
|
|
|
for _, tag := range list.tags {
|
2018-05-22 17:30:25 +02:00
|
|
|
height += list.items[tag].RenderHeight()
|
2018-04-30 11:40:28 +02:00
|
|
|
}
|
2019-04-27 14:02:21 +02:00
|
|
|
list.RUnlock()
|
2018-04-30 11:40:28 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-02-22 00:17:52 +01:00
|
|
|
func (list *RoomList) OnKeyEvent(_ mauview.KeyEvent) bool {
|
2019-03-25 23:37:35 +01:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2020-02-22 00:17:52 +01:00
|
|
|
func (list *RoomList) OnPasteEvent(_ mauview.PasteEvent) bool {
|
2019-03-25 23:37:35 +01:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
func (list *RoomList) OnMouseEvent(event mauview.MouseEvent) bool {
|
2019-03-30 17:51:26 +01:00
|
|
|
if event.HasMotion() {
|
|
|
|
return false
|
|
|
|
}
|
2019-03-25 23:37:35 +01:00
|
|
|
switch event.Buttons() {
|
|
|
|
case tcell.WheelUp:
|
|
|
|
list.AddScrollOffset(-WheelScrollOffsetDiff)
|
2019-06-15 16:04:08 +02:00
|
|
|
return true
|
2019-03-25 23:37:35 +01:00
|
|
|
case tcell.WheelDown:
|
|
|
|
list.AddScrollOffset(WheelScrollOffsetDiff)
|
2019-06-15 16:04:08 +02:00
|
|
|
return true
|
2019-03-25 23:37:35 +01:00
|
|
|
case tcell.Button1:
|
|
|
|
x, y := event.Position()
|
|
|
|
return list.clickRoom(y, x, event.Modifiers() == tcell.ModCtrl)
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
func (list *RoomList) Focus() {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
func (list *RoomList) Blur() {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
func (list *RoomList) clickRoom(line, column int, mod bool) bool {
|
2018-05-03 23:55:28 +02:00
|
|
|
line += list.scrollOffset
|
|
|
|
if line < 0 {
|
2019-03-25 23:37:35 +01:00
|
|
|
return false
|
2018-04-24 01:13:17 +02:00
|
|
|
}
|
2019-04-27 14:02:21 +02:00
|
|
|
list.RLock()
|
2018-04-24 01:13:17 +02:00
|
|
|
for _, tag := range list.tags {
|
2018-05-22 17:30:25 +02:00
|
|
|
trl := list.items[tag]
|
2018-05-03 23:55:28 +02:00
|
|
|
if line--; line == -1 {
|
2018-05-22 17:30:25 +02:00
|
|
|
trl.ToggleCollapse()
|
2019-04-27 14:11:38 +02:00
|
|
|
list.RUnlock()
|
2019-03-30 17:51:26 +01:00
|
|
|
return true
|
2018-05-03 23:55:28 +02:00
|
|
|
}
|
|
|
|
|
2018-05-22 17:30:25 +02:00
|
|
|
if trl.IsCollapsed() {
|
2018-05-03 23:55:28 +02:00
|
|
|
continue
|
|
|
|
}
|
2018-04-24 01:13:17 +02:00
|
|
|
|
2018-05-03 23:55:28 +02:00
|
|
|
if line < 0 {
|
2018-05-22 17:43:00 +02:00
|
|
|
break
|
2018-05-22 17:30:25 +02:00
|
|
|
} else if line < trl.Length() {
|
2019-04-27 14:11:38 +02:00
|
|
|
switchToRoom := trl.Visible()[trl.Length()-1-line].Room
|
|
|
|
list.RUnlock()
|
|
|
|
list.parent.SwitchRoom(tag, switchToRoom)
|
2019-03-25 23:37:35 +01:00
|
|
|
return true
|
2018-04-24 01:13:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Tag items
|
2018-05-22 17:30:25 +02:00
|
|
|
line -= trl.Length()
|
2018-05-03 23:55:28 +02:00
|
|
|
|
2018-05-22 17:30:25 +02:00
|
|
|
hasMore := trl.HasInvisibleRooms()
|
|
|
|
hasLess := trl.maxShown > 10
|
2018-05-03 23:55:28 +02:00
|
|
|
if hasMore || hasLess {
|
|
|
|
if line--; line == -1 {
|
|
|
|
diff := 10
|
|
|
|
if mod {
|
|
|
|
diff = 100
|
|
|
|
}
|
|
|
|
if column <= 6 && hasLess {
|
2018-05-22 17:30:25 +02:00
|
|
|
trl.maxShown -= diff
|
2019-03-25 23:37:35 +01:00
|
|
|
} else if column >= list.width-6 && hasMore {
|
2018-05-22 17:30:25 +02:00
|
|
|
trl.maxShown += diff
|
2018-05-03 23:55:28 +02:00
|
|
|
}
|
2018-05-22 17:30:25 +02:00
|
|
|
if trl.maxShown < 10 {
|
|
|
|
trl.maxShown = 10
|
2018-05-03 23:55:28 +02:00
|
|
|
}
|
2019-06-15 16:04:08 +02:00
|
|
|
list.RUnlock()
|
|
|
|
return true
|
2018-05-03 23:55:28 +02:00
|
|
|
}
|
|
|
|
}
|
2018-04-24 15:51:40 +02:00
|
|
|
// Tag footer
|
2018-05-03 23:55:28 +02:00
|
|
|
line--
|
2018-04-24 01:13:17 +02:00
|
|
|
}
|
2019-04-27 14:11:38 +02:00
|
|
|
list.RUnlock()
|
2019-03-25 23:37:35 +01:00
|
|
|
return false
|
2018-04-24 01:13:17 +02:00
|
|
|
}
|
2019-04-27 14:02:52 +02:00
|
|
|
|
2020-03-03 19:58:33 +01:00
|
|
|
var nsRegex = regexp.MustCompile("^[a-z]+\\.[a-z]+(?:\\.[a-z]+)*$")
|
2018-04-24 01:13:17 +02:00
|
|
|
|
|
|
|
func (list *RoomList) GetTagDisplayName(tag string) string {
|
|
|
|
switch {
|
|
|
|
case len(tag) == 0:
|
|
|
|
return "Rooms"
|
|
|
|
case tag == "m.favourite":
|
|
|
|
return "Favorites"
|
|
|
|
case tag == "m.lowpriority":
|
|
|
|
return "Low Priority"
|
2020-02-19 21:19:59 +01:00
|
|
|
case tag == "m.server_notice":
|
|
|
|
return "System Alerts"
|
2018-04-25 06:01:00 +02:00
|
|
|
case tag == "net.maunium.gomuks.fake.direct":
|
2018-04-24 21:46:16 +02:00
|
|
|
return "People"
|
2020-02-22 00:17:52 +01:00
|
|
|
case tag == "net.maunium.gomuks.fake.invite":
|
|
|
|
return "Invites"
|
|
|
|
case tag == "net.maunium.gomuks.fake.leave":
|
|
|
|
return "Historical"
|
2018-04-24 01:13:17 +02:00
|
|
|
case strings.HasPrefix(tag, "u."):
|
|
|
|
return tag[len("u."):]
|
|
|
|
case !nsRegex.MatchString(tag):
|
|
|
|
return tag
|
|
|
|
default:
|
|
|
|
return ""
|
2018-04-23 22:22:18 +02:00
|
|
|
}
|
2018-04-21 20:53:52 +02:00
|
|
|
}
|
|
|
|
|
2018-03-25 13:21:59 +02:00
|
|
|
// Draw draws this primitive onto the screen.
|
2019-03-25 23:37:35 +01:00
|
|
|
func (list *RoomList) Draw(screen mauview.Screen) {
|
|
|
|
list.width, list.height = screen.Size()
|
|
|
|
y := 0
|
|
|
|
yLimit := y + list.height
|
2018-05-22 17:30:25 +02:00
|
|
|
y -= list.scrollOffset
|
2018-03-25 13:21:59 +02:00
|
|
|
|
|
|
|
// Draw the list items.
|
2019-04-27 14:02:21 +02:00
|
|
|
list.RLock()
|
2018-04-24 01:13:17 +02:00
|
|
|
for _, tag := range list.tags {
|
2018-05-22 17:30:25 +02:00
|
|
|
trl := list.items[tag]
|
2018-04-25 06:01:00 +02:00
|
|
|
tagDisplayName := list.GetTagDisplayName(tag)
|
2019-01-11 22:28:47 +01:00
|
|
|
if trl == nil || len(tagDisplayName) == 0 {
|
2018-04-25 06:01:00 +02:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2018-05-22 17:30:25 +02:00
|
|
|
renderHeight := trl.RenderHeight()
|
2018-05-22 17:43:00 +02:00
|
|
|
if y+renderHeight >= yLimit {
|
2018-05-22 17:30:25 +02:00
|
|
|
renderHeight = yLimit - y
|
2018-03-25 13:21:59 +02:00
|
|
|
}
|
2019-03-25 23:37:35 +01:00
|
|
|
trl.Draw(mauview.NewProxyScreen(screen, 0, y, list.width, renderHeight))
|
2018-05-22 17:30:25 +02:00
|
|
|
y += renderHeight
|
|
|
|
if y >= yLimit {
|
|
|
|
break
|
2018-05-03 23:55:28 +02:00
|
|
|
}
|
2018-03-25 13:21:59 +02:00
|
|
|
}
|
2019-04-27 14:02:21 +02:00
|
|
|
list.RUnlock()
|
2018-03-25 13:21:59 +02:00
|
|
|
}
|