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 ( FieldTaskTodo = iota FieldTaskPriority FieldTaskProjects FieldTaskContexts FieldTaskTags FieldTaskError TaskBundleTaskIdKey = "taskscreen.taskid" ) type TaskScreen struct { message *Message cursor int currentTaskId int displayTask *todotxt.Task fieldLabels map[int]string editing bool inputField *termboxUtil.InputField } func (screen *TaskScreen) GetFieldValue(fld int) string { switch fld { case FieldTaskTodo: return screen.displayTask.Todo case FieldTaskPriority: return screen.displayTask.Priority case FieldTaskProjects: return strings.Join(screen.displayTask.Projects, ",") case FieldTaskContexts: return strings.Join(screen.displayTask.Contexts, ",") case FieldTaskTags: var ret []string for k, v := range screen.displayTask.AdditionalTags { ret = append(ret, k+":"+v) } return strings.Join(ret, ",") } return "" } func (screen *TaskScreen) Id() int { return ScreenIdTask } func (screen *TaskScreen) Initialize(bundle termboxScreen.Bundle) error { screen.fieldLabels = make(map[int]string) screen.fieldLabels[FieldTaskTodo] = "Todo" screen.fieldLabels[FieldTaskPriority] = "Priority" screen.fieldLabels[FieldTaskProjects] = "Projects" screen.fieldLabels[FieldTaskContexts] = "Contexts" screen.fieldLabels[FieldTaskTags] = "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.currentTaskId = bundle.GetInt(TaskBundleTaskIdKey, -1) } if screen.currentTaskId == -1 { return errors.New("Task Screen Initialization Failed") } if screen.displayTask, err = app.TaskList.GetTask(screen.currentTaskId); err != nil { return err } screen.cursor = FieldTaskTodo return nil } func (screen *TaskScreen) ResizeScreen() { screen.Initialize(nil) } func (screen *TaskScreen) 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 *TaskScreen) HandleNoneEvent(event termbox.Event) int { return screen.Id() } func (screen *TaskScreen) DrawScreen() { screen.drawHeader() yPos := 1 if screen.cursor == FieldTaskTodo { termboxUtil.DrawStringAtPoint(screen.displayTask.Todo, 0, yPos, CursorBg, CursorFg) } else { termboxUtil.DrawStringAtPoint(screen.displayTask.Todo, 0, yPos, DefaultFg, DefaultBg) } yPos++ if screen.cursor == FieldTaskPriority { termboxUtil.DrawStringAtPoint(fmt.Sprintf("Priority: %s", screen.displayTask.Priority), 0, yPos, CursorBg, CursorFg) } else { termboxUtil.DrawStringAtPoint(fmt.Sprintf("Priority: %s", screen.displayTask.Priority), 0, yPos, DefaultFg, DefaultBg) } yPos++ if screen.cursor == FieldTaskProjects { termboxUtil.DrawStringAtPoint(fmt.Sprintf("Projects: %s", screen.displayTask.Projects), 0, yPos, CursorBg, CursorFg) } else { termboxUtil.DrawStringAtPoint(fmt.Sprintf("Projects: %s", screen.displayTask.Projects), 0, yPos, DefaultFg, DefaultBg) } yPos++ if screen.cursor == FieldTaskContexts { termboxUtil.DrawStringAtPoint(fmt.Sprintf("Contexts: %s", screen.displayTask.Contexts), 0, yPos, CursorBg, CursorFg) } else { termboxUtil.DrawStringAtPoint(fmt.Sprintf("Contexts: %s", screen.displayTask.Contexts), 0, yPos, DefaultFg, DefaultBg) } yPos++ if screen.cursor == FieldTaskTags { termboxUtil.DrawStringAtPoint("Additional Tags: ", 0, yPos, CursorBg, CursorFg) } else { termboxUtil.DrawStringAtPoint("Additional Tags: ", 0, yPos, DefaultFg, DefaultBg) } yPos++ for k, v := range screen.displayTask.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 *TaskScreen) handleEditingKeyEvent(event termbox.Event) int { if event.Key == termbox.KeyEnter { var needsSave bool // Save the change switch screen.cursor { case FieldTaskTodo: //screen.displayTask.Todo = screen.inputField.GetValue() if screen.inputField.GetValue() != screen.displayTask.Todo { screen.displayTask.Todo = screen.inputField.GetValue() needsSave = true } case FieldTaskPriority: val := screen.inputField.GetValue() if len(val) > 0 { val = string(val[0]) } if val != screen.displayTask.Priority { screen.displayTask.Priority = val needsSave = true } case FieldTaskProjects: projects := splitFields(screen.inputField.GetValue()) if !isCombination(projects, screen.displayTask.Projects) { needsSave = true screen.displayTask.Projects = []string{} for _, v := range projects { screen.displayTask.Projects = append(screen.displayTask.Projects, v) } } case FieldTaskContexts: contexts := splitFields(screen.inputField.GetValue()) if !isCombination(contexts, screen.displayTask.Contexts) { needsSave = true screen.displayTask.Contexts = []string{} for _, v := range contexts { screen.displayTask.Contexts = append(screen.displayTask.Contexts, v) } } case FieldTaskTags: tagsSlice := splitFields(screen.inputField.GetValue()) if !sliceIsValidTags(tagsSlice) { screen.message.Set("Tags should be in format :") return screen.Id() } if !isCombination(tagsSlice, tagsToSlice(screen.displayTask.AdditionalTags)) { needsSave = true screen.displayTask.AdditionalTags = make(map[string]string) for k, v := range sliceToTags(tagsSlice) { screen.displayTask.AdditionalTags[k] = v } } } if needsSave { if err := app.saveTask(screen.displayTask); 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 *TaskScreen) drawHeader() { width, _ := termbox.Size() headerString := screen.displayTask.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 *TaskScreen) drawFooter() { _, height := termbox.Size() screen.message.DrawAt(0, height-1) } func (screen *TaskScreen) moveCursorDown() bool { screen.cursor = (screen.cursor + 1) % FieldTaskError return true } func (screen *TaskScreen) moveCursorUp() bool { if screen.cursor > 0 { screen.cursor-- } else { screen.cursor = FieldTaskError - 1 } return true } func (screen *TaskScreen) editCurrentField() { }