func keybindings(amua *Amua, g *gocui.Gui) error { switchToModeInt := func(mode Mode) func(g *gocui.Gui, v *gocui.View) error { return func(g *gocui.Gui, v *gocui.View) error { return switchToMode(amua, g, mode) } } maildirMove := func(dy int) func(g *gocui.Gui, v *gocui.View) error { return func(g *gocui.Gui, v *gocui.View) error { amua.curMaildirView.scroll(v, dy) drawSlider(amua, g) return nil } } maildirAllDown := func() func(g *gocui.Gui, v *gocui.View) error { return func(g *gocui.Gui, v *gocui.View) error { dy := len(amua.curMaildirView.md.messages) - amua.curMaildirView.cur - 1 amua.curMaildirView.scroll(v, dy) drawSlider(amua, g) return nil } } messageModeToggle := func(g *gocui.Gui, v *gocui.View) error { switch amua.mode { case MessageMode: switchToMode(amua, g, MessageMimeMode) case MessageMimeMode: switchToMode(amua, g, MessageMode) } return nil } search := func(forward bool) func(g *gocui.Gui, v *gocui.View) error { return func(g *gocui.Gui, v *gocui.View) error { setStatus("Looking for: " + amua.searchPattern + " in " + amua.curMaildirView.md.path) found := false direction := 1 if forward == false { direction = -1 } for i := 0; i < len(amua.curMaildirView.md.messages); i++ { idx := ((direction * i) + amua.curMaildirView.cur + direction) % len(amua.curMaildirView.md.messages) if idx < 0 { idx = len(amua.curMaildirView.md.messages) + idx } if idx < 0 { panic(idx) } m := amua.getMessage(idx) if strings.Contains(m.Subject, amua.searchPattern) { setStatus("Found: " + amua.searchPattern + " in " + amua.curMaildirView.md.path) found = true amua.curMaildirView.curTop = idx amua.curMaildirView.cur = idx break } } if found { mv, err := g.View(MAILDIR_VIEW) if err != nil { return err } err = amua.curMaildirView.Draw(mv) if err != nil { setStatus(err.Error()) } } else { setStatus(amua.searchPattern + " not found") } return nil } } enterSearch := func(forward bool) func(g *gocui.Gui, v *gocui.View) error { return func(g *gocui.Gui, v *gocui.View) error { v.Rewind() spbuf, err := ioutil.ReadAll(v) if err != nil { return err } prompt := amua.prompt amua.searchPattern = strings.TrimSpace(string(spbuf[len(prompt):])) switchToMode(amua, g, MaildirMode) search(forward)(g, v) return nil } } cancelSearch := func(g *gocui.Gui, v *gocui.View) error { setStatus("") return switchToMode(amua, g, amua.prevMode) } setFlag := func(flag MessageFlags) func(g *gocui.Gui, v *gocui.View) error { return func(g *gocui.Gui, v *gocui.View) error { m := amua.curMessage() m.Flags |= flag amua.curMaildirView.Draw(v) return nil } } unsetFlag := func(flag MessageFlags) func(g *gocui.Gui, v *gocui.View) error { return func(g *gocui.Gui, v *gocui.View) error { m := amua.curMessage() m.Flags &= ^flag amua.curMaildirView.Draw(v) return nil } } toggleFlag := func(flag MessageFlags) func(g *gocui.Gui, v *gocui.View) error { return func(g *gocui.Gui, v *gocui.View) error { m := amua.curMessage() if (m.Flags & flag) != 0 { m.Flags &= ^flag } else { m.Flags |= flag } amua.curMaildirView.Draw(v) return nil } } quit := func(g *gocui.Gui, v *gocui.View) error { amua.applyCurMaildirChanges() return gocui.ErrQuit } deleteMessage := setFlag(Trashed) undeleteMessage := unsetFlag(Trashed) unreadMessage := unsetFlag(Seen) readMessage := setFlag(Seen) toggleFlagged := toggleFlag(Flagged) syncMaildir := func(g *gocui.Gui, v *gocui.View) error { amua.applyCurMaildirChanges() amua.RefreshMaildir(g, v) v, _ = g.View(SIDE_VIEW) drawKnownMaildirs(amua, g, v) return nil } commandEnter := func(g *gocui.Gui, v *gocui.View) error { switch amua.mode { case CommandSearchMode: return enterSearch(true)(g, v) case CommandMailModeTo: var err error amua.newMail.to, err = mail.ParseAddressList(getPromptInput()) if err != nil { displayError(err.Error()) return nil } setStatus("") switchToMode(amua, g, SendMailMode) case CommandMailModeCc: var err error amua.newMail.cc, err = mail.ParseAddressList(getPromptInput()) if err != nil { //flash error return nil } setStatus("") switchToMode(amua, g, SendMailMode) case CommandMailModeBcc: var err error amua.newMail.bcc, err = mail.ParseAddressList(getPromptInput()) if err != nil { //flash error return nil } setStatus("") switchToMode(amua, g, SendMailMode) case CommandNewMailMode: if len(amua.newMail.to) == 0 { var err error amua.newMail.to, err = mail.ParseAddressList(getPromptInput()) if err != nil { displayError(err.Error()) return nil } displayPromptWithPrefill(SUBJECT_PROMPT, amua.newMail.subject) amua.newMail.subject = "" } else if amua.newMail.subject == "" { amua.newMail.subject = getPromptInput() /* Exec $EDITOR */ tf, err := ioutil.TempFile("", "amuamail") if err != nil { log.Fatal(err) } defer os.Remove(tf.Name()) tf.Close() err = ioutil.WriteFile(tf.Name(), amua.newMail.body, 0600) if err != nil { log.Fatal(err) } cmd := exec.Command(amua.ExtEditor(), tf.Name()) cmd.Stdin = os.Stdin cmd.Stdout = os.Stdout if err := cmd.Run(); err != nil { log.Fatal(err) } amua.newMail.body, err = ioutil.ReadFile(tf.Name()) if err != nil { log.Fatal(err.Error()) } setStatus("") switchToMode(amua, g, SendMailMode) err = g.Sync() if err != nil { log.Fatal(err) } } } return nil } sendMail := func(g *gocui.Gui, v *gocui.View) error { err := send(&amua.newMail, cfg) if err != nil { setStatus(err.Error()) return nil } setStatus("Sent to " + cfg.SMTPConfig.Host) switchToMode(amua, g, MaildirMode) amua.newMail = NewMail{} return nil } reply := func(group bool) func(g *gocui.Gui, v *gocui.View) error { return func(g *gocui.Gui, v *gocui.View) error { m := amua.curMessage() amua.newMail.to = buildTo(m) if group { amua.newMail.cc = buildCCs(m) } amua.newMail.subject = "Re: " + m.Subject buf, err := ioutil.ReadAll((*MessageAsText)(m)) if err != nil { return err } buf = bytes.Replace(buf, []byte("\n"), []byte("\n> "), -1) replyHeader := fmt.Sprintf("On %s, %s wrote:\n> ", m.Date.Format("Mon Jan 2 15:04:05 -0700 MST 2006"), m.From) amua.newMail.body = append([]byte(replyHeader), buf...) switchToMode(amua, g, CommandNewMailMode) return nil } } replyMessage := reply(false) groupReplyMessage := reply(true) pipeMessage := func(g *gocui.Gui, v *gocui.View) error { m := amua.curMessage() cmd := exec.Command(amua.ExtEditor(), m.path) cmd.Stdin = os.Stdin cmd.Stdout = os.Stdout if err := cmd.Run(); err != nil { log.Fatal(err) } setStatus("") switchToMode(amua, g, MaildirMode) err := g.Sync() if err != nil { log.Fatal(err) } /* exec a command, pipe the message there */ return nil } type keybinding struct { key interface{} fn gocui.KeybindingHandler mod bool } bindings := map[string][]keybinding{ MAILDIR_VIEW: { {gocui.KeyEnter, switchToModeInt(MessageMode), false}, {'c', switchToModeInt(KnownMaildirsMode), false}, {'v', switchToModeInt(MessageMimeMode), false}, {'q', quit, false}, {'d', deleteMessage, false}, {'u', undeleteMessage, false}, {'G', maildirAllDown(), false}, {'k', maildirMove(-1), false}, {gocui.KeyArrowUp, maildirMove(-1), false}, {'j', maildirMove(1), false}, {'n', search(true), false}, {'N', search(false), false}, {'$', syncMaildir, false}, {'F', toggleFlagged, false}, {gocui.KeyCtrlR, readMessage, false}, {gocui.KeyCtrlN, unreadMessage, false}, {gocui.KeyArrowDown, maildirMove(1), false}, {gocui.KeyCtrlF, maildirMove(10), false}, {gocui.KeyPgdn, maildirMove(10), false}, {gocui.KeyCtrlB, maildirMove(-10), false}, {gocui.KeyPgup, maildirMove(-10), false}, {'/', switchToModeInt(CommandSearchMode), false}, {'m', switchToModeInt(CommandNewMailMode), false}, {'r', replyMessage, false}, {'g', groupReplyMessage, false}, {'|', pipeMessage, false}, }, MESSAGE_VIEW: { {'q', switchToModeInt(MaildirMode), false}, {'v', messageModeToggle, false}, {gocui.KeyPgup, scrollMessageView(-10), false}, {gocui.KeyPgdn, scrollMessageView(10), false}, {gocui.KeySpace, scrollMessageView(10), false}, {'j', scrollMessageView(1), false}, {'k', scrollMessageView(-1), false}, {'r', replyMessage, false}, {'g', groupReplyMessage, false}, {'|', pipeMessage, false}, }, SEND_MAIL_VIEW: { {'q', switchToModeInt(MaildirMode), false}, {'t', switchToModeInt(CommandMailModeTo), false}, {'c', switchToModeInt(CommandMailModeCc), false}, {'b', switchToModeInt(CommandMailModeBcc), false}, {'y', sendMail, false}, {gocui.KeyCtrlG, switchToModeInt(MaildirMode), false}, }, STATUS_VIEW: { {gocui.KeyEnter, commandEnter, false}, {gocui.KeyCtrlG, cancelSearch, false}, }, SIDE_VIEW: { {'j', scrollSideView(amua, 1), false}, {gocui.KeyArrowDown, scrollSideView(amua, 1), false}, {'k', scrollSideView(amua, -1), false}, {gocui.KeyArrowUp, scrollSideView(amua, 1), false}, {gocui.KeyEnter, selectNewMaildir(amua), false}, }, "": { {gocui.KeyCtrlC, quit, false}, }, } for vn, binds := range bindings { for _, b := range binds { err := g.SetKeybinding(vn, b.key, gocui.ModNone, b.fn) if err != nil { return err } } } return nil }