summary refs log tree commit diff stats
path: root/lib
diff options
context:
space:
mode:
authorReto Brunner <reto@labrat.space>2019-12-18 06:33:59 +0100
committerDrew DeVault <sir@cmpwn.com>2019-12-21 09:27:49 -0500
commitcf43066d5b8bbe991bef8280bbbeeb13059585b6 (patch)
treea8028541bd9f7d10a7df1d786942d9aa48e1b8fb /lib
parent3e535f49b314979615e7ce76573fa76f9ab71b00 (diff)
downloadaerc-cf43066d5b8bbe991bef8280bbbeeb13059585b6.tar.gz
msgstore: add marking functionality
Diffstat (limited to 'lib')
-rw-r--r--lib/msgstore.go131
1 files changed, 127 insertions, 4 deletions
diff --git a/lib/msgstore.go b/lib/msgstore.go
index 2a9ea86..9075a26 100644
--- a/lib/msgstore.go
+++ b/lib/msgstore.go
@@ -21,6 +21,11 @@ type MessageStore struct {
 	bodyCallbacks   map[uint32][]func(io.Reader)
 	headerCallbacks map[uint32][]func(*types.MessageInfo)
 
+	//marking
+	marked         map[uint32]struct{}
+	visualStartUid uint32
+	visualMarkMode bool
+
 	// Search/filter results
 	results     []uint32
 	resultIndex int
@@ -51,6 +56,7 @@ func NewMessageStore(worker *types.Worker,
 		Messages: make(map[uint32]*models.MessageInfo),
 
 		selected:        0,
+		marked:          make(map[uint32]struct{}),
 		bodyCallbacks:   make(map[uint32][]func(io.Reader)),
 		headerCallbacks: make(map[uint32][]func(*types.MessageInfo)),
 
@@ -223,6 +229,7 @@ func (store *MessageStore) Update(msg types.WorkerMessage) {
 			toDelete[uid] = nil
 			delete(store.Messages, uid)
 			delete(store.Deleted, uid)
+			delete(store.marked, uid)
 		}
 		uids := make([]uint32, len(store.uids)-len(msg.Uids))
 		j := 0
@@ -345,12 +352,114 @@ func (store *MessageStore) SelectedIndex() int {
 func (store *MessageStore) Select(index int) {
 	uids := store.Uids()
 	store.selected = index
-	for ; store.selected < 0; store.selected = len(uids) + store.selected {
-		/* This space deliberately left blank */
-	}
-	if store.selected > len(uids) {
+	if store.selected < 0 {
+		store.selected = len(uids) - 1
+	} else if store.selected > len(uids) {
 		store.selected = len(uids)
 	}
+	store.updateVisual()
+}
+
+// Mark sets the marked state on a MessageInfo
+func (store *MessageStore) Mark(uid uint32) {
+	if store.visualMarkMode {
+		// visual mode has override, bogus input from user
+		return
+	}
+	store.marked[uid] = struct{}{}
+}
+
+// Unmark removes the marked state on a MessageInfo
+func (store *MessageStore) Unmark(uid uint32) {
+	if store.visualMarkMode {
+		// user probably wanted to clear the visual marking
+		store.ClearVisualMark()
+		return
+	}
+	delete(store.marked, uid)
+}
+
+// ToggleMark toggles the marked state on a MessageInfo
+func (store *MessageStore) ToggleMark(uid uint32) {
+	if store.visualMarkMode {
+		// visual mode has override, bogus input from user
+		return
+	}
+	if store.IsMarked(uid) {
+		store.Unmark(uid)
+	} else {
+		store.Mark(uid)
+	}
+}
+
+// resetMark removes the marking from all messages
+func (store *MessageStore) resetMark() {
+	store.marked = make(map[uint32]struct{})
+}
+
+//IsMarked checks whether a MessageInfo has been marked
+func (store *MessageStore) IsMarked(uid uint32) bool {
+	_, marked := store.marked[uid]
+	return marked
+}
+
+//ToggleVisualMark enters or leaves the visual marking mode
+func (store *MessageStore) ToggleVisualMark() {
+	store.visualMarkMode = !store.visualMarkMode
+	switch store.visualMarkMode {
+	case true:
+		// just entered visual mode, reset whatever marking was already done
+		store.resetMark()
+		store.visualStartUid = store.Selected().Uid
+		store.marked[store.visualStartUid] = struct{}{}
+	case false:
+		// visual mode ended, nothing to do
+		return
+	}
+}
+
+//ClearVisualMark leaves the visual marking mode and resets any marking
+func (store *MessageStore) ClearVisualMark() {
+	store.resetMark()
+	store.visualMarkMode = false
+	store.visualStartUid = 0
+}
+
+// Marked returns the uids of all marked messages
+func (store *MessageStore) Marked() []uint32 {
+	marked := make([]uint32, len(store.marked))
+	i := 0
+	for uid := range store.marked {
+		marked[i] = uid
+		i++
+	}
+	return marked
+}
+
+func (store *MessageStore) updateVisual() {
+	if !store.visualMarkMode {
+		// nothing to do
+		return
+	}
+	startIdx := store.visualStartIdx()
+	if startIdx < 0 {
+		// something deleted the startuid, abort the marking process
+		store.ClearVisualMark()
+		return
+	}
+	uidLen := len(store.Uids())
+	// store.selected is the inverted form of the actual array
+	selectedIdx := uidLen - store.selected - 1
+	var visUids []uint32
+	if selectedIdx > startIdx {
+		visUids = store.Uids()[startIdx : selectedIdx+1]
+	} else {
+		visUids = store.Uids()[selectedIdx : startIdx+1]
+	}
+	store.resetMark()
+	for _, uid := range visUids {
+		store.marked[uid] = struct{}{}
+	}
 }
 
 func (store *MessageStore) NextPrev(delta int) {
@@ -365,6 +474,7 @@ func (store *MessageStore) NextPrev(delta int) {
 	if store.selected >= len(uids) {
 		store.selected = len(uids) - 1
 	}
+	store.updateVisual()
 	nextResultIndex := len(store.results) - store.resultIndex - 2*delta
 	if nextResultIndex < 0 || nextResultIndex >= len(store.results) {
 		return
@@ -406,6 +516,9 @@ func (store *MessageStore) ApplyFilter(results []uint32) {
 	store.results = results
 	store.filter = true
 	store.update()
+	// any marking is now invalid
+	// TODO: could save that probably
+	store.ClearVisualMark()
 }
 
 func (store *MessageStore) ApplyClear() {
@@ -457,3 +570,13 @@ func (store *MessageStore) Sort(criteria []*types.SortCriterion, cb func()) {
 		cb()
 	})
 }
+
+// returns the index of needle in haystack or -1 if not found
+func (store *MessageStore) visualStartIdx() int {
+	for idx, u := range store.Uids() {
+		if u == store.visualStartUid {
+			return idx
+		}
+	}
+	return -1
+}