2019-03-11 03:45:00 +00:00
|
|
|
package widgets
|
|
|
|
|
|
|
|
import (
|
2019-03-15 01:37:00 +00:00
|
|
|
"log"
|
2019-04-28 13:26:22 +00:00
|
|
|
"sync/atomic"
|
2019-03-15 01:37:00 +00:00
|
|
|
|
|
|
|
"github.com/gdamore/tcell"
|
|
|
|
|
|
|
|
"git.sr.ht/~sircmpwn/aerc2/config"
|
2019-03-16 01:36:06 +00:00
|
|
|
"git.sr.ht/~sircmpwn/aerc2/lib"
|
2019-03-15 01:37:00 +00:00
|
|
|
"git.sr.ht/~sircmpwn/aerc2/lib/ui"
|
2019-03-21 03:23:38 +00:00
|
|
|
"git.sr.ht/~sircmpwn/aerc2/worker/types"
|
2019-03-11 03:45:00 +00:00
|
|
|
)
|
|
|
|
|
2019-03-15 01:37:00 +00:00
|
|
|
type MessageList struct {
|
2019-04-27 16:47:59 +00:00
|
|
|
ui.Invalidatable
|
|
|
|
conf *config.AercConfig
|
|
|
|
logger *log.Logger
|
|
|
|
height int
|
|
|
|
scroll int
|
|
|
|
selected int
|
|
|
|
spinner *Spinner
|
2019-04-28 13:26:22 +00:00
|
|
|
store atomic.Value // *lib.MessageStore
|
2019-03-15 01:37:00 +00:00
|
|
|
}
|
|
|
|
|
2019-05-17 15:52:38 +00:00
|
|
|
func NewMessageList(conf *config.AercConfig, logger *log.Logger) *MessageList {
|
2019-03-15 01:37:00 +00:00
|
|
|
ml := &MessageList{
|
2019-05-17 15:52:38 +00:00
|
|
|
conf: conf,
|
2019-03-15 02:19:04 +00:00
|
|
|
logger: logger,
|
|
|
|
selected: 0,
|
|
|
|
spinner: NewSpinner(),
|
2019-03-15 01:37:00 +00:00
|
|
|
}
|
2019-04-28 13:26:22 +00:00
|
|
|
ml.store.Store((*lib.MessageStore)(nil))
|
2019-03-15 01:37:00 +00:00
|
|
|
ml.spinner.OnInvalidate(func(_ ui.Drawable) {
|
|
|
|
ml.Invalidate()
|
|
|
|
})
|
|
|
|
// TODO: stop spinner, probably
|
|
|
|
ml.spinner.Start()
|
|
|
|
return ml
|
|
|
|
}
|
|
|
|
|
|
|
|
func (ml *MessageList) Invalidate() {
|
2019-04-27 16:47:59 +00:00
|
|
|
ml.DoInvalidate(ml)
|
2019-03-15 01:37:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (ml *MessageList) Draw(ctx *ui.Context) {
|
2019-03-16 01:41:18 +00:00
|
|
|
ml.height = ctx.Height()
|
2019-03-15 01:37:00 +00:00
|
|
|
ctx.Fill(0, 0, ctx.Width(), ctx.Height(), ' ', tcell.StyleDefault)
|
|
|
|
|
2019-04-28 13:26:22 +00:00
|
|
|
store := ml.Store()
|
|
|
|
if store == nil {
|
2019-03-15 01:37:00 +00:00
|
|
|
ml.spinner.Draw(ctx)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2019-04-28 13:26:38 +00:00
|
|
|
store.Lock()
|
|
|
|
|
2019-03-15 01:37:00 +00:00
|
|
|
var (
|
2019-03-15 01:51:29 +00:00
|
|
|
needsHeaders []uint32
|
2019-03-15 01:37:00 +00:00
|
|
|
row int = 0
|
|
|
|
)
|
|
|
|
|
2019-04-28 13:26:22 +00:00
|
|
|
for i := len(store.Uids) - 1 - ml.scroll; i >= 0; i-- {
|
|
|
|
uid := store.Uids[i]
|
|
|
|
msg := store.Messages[uid]
|
2019-03-15 02:19:04 +00:00
|
|
|
|
2019-03-15 01:37:00 +00:00
|
|
|
if row >= ctx.Height() {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
if msg == nil {
|
|
|
|
needsHeaders = append(needsHeaders, uid)
|
|
|
|
ml.spinner.Draw(ctx.Subcontext(0, row, ctx.Width(), 1))
|
2019-03-15 02:19:04 +00:00
|
|
|
row += 1
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
style := tcell.StyleDefault
|
2019-03-16 01:49:40 +00:00
|
|
|
if row == ml.selected-ml.scroll {
|
2019-03-30 16:59:18 +00:00
|
|
|
style = style.Reverse(true)
|
2019-03-15 01:37:00 +00:00
|
|
|
}
|
2019-04-28 13:26:22 +00:00
|
|
|
if _, ok := store.Deleted[msg.Uid]; ok {
|
2019-03-30 14:40:55 +00:00
|
|
|
style = style.Foreground(tcell.ColorGray)
|
|
|
|
}
|
2019-03-15 02:19:04 +00:00
|
|
|
ctx.Fill(0, row, ctx.Width(), 1, ' ', style)
|
|
|
|
ctx.Printf(0, row, style, "%s", msg.Envelope.Subject)
|
2019-03-15 01:37:00 +00:00
|
|
|
|
|
|
|
row += 1
|
|
|
|
}
|
|
|
|
|
2019-04-28 13:26:22 +00:00
|
|
|
if len(store.Uids) == 0 {
|
2019-05-17 15:42:34 +00:00
|
|
|
msg := ml.conf.Ui.EmptyMessage
|
2019-04-04 18:25:51 +00:00
|
|
|
ctx.Printf((ctx.Width()/2)-(len(msg)/2), 0,
|
|
|
|
tcell.StyleDefault, "%s", msg)
|
|
|
|
}
|
|
|
|
|
2019-04-28 13:26:38 +00:00
|
|
|
store.Unlock()
|
|
|
|
|
2019-03-15 01:37:00 +00:00
|
|
|
if len(needsHeaders) != 0 {
|
2019-04-28 13:26:22 +00:00
|
|
|
store.FetchHeaders(needsHeaders, nil)
|
2019-03-15 01:37:00 +00:00
|
|
|
ml.spinner.Start()
|
|
|
|
} else {
|
|
|
|
ml.spinner.Stop()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-16 01:41:18 +00:00
|
|
|
func (ml *MessageList) Height() int {
|
|
|
|
return ml.height
|
|
|
|
}
|
|
|
|
|
2019-03-21 03:23:38 +00:00
|
|
|
func (ml *MessageList) storeUpdate(store *lib.MessageStore) {
|
2019-04-28 13:26:22 +00:00
|
|
|
if ml.Store() != store {
|
2019-03-21 03:23:38 +00:00
|
|
|
return
|
|
|
|
}
|
2019-04-28 13:26:22 +00:00
|
|
|
|
2019-04-28 13:26:38 +00:00
|
|
|
store.Lock()
|
2019-04-28 13:26:22 +00:00
|
|
|
if len(store.Uids) > 0 {
|
|
|
|
for ml.selected >= len(store.Uids) {
|
2019-04-05 19:03:18 +00:00
|
|
|
ml.Prev()
|
|
|
|
}
|
2019-03-21 03:23:38 +00:00
|
|
|
}
|
2019-04-28 13:26:38 +00:00
|
|
|
store.Unlock()
|
2019-04-28 13:26:22 +00:00
|
|
|
|
2019-03-21 03:23:38 +00:00
|
|
|
ml.Invalidate()
|
|
|
|
}
|
|
|
|
|
2019-03-16 01:36:06 +00:00
|
|
|
func (ml *MessageList) SetStore(store *lib.MessageStore) {
|
2019-04-28 13:26:22 +00:00
|
|
|
if ml.Store() == store {
|
2019-03-16 01:49:40 +00:00
|
|
|
ml.scroll = 0
|
|
|
|
ml.selected = 0
|
|
|
|
}
|
2019-04-28 13:26:22 +00:00
|
|
|
ml.store.Store(store)
|
2019-03-15 01:37:00 +00:00
|
|
|
if store != nil {
|
|
|
|
ml.spinner.Stop()
|
2019-04-28 13:26:22 +00:00
|
|
|
store.OnUpdate(ml.storeUpdate)
|
2019-03-15 01:37:00 +00:00
|
|
|
} else {
|
|
|
|
ml.spinner.Start()
|
|
|
|
}
|
|
|
|
ml.Invalidate()
|
|
|
|
}
|
2019-03-15 03:41:25 +00:00
|
|
|
|
2019-03-21 03:23:38 +00:00
|
|
|
func (ml *MessageList) Store() *lib.MessageStore {
|
2019-04-28 13:26:22 +00:00
|
|
|
return ml.store.Load().(*lib.MessageStore)
|
2019-03-21 03:23:38 +00:00
|
|
|
}
|
|
|
|
|
2019-04-09 03:14:14 +00:00
|
|
|
func (ml *MessageList) Empty() bool {
|
2019-04-28 13:26:22 +00:00
|
|
|
store := ml.Store()
|
2019-04-28 13:26:38 +00:00
|
|
|
store.Lock()
|
|
|
|
defer store.Unlock()
|
|
|
|
|
2019-04-28 13:26:22 +00:00
|
|
|
return store == nil || len(store.Uids) == 0
|
2019-04-09 03:14:14 +00:00
|
|
|
}
|
|
|
|
|
2019-03-21 03:23:38 +00:00
|
|
|
func (ml *MessageList) Selected() *types.MessageInfo {
|
2019-04-28 13:26:22 +00:00
|
|
|
store := ml.Store()
|
2019-04-28 13:26:38 +00:00
|
|
|
store.Lock()
|
|
|
|
defer store.Unlock()
|
|
|
|
|
2019-04-28 13:26:22 +00:00
|
|
|
return store.Messages[store.Uids[len(store.Uids)-ml.selected-1]]
|
2019-03-21 03:23:38 +00:00
|
|
|
}
|
|
|
|
|
2019-03-16 02:01:20 +00:00
|
|
|
func (ml *MessageList) Select(index int) {
|
2019-04-28 13:26:22 +00:00
|
|
|
store := ml.Store()
|
2019-04-28 13:26:38 +00:00
|
|
|
store.Lock()
|
|
|
|
defer store.Unlock()
|
|
|
|
|
2019-03-16 02:01:20 +00:00
|
|
|
ml.selected = index
|
2019-04-28 13:26:22 +00:00
|
|
|
for ; ml.selected < 0; ml.selected = len(store.Uids) + ml.selected {
|
2019-03-16 02:01:20 +00:00
|
|
|
}
|
2019-04-28 13:26:22 +00:00
|
|
|
if ml.selected > len(store.Uids) {
|
|
|
|
ml.selected = len(store.Uids)
|
2019-03-16 02:01:20 +00:00
|
|
|
}
|
2019-03-17 21:51:14 +00:00
|
|
|
// I'm too lazy to do the math right now
|
|
|
|
for ml.selected-ml.scroll >= ml.Height() {
|
|
|
|
ml.scroll += 1
|
|
|
|
}
|
|
|
|
for ml.selected-ml.scroll < 0 {
|
|
|
|
ml.scroll -= 1
|
|
|
|
}
|
2019-03-16 02:01:20 +00:00
|
|
|
}
|
|
|
|
|
2019-03-15 03:41:25 +00:00
|
|
|
func (ml *MessageList) nextPrev(delta int) {
|
2019-04-28 13:26:22 +00:00
|
|
|
store := ml.Store()
|
2019-04-28 13:26:38 +00:00
|
|
|
store.Lock()
|
|
|
|
defer store.Unlock()
|
|
|
|
|
2019-04-28 13:26:22 +00:00
|
|
|
if store == nil || len(store.Uids) == 0 {
|
2019-03-22 01:00:03 +00:00
|
|
|
return
|
|
|
|
}
|
2019-03-15 03:41:25 +00:00
|
|
|
ml.selected += delta
|
|
|
|
if ml.selected < 0 {
|
2019-03-15 05:46:14 +00:00
|
|
|
ml.selected = 0
|
2019-03-15 03:41:25 +00:00
|
|
|
}
|
2019-04-28 13:26:22 +00:00
|
|
|
if ml.selected >= len(store.Uids) {
|
|
|
|
ml.selected = len(store.Uids) - 1
|
2019-03-15 03:41:25 +00:00
|
|
|
}
|
2019-03-16 01:49:40 +00:00
|
|
|
if ml.Height() != 0 {
|
|
|
|
if ml.selected-ml.scroll >= ml.Height() {
|
|
|
|
ml.scroll += 1
|
|
|
|
} else if ml.selected-ml.scroll < 0 {
|
|
|
|
ml.scroll -= 1
|
|
|
|
}
|
|
|
|
}
|
2019-03-15 03:41:25 +00:00
|
|
|
ml.Invalidate()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (ml *MessageList) Next() {
|
|
|
|
ml.nextPrev(1)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (ml *MessageList) Prev() {
|
|
|
|
ml.nextPrev(-1)
|
|
|
|
}
|