aboutsummaryrefslogtreecommitdiffstats
path: root/lib/marker/marker.go
diff options
context:
space:
mode:
Diffstat (limited to 'lib/marker/marker.go')
-rw-r--r--lib/marker/marker.go179
1 files changed, 179 insertions, 0 deletions
diff --git a/lib/marker/marker.go b/lib/marker/marker.go
new file mode 100644
index 00000000..1b89fc80
--- /dev/null
+++ b/lib/marker/marker.go
@@ -0,0 +1,179 @@
+package marker
+
+// TODO: fix headers for message that are nil
+
+// Marker provides the interface for the marking behavior of messages
+type Marker interface {
+ Mark(uint32)
+ Unmark(uint32)
+ ToggleMark(uint32)
+ Remark()
+ Marked() []uint32
+ IsMarked(uint32) bool
+ ToggleVisualMark()
+ UpdateVisualMark()
+ ClearVisualMark()
+}
+
+// UIDProvider provides the underlying uids and the selected message index
+type UIDProvider interface {
+ Uids() []uint32
+ SelectedIndex() int
+}
+
+type controller struct {
+ uidProvider UIDProvider
+ marked map[uint32]struct{}
+ lastMarked map[uint32]struct{}
+ visualStartUID uint32
+ visualMarkMode bool
+}
+
+// New returns a new Marker
+func New(up UIDProvider) Marker {
+ return &controller{
+ uidProvider: up,
+ marked: make(map[uint32]struct{}),
+ lastMarked: make(map[uint32]struct{}),
+ }
+}
+
+// Mark markes the uid as marked
+func (mc *controller) Mark(uid uint32) {
+ if mc.visualMarkMode {
+ // visual mode has override, bogus input from user
+ return
+ }
+ mc.marked[uid] = struct{}{}
+}
+
+// Unmark unmarks the uid
+func (mc *controller) Unmark(uid uint32) {
+ if mc.visualMarkMode {
+ // user probably wanted to clear the visual marking
+ mc.ClearVisualMark()
+ return
+ }
+ delete(mc.marked, uid)
+}
+
+// Remark restores the previous marks
+func (mc *controller) Remark() {
+ mc.marked = mc.lastMarked
+}
+
+// ToggleMark toggles the marked state for the given uid
+func (mc *controller) ToggleMark(uid uint32) {
+ if mc.visualMarkMode {
+ // visual mode has override, bogus input from user
+ return
+ }
+ if mc.IsMarked(uid) {
+ mc.Unmark(uid)
+ } else {
+ mc.Mark(uid)
+ }
+}
+
+// resetMark removes the marking from all messages
+func (mc *controller) resetMark() {
+ mc.lastMarked = mc.marked
+ mc.marked = make(map[uint32]struct{})
+}
+
+// removeStaleUID removes uids that are no longer presents in the UIDProvider
+func (mc *controller) removeStaleUID() {
+ for mark := range mc.marked {
+ present := false
+ for _, uid := range mc.uidProvider.Uids() {
+ if mark == uid {
+ present = true
+ break
+ }
+ }
+ if !present {
+ delete(mc.marked, mark)
+ }
+ }
+}
+
+// IsMarked checks whether the given uid has been marked
+func (mc *controller) IsMarked(uid uint32) bool {
+ _, marked := mc.marked[uid]
+ return marked
+}
+
+// Marked returns the uids of all marked messages
+func (mc *controller) Marked() []uint32 {
+ mc.removeStaleUID()
+ marked := make([]uint32, len(mc.marked))
+ i := 0
+ for uid := range mc.marked {
+ marked[i] = uid
+ i++
+ }
+ return marked
+}
+
+// ToggleVisualMark enters or leaves the visual marking mode
+func (mc *controller) ToggleVisualMark() {
+ mc.visualMarkMode = !mc.visualMarkMode
+ if mc.visualMarkMode {
+ // just entered visual mode, reset whatever marking was already done
+ mc.resetMark()
+ uids := mc.uidProvider.Uids()
+ if idx := mc.uidProvider.SelectedIndex(); idx >= 0 && idx < len(uids) {
+ mc.visualStartUID = uids[idx]
+ mc.marked[mc.visualStartUID] = struct{}{}
+ }
+ }
+}
+
+// ClearVisualMark leaves the visual marking mode and resets any marking
+func (mc *controller) ClearVisualMark() {
+ mc.resetMark()
+ mc.visualMarkMode = false
+ mc.visualStartUID = 0
+}
+
+// UpdateVisualMark updates the index with the currently selected message
+func (mc *controller) UpdateVisualMark() {
+ if !mc.visualMarkMode {
+ // nothing to do
+ return
+ }
+ startIdx := mc.visualStartIdx()
+ if startIdx < 0 {
+ // something deleted the startuid, abort the marking process
+ mc.ClearVisualMark()
+ return
+ }
+
+ selectedIdx := mc.uidProvider.SelectedIndex()
+ if selectedIdx < 0 {
+ return
+ }
+
+ uids := mc.uidProvider.Uids()
+
+ var visUids []uint32
+ if selectedIdx > startIdx {
+ visUids = uids[startIdx : selectedIdx+1]
+ } else {
+ visUids = uids[selectedIdx : startIdx+1]
+ }
+ mc.resetMark()
+ for _, uid := range visUids {
+ mc.marked[uid] = struct{}{}
+ }
+}
+
+// returns the index of needle in haystack or -1 if not found
+func (mc *controller) visualStartIdx() int {
+ for idx, u := range mc.uidProvider.Uids() {
+ if u == mc.visualStartUID {
+ return idx
+ }
+ }
+ return -1
+}