package main import ( "errors" "fmt" "strings" "time" todotxt "git.bullercodeworks.com/brian/go-todotxt" termboxScreen "git.bullercodeworks.com/brian/termbox-screen" termboxUtil "git.bullercodeworks.com/brian/termbox-util" termbox "github.com/nsf/termbox-go" ) const ( FieldTodoTodo = iota FieldTodoPriority FieldTodoProjects FieldTodoContexts FieldTodoTags FieldTodoError TodoBundleTodoIdKey = "todoscreen.todoid" ) type TodoScreen struct { message *Message cursor int currentTodoId int displayTodo *todotxt.Todo fieldLabels map[int]string editing bool inputField *termboxUtil.InputField } func (screen *TodoScreen) GetFieldValue(fld int) string { switch fld { case FieldTodoTodo: return screen.displayTodo.Todo case FieldTodoPriority: return screen.displayTodo.Priority case FieldTodoProjects: return strings.Join(screen.displayTodo.Projects, ",") case FieldTodoContexts: return strings.Join(screen.displayTodo.Contexts, ",") case FieldTodoTags: var ret []string for k, v := range screen.displayTodo.AdditionalTags { ret = append(ret, k+":"+v) } return strings.Join(ret, ",") } return "" } func (screen *TodoScreen) Id() int { return ScreenIdTodo } func (screen *TodoScreen) Initialize(bundle termboxScreen.Bundle) error { screen.fieldLabels = make(map[int]string) screen.fieldLabels[FieldTodoTodo] = "Todo" screen.fieldLabels[FieldTodoPriority] = "Priority" screen.fieldLabels[FieldTodoProjects] = "Projects" screen.fieldLabels[FieldTodoContexts] = "Contexts" screen.fieldLabels[FieldTodoTags] = "Tags" var err error width, height := termbox.Size() screen.inputField = termboxUtil.CreateInputField(2, (height - 3), width, 1, DefaultFg, DefaultBg) screen.message = NewMessage("", DefaultFg, DefaultBg, time.Second*2) if bundle != nil { screen.currentTodoId = bundle.GetInt(TodoBundleTodoIdKey, -1) } if screen.currentTodoId == -1 { return errors.New("Todo Screen Initialization Failed") } if screen.displayTodo, err = app.TodoList.GetTodo(screen.currentTodoId); err != nil { return err } screen.cursor = FieldTodoTodo return nil } func (screen *TodoScreen) ResizeScreen() { screen.Initialize(nil) } func (screen *TodoScreen) HandleKeyEvent(event termbox.Event) int { if screen.editing { return screen.handleEditingKeyEvent(event) } if event.Key == termbox.KeyEnter { screen.editing = true //screen.inputField.SetTitle(screen.fieldLabels[screen.cursor] + ": ") screen.inputField.SetValue(screen.GetFieldValue(screen.cursor)) //screen.inputField.SetActive(true) return screen.Id() } else if event.Key == termbox.KeyBackspace || event.Key == termbox.KeyBackspace2 || event.Ch == 'h' || event.Key == termbox.KeyArrowLeft { bundle := termboxScreen.Bundle{} bundle.SetValue(MainBundleListKey, MainBundleListCurrent) app.uiManager.InitializeScreen(ScreenIdMain, bundle) return ScreenIdMain } else if event.Ch == 'j' || event.Key == termbox.KeyArrowDown { screen.moveCursorDown() } else if event.Ch == 'k' || event.Key == termbox.KeyArrowUp { screen.moveCursorUp() } return screen.Id() } func (screen *TodoScreen) HandleNoneEvent(event termbox.Event) int { return screen.Id() } func (screen *TodoScreen) DrawScreen() { screen.drawHeader() yPos := 1 if screen.cursor == FieldTodoTodo { termboxUtil.DrawStringAtPoint(screen.displayTodo.Todo, 0, yPos, CursorBg, CursorFg) } else { termboxUtil.DrawStringAtPoint(screen.displayTodo.Todo, 0, yPos, DefaultFg, DefaultBg) } yPos++ if screen.cursor == FieldTodoPriority { termboxUtil.DrawStringAtPoint(fmt.Sprintf("Priority: %s", screen.displayTodo.Priority), 0, yPos, CursorBg, CursorFg) } else { termboxUtil.DrawStringAtPoint(fmt.Sprintf("Priority: %s", screen.displayTodo.Priority), 0, yPos, DefaultFg, DefaultBg) } yPos++ if screen.cursor == FieldTodoProjects { termboxUtil.DrawStringAtPoint(fmt.Sprintf("Projects: %s", screen.displayTodo.Projects), 0, yPos, CursorBg, CursorFg) } else { termboxUtil.DrawStringAtPoint(fmt.Sprintf("Projects: %s", screen.displayTodo.Projects), 0, yPos, DefaultFg, DefaultBg) } yPos++ if screen.cursor == FieldTodoContexts { termboxUtil.DrawStringAtPoint(fmt.Sprintf("Contexts: %s", screen.displayTodo.Contexts), 0, yPos, CursorBg, CursorFg) } else { termboxUtil.DrawStringAtPoint(fmt.Sprintf("Contexts: %s", screen.displayTodo.Contexts), 0, yPos, DefaultFg, DefaultBg) } yPos++ if screen.cursor == FieldTodoTags { termboxUtil.DrawStringAtPoint("Additional Tags: ", 0, yPos, CursorBg, CursorFg) } else { termboxUtil.DrawStringAtPoint("Additional Tags: ", 0, yPos, DefaultFg, DefaultBg) } yPos++ for k, v := range screen.displayTodo.AdditionalTags { termboxUtil.DrawStringAtPoint(fmt.Sprintf("%s: %s", k, v), 0, yPos, DefaultFg, DefaultBg) yPos++ } if screen.editing { w, h := termbox.Size() termboxUtil.DrawBorder(0, h-4, w-1, h-2, CursorBg, CursorFg) termboxUtil.FillWithChar(' ', 1, h-3, w-2, h-3, DefaultFg, DefaultBg) screen.inputField.Draw() } screen.drawFooter() } func (screen *TodoScreen) handleEditingKeyEvent(event termbox.Event) int { if event.Key == termbox.KeyEnter { var needsSave bool // Save the change switch screen.cursor { case FieldTodoTodo: //screen.displayTodo.Todo = screen.inputField.GetValue() if screen.inputField.GetValue() != screen.displayTodo.Todo { screen.displayTodo.Todo = screen.inputField.GetValue() needsSave = true } case FieldTodoPriority: val := screen.inputField.GetValue() if len(val) > 0 { val = string(val[0]) } if val != screen.displayTodo.Priority { screen.displayTodo.Priority = val needsSave = true } case FieldTodoProjects: projects := splitFields(screen.inputField.GetValue()) if !isCombination(projects, screen.displayTodo.Projects) { needsSave = true screen.displayTodo.Projects = []string{} for _, v := range projects { screen.displayTodo.Projects = append(screen.displayTodo.Projects, v) } } case FieldTodoContexts: contexts := splitFields(screen.inputField.GetValue()) if !isCombination(contexts, screen.displayTodo.Contexts) { needsSave = true screen.displayTodo.Contexts = []string{} for _, v := range contexts { screen.displayTodo.Contexts = append(screen.displayTodo.Contexts, v) } } case FieldTodoTags: tagsSlice := splitFields(screen.inputField.GetValue()) if !sliceIsValidTags(tagsSlice) { screen.message.Set("Tags should be in format :") return screen.Id() } if !isCombination(tagsSlice, tagsToSlice(screen.displayTodo.AdditionalTags)) { needsSave = true screen.displayTodo.AdditionalTags = make(map[string]string) for k, v := range sliceToTags(tagsSlice) { screen.displayTodo.AdditionalTags[k] = v } } } if needsSave { if err := app.saveTodo(screen.displayTodo); err != nil { screen.message.SetError(err.Error()) } } screen.editing = false //screen.inputField.SetActive(false) } else { if screen.inputField.HandleEvent(event) { } } return screen.Id() } func (screen *TodoScreen) drawHeader() { width, _ := termbox.Size() headerString := screen.displayTodo.Todo spaces := strings.Repeat(" ", ((width-len(headerString))/2)+1) termboxUtil.DrawStringAtPoint(fmt.Sprintf("%s%s%s", spaces, headerString, spaces), 0, 0, TitleFg, TitleBg) } func (screen *TodoScreen) drawFooter() { _, height := termbox.Size() screen.message.DrawAt(0, height-1) } func (screen *TodoScreen) moveCursorDown() bool { screen.cursor = (screen.cursor + 1) % FieldTodoError return true } func (screen *TodoScreen) moveCursorUp() bool { if screen.cursor > 0 { screen.cursor-- } else { screen.cursor = FieldTodoError - 1 } return true } func (screen *TodoScreen) editCurrentField() { }