func switchToMode(amua *Amua, g *gocui.Gui, mode Mode) error { /* highlight off */ if amua.mode.IsHighlighted() { v := modeToView(g, amua.mode) v.Highlight = false } amua.prevMode = amua.mode amua.mode = mode curview := modeToViewStr(amua.mode) var err error switch amua.mode { case MessageMode: m := amua.curMessage() m.Flags |= Seen err = m.Draw(amua, g) case MessageMimeMode: m := amua.curMessage() err = (*MessageAsMimeTree)(m).Draw(amua, g) case MaildirMode: v, _ := g.View(curview) err = amua.curMaildirView.Draw(v) case SendMailMode: v, _ := g.View(curview) err = amua.sendMailDraw(v) case CommandNewMailMode: tos := util.ConcatAddresses(amua.newMail.to) amua.newMail.to = []*mail.Address{} displayPromptWithPrefill(TO_PROMPT, tos) case CommandMailModeTo: displayPromptWithPrefill(TO_PROMPT, util.ConcatAddresses(amua.newMail.to)) case CommandMailModeCc: displayPromptWithPrefill(CC_PROMPT, util.ConcatAddresses(amua.newMail.cc)) case CommandMailModeBcc: displayPromptWithPrefill(BCC_PROMPT, util.ConcatAddresses(amua.newMail.bcc)) case CommandSearchMode: displayPrompt(SEARCH_PROMPT) } if err != nil { v, _ := g.View(curview) if v != nil { fmt.Fprintf(v, err.Error()) } /* we printed the error, fallback */ } _, err = g.SetViewOnTop(curview) if err != nil { return err } err = g.SetCurrentView(curview) if err != nil { return err } /* highlight back */ if amua.mode.IsHighlighted() { v := modeToView(g, amua.mode) v.Highlight = true } return nil }
func (m *MessageAsText) Draw(amua *Amua, g *gocui.Gui) error { v, err := g.View(MESSAGE_VIEW) if err != nil { return err } v.Clear() v.Wrap = true v.SetOrigin(0, 0) _, err = io.Copy(v, m) if err != nil { return err } return nil }
func (m *Message) Draw(amua *Amua, g *gocui.Gui) error { v, err := g.View(MESSAGE_VIEW) if err != nil { return err } v.Clear() v.Wrap = true v.SetOrigin(0, 0) colorstring.Fprintf(v, "[green]Subject: %s\n", m.Subject) colorstring.Fprintf(v, "[red]From: %s\n", m.From) colorstring.Fprintf(v, "[red]To: %s\n", m.To) colorstring.Fprintf(v, "[green]Date: %s\n", m.Date.Format("Mon, 2 Jan 2006 15:04:05 -0700")) fmt.Fprintf(v, "\n") _, err = io.Copy(v, m) if err != nil { return err } return nil }
func drawSlider(amua *Amua, g *gocui.Gui) { v, err := g.View(SLIDER_VIEW) if err != nil { return } v.Clear() _, h := v.Size() sliderH := 1 whites := h - 1 if len(amua.curMaildirView.md.messages) > 0 { sliderH = h * h / len(amua.curMaildirView.md.messages) whites = amua.curMaildirView.curTop * h / len(amua.curMaildirView.md.messages) } if sliderH <= 0 { sliderH = 1 } for i := 0; i < whites; i++ { fmt.Fprintln(v, " ") } for i := 0; i < sliderH; i++ { fmt.Fprintln(v, "\u2588") } }
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 }
func modeToView(g *gocui.Gui, mode Mode) *gocui.View { v, _ := g.View(modeToViewStr(mode)) return v }