v1
This commit is contained in:
parent
017ce196b4
commit
9bc8de8700
207
app_state.go
Normal file
207
app_state.go
Normal file
@ -0,0 +1,207 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
todotxt "github.com/JamesClonk/go-todotxt"
|
||||||
|
"github.com/br0xen/user-config"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AppState struct {
|
||||||
|
Name string
|
||||||
|
Version int
|
||||||
|
config *userConfig.Config
|
||||||
|
directory string
|
||||||
|
fileTodo string
|
||||||
|
fileDone string
|
||||||
|
fileReport string
|
||||||
|
ValidOperations map[string][]string
|
||||||
|
OpFuncs map[string]func([]string) int
|
||||||
|
|
||||||
|
TaskList *todotxt.TaskList
|
||||||
|
DoneList *todotxt.TaskList
|
||||||
|
|
||||||
|
screens []Screen
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewApp() *AppState {
|
||||||
|
app := &AppState{Name: AppName, Version: AppVersion}
|
||||||
|
app.initialize()
|
||||||
|
app.doVersionCheck()
|
||||||
|
if err := app.LoadTaskList(); err != nil {
|
||||||
|
if len(os.Args) > 1 && os.Args[1] != "--reinit" {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return app
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AppState) run(parms []string) int {
|
||||||
|
if len(parms) == 0 || parms[0] == "ui" {
|
||||||
|
// UI Mode
|
||||||
|
return uiLoop()
|
||||||
|
}
|
||||||
|
if fn, ok := a.OpFuncs[parms[0]]; ok {
|
||||||
|
return fn(parms[1:])
|
||||||
|
}
|
||||||
|
fmt.Println("Unknown Command")
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AppState) filterList(list *todotxt.TaskList, filter string) *todotxt.TaskList {
|
||||||
|
return list.Filter(a.getFilterPredicate(filter))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AppState) getFilteredList(filter string) *todotxt.TaskList {
|
||||||
|
return a.TaskList.Filter(a.getFilterPredicate(filter))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AppState) getFilteredDoneList(filter string) *todotxt.TaskList {
|
||||||
|
return a.DoneList.Filter(a.getFilterPredicate(filter))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AppState) getTodoFile() string {
|
||||||
|
return a.directory + a.fileTodo
|
||||||
|
}
|
||||||
|
func (a *AppState) getDoneFile() string {
|
||||||
|
return a.directory + a.fileDone
|
||||||
|
}
|
||||||
|
func (a *AppState) getReportFile() string {
|
||||||
|
return a.directory + a.fileReport
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AppState) addOperation(name string, desc []string, fn func([]string) int) {
|
||||||
|
a.ValidOperations[name] = desc
|
||||||
|
a.OpFuncs[name] = fn
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AppState) getTaskString(task todotxt.Task) string {
|
||||||
|
var completed string
|
||||||
|
completed = " "
|
||||||
|
if task.Completed {
|
||||||
|
completed = "X"
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%d. [%s] %s", task.Id, completed, strings.TrimPrefix(task.String(), "x "))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AppState) getDoneTaskString(task todotxt.Task) string {
|
||||||
|
var completed string
|
||||||
|
completed = " "
|
||||||
|
if task.Completed {
|
||||||
|
completed = "X"
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("0. [%s] %s", completed, strings.TrimPrefix(task.String(), "x "))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AppState) doVersionCheck() {
|
||||||
|
confVer, _ := a.config.GetInt("version")
|
||||||
|
for confVer < a.Version {
|
||||||
|
confVer = a.migrate(confVer, a.Version)
|
||||||
|
}
|
||||||
|
a.config.SetInt("version", confVer)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AppState) migrate(from, to int) int {
|
||||||
|
if from == to {
|
||||||
|
return to
|
||||||
|
}
|
||||||
|
switch from {
|
||||||
|
case 0:
|
||||||
|
a.initializeConfig()
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
// If we get all the way down here, we _must_ be done.
|
||||||
|
return to
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AppState) initialize() {
|
||||||
|
var err error
|
||||||
|
a.config, err = userConfig.NewConfig(a.Name)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
a.ValidOperations = make(map[string][]string)
|
||||||
|
a.OpFuncs = make(map[string]func([]string) int)
|
||||||
|
a.addOperation("ls",
|
||||||
|
[]string{"ls - List Tasks"},
|
||||||
|
a.opListTasks,
|
||||||
|
)
|
||||||
|
a.addOperation("lsa",
|
||||||
|
[]string{"lsa - The same as 'ls -a'"},
|
||||||
|
func(args []string) int {
|
||||||
|
return a.opListTasks(append([]string{"-a"}, args...))
|
||||||
|
},
|
||||||
|
)
|
||||||
|
a.addOperation("add",
|
||||||
|
[]string{"add - Add a task"},
|
||||||
|
a.opAddTask,
|
||||||
|
)
|
||||||
|
a.addOperation("new",
|
||||||
|
[]string{"new - Same as 'add'"},
|
||||||
|
a.opAddTask,
|
||||||
|
)
|
||||||
|
a.addOperation("x",
|
||||||
|
[]string{"x - Toggle a task's complete flag on/off"},
|
||||||
|
a.opToggleTaskComplete,
|
||||||
|
)
|
||||||
|
a.addOperation("done",
|
||||||
|
[]string{"done - The same as 'x'"},
|
||||||
|
a.opToggleTaskComplete,
|
||||||
|
)
|
||||||
|
a.addOperation("archive",
|
||||||
|
[]string{"archive [id1 id2 ...] - Archive completed tasks"},
|
||||||
|
a.opArchiveTasks,
|
||||||
|
)
|
||||||
|
a.addOperation("--reinit",
|
||||||
|
[]string{"--reinit - Reset all Configuration Settings"},
|
||||||
|
func(args []string) int {
|
||||||
|
a.initializeConfig()
|
||||||
|
return 0
|
||||||
|
},
|
||||||
|
)
|
||||||
|
a.addOperation("-h",
|
||||||
|
[]string{"-h - Print this message"},
|
||||||
|
a.opPrintUsage,
|
||||||
|
)
|
||||||
|
a.addOperation("help",
|
||||||
|
[]string{"help - Print this message"},
|
||||||
|
a.opPrintUsage,
|
||||||
|
)
|
||||||
|
a.addOperation("--h",
|
||||||
|
[]string{"--h - Print this message"},
|
||||||
|
a.opPrintUsage,
|
||||||
|
)
|
||||||
|
a.directory = a.config.Get("directory")
|
||||||
|
a.fileTodo = a.config.Get("todofile")
|
||||||
|
a.fileDone = a.config.Get("donefile")
|
||||||
|
a.fileReport = a.config.Get("reportfile")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AppState) initializeConfig() {
|
||||||
|
fmt.Println("Initializing " + a.Name)
|
||||||
|
for {
|
||||||
|
var add string
|
||||||
|
if a.directory != "" {
|
||||||
|
add = " (" + a.directory + ")"
|
||||||
|
}
|
||||||
|
fmt.Println("Path to todo.txt" + add + ":")
|
||||||
|
var resp string
|
||||||
|
fmt.Scanln(&resp)
|
||||||
|
if resp == "" && a.directory != "" {
|
||||||
|
resp = a.directory
|
||||||
|
}
|
||||||
|
if resp != "" {
|
||||||
|
if !strings.HasSuffix(resp, "/") {
|
||||||
|
resp = resp + "/"
|
||||||
|
}
|
||||||
|
fmt.Println("Setting todo.txt directory to: " + resp)
|
||||||
|
a.config.Set("directory", resp)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
a.config.Set("todofile", "todo.txt")
|
||||||
|
a.config.Set("donefile", "done.txt")
|
||||||
|
a.config.Set("reportfile", "report.txt")
|
||||||
|
}
|
28
bundle.go
Normal file
28
bundle.go
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
type Bundle map[string]interface{}
|
||||||
|
|
||||||
|
func (b Bundle) setValue(key string, val interface{}) {
|
||||||
|
b[key] = val
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b Bundle) getBool(key string, def bool) bool {
|
||||||
|
if v, ok := b[key].(bool); ok {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
return def
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b Bundle) getString(key, def string) string {
|
||||||
|
if v, ok := b[key].(string); ok {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
return def
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b Bundle) getInt(key string, def int) int {
|
||||||
|
if v, ok := b[key].(int); ok {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
return def
|
||||||
|
}
|
25
main.go
Normal file
25
main.go
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
AppName = "gask"
|
||||||
|
AppVersion = 1
|
||||||
|
)
|
||||||
|
|
||||||
|
var app *AppState
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
app = NewApp()
|
||||||
|
|
||||||
|
var parms []string
|
||||||
|
if len(os.Args) > 1 {
|
||||||
|
parms = os.Args[1:]
|
||||||
|
} else {
|
||||||
|
// If no parameters were passed, just do an ls
|
||||||
|
parms = append(parms, "ui")
|
||||||
|
}
|
||||||
|
os.Exit(app.run(parms))
|
||||||
|
}
|
117
model.go
Normal file
117
model.go
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
todotxt "github.com/JamesClonk/go-todotxt"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (a *AppState) addTask(taskString string) error {
|
||||||
|
t, err := todotxt.ParseTask(taskString)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if t.CreatedDate.IsZero() {
|
||||||
|
t.CreatedDate = time.Now()
|
||||||
|
}
|
||||||
|
a.TaskList.AddTask(t)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AppState) toggleTaskComplete(id int) error {
|
||||||
|
var task *todotxt.Task
|
||||||
|
var err error
|
||||||
|
if task, err = a.TaskList.GetTask(id); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if task.Completed {
|
||||||
|
task.Reopen()
|
||||||
|
} else {
|
||||||
|
task.Complete()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AppState) archiveTask(id int) error {
|
||||||
|
var err error
|
||||||
|
var task *todotxt.Task
|
||||||
|
if task, err = a.TaskList.GetTask(id); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
a.TaskList.RemoveTask(*task)
|
||||||
|
task.Completed = true
|
||||||
|
a.DoneList.AddTask(task)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AppState) getFilterPredicate(filter string) func(todotxt.Task) bool {
|
||||||
|
var predicates []func(todotxt.Task) bool
|
||||||
|
// If none of the 'filter' is in upper-case, do a case-insensitive filter
|
||||||
|
checkCase := true
|
||||||
|
if strings.ToLower(filter) == filter {
|
||||||
|
checkCase = false
|
||||||
|
}
|
||||||
|
filterParts := strings.Split(filter, " ")
|
||||||
|
for _, part := range filterParts {
|
||||||
|
if strings.HasPrefix(part, "@") {
|
||||||
|
predicates = append(predicates, func(t todotxt.Task) bool {
|
||||||
|
for _, v := range t.Contexts {
|
||||||
|
if "@"+v == part {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
} else if strings.HasPrefix(part, "+") {
|
||||||
|
predicates = append(predicates, func(t todotxt.Task) bool {
|
||||||
|
for _, v := range t.Projects {
|
||||||
|
if "+"+v == part {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
predicates = append(predicates, func(t todotxt.Task) bool {
|
||||||
|
val := t.Original
|
||||||
|
if !checkCase {
|
||||||
|
val = strings.ToLower(t.Original)
|
||||||
|
}
|
||||||
|
return strings.Contains(val, part)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return func(t todotxt.Task) bool {
|
||||||
|
for _, v := range predicates {
|
||||||
|
if v(t) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AppState) LoadTaskList() error {
|
||||||
|
var err error
|
||||||
|
var tl todotxt.TaskList
|
||||||
|
tl, err = todotxt.LoadFromFilename(a.getTodoFile())
|
||||||
|
a.TaskList = &tl
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AppState) LoadDoneList() error {
|
||||||
|
var err error
|
||||||
|
var tl todotxt.TaskList
|
||||||
|
tl, err = todotxt.LoadFromFilename(a.getDoneFile())
|
||||||
|
a.DoneList = &tl
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AppState) WriteList() error {
|
||||||
|
return a.TaskList.WriteToFilename(a.getTodoFile())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AppState) WriteDoneList() error {
|
||||||
|
return a.DoneList.WriteToFilename(a.getDoneFile())
|
||||||
|
}
|
62
screen.go
Normal file
62
screen.go
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
termbox "github.com/nsf/termbox-go"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Screen interface {
|
||||||
|
handleKeyEvent(termbox.Event) int
|
||||||
|
initialize(Bundle) error
|
||||||
|
drawScreen()
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
ScreenMain = iota
|
||||||
|
ScreenTask
|
||||||
|
ScreenAbout
|
||||||
|
ScreenExit
|
||||||
|
|
||||||
|
DefaultBg = termbox.ColorBlack
|
||||||
|
DefaultFg = termbox.ColorWhite
|
||||||
|
TitleFg = termbox.ColorWhite
|
||||||
|
TitleBg = termbox.ColorBlue
|
||||||
|
CursorFg = termbox.ColorBlack
|
||||||
|
CursorBg = termbox.ColorGreen
|
||||||
|
)
|
||||||
|
|
||||||
|
func (a *AppState) BuildScreens() {
|
||||||
|
mainScreen := MainScreen{
|
||||||
|
viewPort: &ViewPort{},
|
||||||
|
}
|
||||||
|
aboutScreen := AboutScreen{}
|
||||||
|
taskScreen := TaskScreen{
|
||||||
|
viewPort: &ViewPort{},
|
||||||
|
}
|
||||||
|
a.screens = append(a.screens, &mainScreen)
|
||||||
|
a.screens = append(a.screens, &taskScreen)
|
||||||
|
a.screens = append(a.screens, &aboutScreen)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AppState) drawBackground(bg termbox.Attribute) {
|
||||||
|
termbox.Clear(0, bg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AppState) layoutAndDrawScreen(s Screen) {
|
||||||
|
a.drawBackground(DefaultBg)
|
||||||
|
s.drawScreen()
|
||||||
|
termbox.Flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ViewPort helps keep track of what's being displayed on the screen
|
||||||
|
type ViewPort struct {
|
||||||
|
bytesPerRow int
|
||||||
|
numberOfRows int
|
||||||
|
firstRow int
|
||||||
|
cursor int
|
||||||
|
}
|
||||||
|
|
||||||
|
func readUserInput(e chan termbox.Event) {
|
||||||
|
for {
|
||||||
|
e <- termbox.PollEvent()
|
||||||
|
}
|
||||||
|
}
|
30
screen_about.go
Normal file
30
screen_about.go
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/br0xen/termbox-util"
|
||||||
|
termbox "github.com/nsf/termbox-go"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AboutScreen holds all that's going on
|
||||||
|
type AboutScreen struct {
|
||||||
|
viewPort *ViewPort
|
||||||
|
message string
|
||||||
|
messageTimeout time.Duration
|
||||||
|
messageTime time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
func (screen *AboutScreen) initialize(bundle Bundle) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (screen *AboutScreen) handleKeyEvent(event termbox.Event) int {
|
||||||
|
return ScreenMain
|
||||||
|
}
|
||||||
|
|
||||||
|
func (screen *AboutScreen) drawScreen() {
|
||||||
|
width, height := termbox.Size()
|
||||||
|
exitTxt := "Press any key to return to tasks"
|
||||||
|
termboxUtil.DrawStringAtPoint(exitTxt, (width-len(exitTxt))/2, height-1, TitleFg, TitleBg)
|
||||||
|
}
|
380
screen_main.go
Normal file
380
screen_main.go
Normal file
@ -0,0 +1,380 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
todotxt "github.com/JamesClonk/go-todotxt"
|
||||||
|
"github.com/br0xen/termbox-util"
|
||||||
|
termbox "github.com/nsf/termbox-go"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MainScreen holds all that's going on
|
||||||
|
type MainScreen struct {
|
||||||
|
viewPort *ViewPort
|
||||||
|
message string
|
||||||
|
messageTimeout time.Duration
|
||||||
|
messageTime time.Time
|
||||||
|
mode int
|
||||||
|
|
||||||
|
inputField *termboxUtil.InputField
|
||||||
|
|
||||||
|
currentList string
|
||||||
|
currentFilter string
|
||||||
|
activeList todotxt.TaskList
|
||||||
|
displayList []todotxt.Task
|
||||||
|
|
||||||
|
undoQueue []string
|
||||||
|
redoQueue []string
|
||||||
|
backspaceDoes int
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
MainBundleListKey = "mainscreen.list"
|
||||||
|
MainBundleFilterKey = "mainscreen.filter"
|
||||||
|
|
||||||
|
MainBundleListTodo = "mainscreen.list.todo"
|
||||||
|
MainBundleListDone = "mainscreen.list.done"
|
||||||
|
|
||||||
|
MainModalIdFilter = "mainscreen.filter"
|
||||||
|
MainModalIdAddTask = "mainscreen.addtask"
|
||||||
|
|
||||||
|
MainBackspaceNothing = iota
|
||||||
|
MainBackspaceMain
|
||||||
|
MainBackspaceFilter
|
||||||
|
)
|
||||||
|
|
||||||
|
func (screen *MainScreen) initialize(bundle Bundle) error {
|
||||||
|
if err := screen.reloadList(bundle); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
width, height := termbox.Size()
|
||||||
|
screen.inputField = termboxUtil.CreateInputField(0, (height - 3), width, 1, DefaultFg, DefaultBg)
|
||||||
|
screen.inputField.SetID("")
|
||||||
|
screen.inputField.SetBordered(false)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (screen *MainScreen) reloadList(bundle Bundle) error {
|
||||||
|
screen.displayList = []todotxt.Task{}
|
||||||
|
screen.currentList = bundle.getString(MainBundleListKey, MainBundleListTodo)
|
||||||
|
switch screen.currentList {
|
||||||
|
case MainBundleListTodo:
|
||||||
|
screen.setActiveList(*app.TaskList)
|
||||||
|
if screen.currentFilter = bundle.getString(MainBundleFilterKey, ""); screen.currentFilter != "" {
|
||||||
|
filteredList := app.filterList(&screen.activeList, screen.currentFilter)
|
||||||
|
for _, av := range screen.activeList {
|
||||||
|
for _, fv := range *filteredList {
|
||||||
|
if av.String() == fv.String() {
|
||||||
|
screen.displayList = append(screen.displayList, av)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for _, av := range screen.activeList {
|
||||||
|
screen.displayList = append(screen.displayList, av)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case MainBundleListDone:
|
||||||
|
if err := app.LoadDoneList(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
screen.setActiveList(*app.DoneList)
|
||||||
|
if screen.currentFilter = bundle.getString(MainBundleFilterKey, ""); screen.currentFilter != "" {
|
||||||
|
filteredList := app.filterList(&screen.activeList, screen.currentFilter)
|
||||||
|
for _, av := range screen.activeList {
|
||||||
|
for _, fv := range *filteredList {
|
||||||
|
if av.String() == fv.String() {
|
||||||
|
screen.displayList = append(screen.displayList, av)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for _, av := range screen.activeList {
|
||||||
|
screen.displayList = append(screen.displayList, av)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (screen *MainScreen) handleKeyEvent(event termbox.Event) int {
|
||||||
|
if screen.inputField.GetID() != "" {
|
||||||
|
return screen.handleInputKeyEvent(event)
|
||||||
|
}
|
||||||
|
if event.Ch == '?' {
|
||||||
|
// Go to About Screen
|
||||||
|
return ScreenAbout
|
||||||
|
} else if event.Key == termbox.KeyBackspace || event.Key == termbox.KeyBackspace2 {
|
||||||
|
|
||||||
|
if screen.backspaceDoes == MainBackspaceNothing {
|
||||||
|
if screen.currentList == MainBundleListDone {
|
||||||
|
screen.reloadList(screen.buildBundle(MainBundleListTodo, screen.currentFilter))
|
||||||
|
} else if screen.currentFilter != "" {
|
||||||
|
screen.reloadList(screen.buildBundle(screen.currentList, ""))
|
||||||
|
}
|
||||||
|
} else if screen.backspaceDoes == MainBackspaceMain {
|
||||||
|
screen.reloadList(screen.buildBundle(MainBundleListTodo, screen.currentFilter))
|
||||||
|
} else if screen.backspaceDoes == MainBackspaceFilter {
|
||||||
|
screen.reloadList(screen.buildBundle(screen.currentList, ""))
|
||||||
|
}
|
||||||
|
return ScreenMain
|
||||||
|
|
||||||
|
} else if event.Key == termbox.KeySpace {
|
||||||
|
return screen.toggleTaskComplete()
|
||||||
|
|
||||||
|
} else if event.Ch == 'L' {
|
||||||
|
return screen.toggleViewList()
|
||||||
|
|
||||||
|
} else if event.Ch == 'a' {
|
||||||
|
return screen.startAddNewTask()
|
||||||
|
|
||||||
|
} else if event.Ch == 'e' || event.Ch == 'l' || event.Key == termbox.KeyEnter {
|
||||||
|
return screen.startEditTaskScreen()
|
||||||
|
|
||||||
|
} else if event.Ch == 'j' || event.Key == termbox.KeyArrowDown {
|
||||||
|
screen.moveCursorDown()
|
||||||
|
|
||||||
|
} else if event.Ch == 'k' || event.Key == termbox.KeyArrowUp {
|
||||||
|
screen.moveCursorUp()
|
||||||
|
|
||||||
|
} else if event.Ch == '/' {
|
||||||
|
screen.startFilter()
|
||||||
|
|
||||||
|
} else if event.Ch == 'D' {
|
||||||
|
screen.archiveCurrentItem()
|
||||||
|
|
||||||
|
} else if event.Ch == 'q' {
|
||||||
|
return ScreenExit
|
||||||
|
}
|
||||||
|
return ScreenMain
|
||||||
|
}
|
||||||
|
|
||||||
|
func (screen *MainScreen) handleConfirmKeyEvent(event termbox.Event) int {
|
||||||
|
return ScreenMain
|
||||||
|
}
|
||||||
|
|
||||||
|
func (screen *MainScreen) handleInputKeyEvent(event termbox.Event) int {
|
||||||
|
screen.inputField.SetID("add task")
|
||||||
|
id := screen.inputField.GetID()
|
||||||
|
if id == "filter" {
|
||||||
|
if event.Key == termbox.KeyEnter {
|
||||||
|
// Apply the filter
|
||||||
|
screen.backspaceDoes = MainBackspaceFilter
|
||||||
|
screen.reloadList(screen.buildBundle(screen.currentList, screen.inputField.GetValue()))
|
||||||
|
screen.inputField.SetID("")
|
||||||
|
screen.inputField.SetValue("")
|
||||||
|
return ScreenMain
|
||||||
|
}
|
||||||
|
} else if id == "add task" {
|
||||||
|
if event.Key == termbox.KeyEnter {
|
||||||
|
// Create the new item
|
||||||
|
err := app.addTask(screen.inputField.GetValue())
|
||||||
|
if err != nil {
|
||||||
|
screen.setMessage(err.Error())
|
||||||
|
}
|
||||||
|
if err = app.WriteList(); err != nil {
|
||||||
|
screen.setMessage(err.Error())
|
||||||
|
return ScreenMain
|
||||||
|
}
|
||||||
|
screen.reloadList(screen.buildBundle(screen.currentList, screen.currentFilter))
|
||||||
|
screen.inputField.SetID("")
|
||||||
|
screen.inputField.SetValue("")
|
||||||
|
return ScreenMain
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if event.Key == termbox.KeyBackspace || event.Key == termbox.KeyBackspace2 {
|
||||||
|
if screen.inputField.GetValue() == "" {
|
||||||
|
screen.reloadList(screen.buildBundle(screen.currentList, screen.inputField.GetValue()))
|
||||||
|
screen.inputField.SetID("")
|
||||||
|
screen.inputField.SetValue("")
|
||||||
|
return ScreenMain
|
||||||
|
}
|
||||||
|
}
|
||||||
|
screen.inputField.HandleEvent(event)
|
||||||
|
return ScreenMain
|
||||||
|
}
|
||||||
|
|
||||||
|
func (screen *MainScreen) setActiveList(list todotxt.TaskList) {
|
||||||
|
screen.activeList = todotxt.NewTaskList()
|
||||||
|
for _, v := range list {
|
||||||
|
screen.activeList.AddTask(&v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (screen *MainScreen) drawScreen() {
|
||||||
|
if screen.message == "" {
|
||||||
|
screen.setMessageWithTimeout("Press '?' for help", -1)
|
||||||
|
}
|
||||||
|
screen.drawHeader()
|
||||||
|
for k, v := range screen.displayList {
|
||||||
|
useFg, useBg := DefaultFg, DefaultBg
|
||||||
|
if k == screen.viewPort.cursor {
|
||||||
|
useFg, useBg = CursorFg, CursorBg
|
||||||
|
}
|
||||||
|
termboxUtil.DrawStringAtPoint(app.getTaskString(v), 0, k+1, useFg, useBg)
|
||||||
|
}
|
||||||
|
screen.drawFooter()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (screen *MainScreen) drawHeader() {
|
||||||
|
width, _ := termbox.Size()
|
||||||
|
headerString := screen.currentFilter
|
||||||
|
if headerString == "" {
|
||||||
|
if screen.currentList == MainBundleListTodo {
|
||||||
|
headerString = "Todo List"
|
||||||
|
} else if screen.currentList == MainBundleListDone {
|
||||||
|
headerString = "Done List"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
spaces := strings.Repeat(" ", ((width-len(headerString))/2)+1)
|
||||||
|
termboxUtil.DrawStringAtPoint(fmt.Sprintf("%s%s%s", spaces, headerString, spaces), 0, 0, TitleFg, TitleBg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (screen *MainScreen) drawFooter() {
|
||||||
|
if screen.messageTimeout > 0 && time.Since(screen.messageTime) > screen.messageTimeout {
|
||||||
|
screen.clearMessage()
|
||||||
|
}
|
||||||
|
_, height := termbox.Size()
|
||||||
|
if screen.inputField.GetID() != "" {
|
||||||
|
termboxUtil.DrawStringAtPoint(screen.inputField.GetID()+": ", 0, height-2, DefaultFg, DefaultBg)
|
||||||
|
screen.inputField.Draw()
|
||||||
|
}
|
||||||
|
// And the 'message'
|
||||||
|
termboxUtil.DrawStringAtPoint(screen.message, 0, height-1, DefaultFg, DefaultBg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (screen *MainScreen) archiveCurrentItem() int {
|
||||||
|
/*
|
||||||
|
// Find the task under the cursor
|
||||||
|
if len(screen.displayList) > screen.viewPort.cursor {
|
||||||
|
t := screen.displayList[screen.viewPort.cursor]
|
||||||
|
// Load the task screen with this task
|
||||||
|
if err := a.archiveTask(t.Id); err != nil {
|
||||||
|
screen.setMessage(error.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
b := Bundle{}
|
||||||
|
b.setValue(TaskBundleTaskIdKey, t.Id)
|
||||||
|
if err := app.screens[ScreenTask].initialize(b); err != nil {
|
||||||
|
screen.setMessage(err.Error())
|
||||||
|
}
|
||||||
|
return ScreenTask
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
return ScreenMain
|
||||||
|
}
|
||||||
|
|
||||||
|
func (screen *MainScreen) startEditTaskScreen() int {
|
||||||
|
// Find the task under the cursor
|
||||||
|
if len(screen.displayList) > screen.viewPort.cursor {
|
||||||
|
t := screen.displayList[screen.viewPort.cursor]
|
||||||
|
// Load the task screen with this task
|
||||||
|
b := Bundle{}
|
||||||
|
b.setValue(TaskBundleTaskIdKey, t.Id)
|
||||||
|
if err := app.screens[ScreenTask].initialize(b); err != nil {
|
||||||
|
screen.setMessage(err.Error())
|
||||||
|
}
|
||||||
|
return ScreenTask
|
||||||
|
}
|
||||||
|
return ScreenMain
|
||||||
|
}
|
||||||
|
|
||||||
|
func (screen *MainScreen) reloadCurrentView() {
|
||||||
|
bundle := Bundle{}
|
||||||
|
bundle.setValue(MainBundleListKey, screen.currentList)
|
||||||
|
bundle.setValue(MainBundleFilterKey, screen.currentFilter)
|
||||||
|
screen.initialize(bundle)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (screen *MainScreen) toggleViewList() int {
|
||||||
|
bundle := Bundle{}
|
||||||
|
if screen.currentList == MainBundleListTodo {
|
||||||
|
bundle.setValue(MainBundleListKey, MainBundleListDone)
|
||||||
|
screen.backspaceDoes = MainBackspaceMain
|
||||||
|
} else {
|
||||||
|
bundle.setValue(MainBundleListKey, MainBundleListTodo)
|
||||||
|
}
|
||||||
|
bundle.setValue(MainBundleFilterKey, screen.currentFilter)
|
||||||
|
screen.initialize(bundle)
|
||||||
|
return ScreenMain
|
||||||
|
}
|
||||||
|
|
||||||
|
func (screen *MainScreen) startAddNewTask() int {
|
||||||
|
screen.inputField.SetID("add task")
|
||||||
|
screen.inputField.SetX(len(screen.inputField.GetID()) + 2)
|
||||||
|
return ScreenMain
|
||||||
|
}
|
||||||
|
|
||||||
|
func (screen *MainScreen) toggleTaskComplete() int {
|
||||||
|
// Find the task under the cursor
|
||||||
|
if len(screen.displayList) > screen.viewPort.cursor {
|
||||||
|
t := screen.displayList[screen.viewPort.cursor]
|
||||||
|
err := app.toggleTaskComplete(t.Id)
|
||||||
|
if err != nil {
|
||||||
|
screen.setMessage(err.Error())
|
||||||
|
return ScreenMain
|
||||||
|
}
|
||||||
|
if err = app.WriteList(); err != nil {
|
||||||
|
screen.setMessage(err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
screen.reloadCurrentView()
|
||||||
|
return ScreenMain
|
||||||
|
}
|
||||||
|
|
||||||
|
func (screen *MainScreen) moveCursorDown() bool {
|
||||||
|
screen.viewPort.cursor++
|
||||||
|
if screen.viewPort.cursor >= len(screen.displayList) {
|
||||||
|
screen.viewPort.cursor = len(screen.displayList) - 1
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (screen *MainScreen) moveCursorUp() bool {
|
||||||
|
screen.viewPort.cursor--
|
||||||
|
if screen.viewPort.cursor < 0 {
|
||||||
|
screen.viewPort.cursor = 0
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (screen *MainScreen) startFilter() {
|
||||||
|
screen.inputField.SetID("filter")
|
||||||
|
screen.inputField.SetX(len(screen.inputField.GetID()) + 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (screen *MainScreen) setMessage(msg string) {
|
||||||
|
screen.message = msg
|
||||||
|
screen.messageTime = time.Now()
|
||||||
|
screen.messageTimeout = time.Second * 2
|
||||||
|
}
|
||||||
|
|
||||||
|
/* setMessageWithTimeout lets you specify the timeout for the message
|
||||||
|
* setting it to -1 means it won't timeout
|
||||||
|
*/
|
||||||
|
func (screen *MainScreen) setMessageWithTimeout(msg string, timeout time.Duration) {
|
||||||
|
screen.message = msg
|
||||||
|
screen.messageTime = time.Now()
|
||||||
|
screen.messageTimeout = timeout
|
||||||
|
}
|
||||||
|
|
||||||
|
func (screen *MainScreen) clearMessage() {
|
||||||
|
screen.message = ""
|
||||||
|
screen.messageTimeout = -1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (screen *MainScreen) buildBundle(list, filter string) Bundle {
|
||||||
|
bundle := Bundle{}
|
||||||
|
bundle.setValue(MainBundleListKey, list)
|
||||||
|
bundle.setValue(MainBundleFilterKey, filter)
|
||||||
|
return bundle
|
||||||
|
}
|
122
screen_task.go
Normal file
122
screen_task.go
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
todotxt "github.com/JamesClonk/go-todotxt"
|
||||||
|
"github.com/br0xen/termbox-util"
|
||||||
|
termbox "github.com/nsf/termbox-go"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TaskScreen holds all that's going on
|
||||||
|
type TaskScreen struct {
|
||||||
|
viewPort *ViewPort
|
||||||
|
message string
|
||||||
|
messageTimeout time.Duration
|
||||||
|
messageTime time.Time
|
||||||
|
|
||||||
|
inputModal *termboxUtil.InputModal
|
||||||
|
confirmModal *termboxUtil.ConfirmModal
|
||||||
|
|
||||||
|
currentTaskId int
|
||||||
|
displayTask *todotxt.Task
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
TaskBundleTaskIdKey = "taskscreen.taskid"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (screen *TaskScreen) initialize(bundle Bundle) error {
|
||||||
|
var err error
|
||||||
|
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
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (screen *TaskScreen) handleKeyEvent(event termbox.Event) int {
|
||||||
|
if event.Key == termbox.KeyBackspace || event.Key == termbox.KeyBackspace2 || event.Ch == 'h' || event.Key == termbox.KeyArrowLeft {
|
||||||
|
return ScreenMain
|
||||||
|
|
||||||
|
} else if event.Ch == 'j' || event.Key == termbox.KeyArrowDown {
|
||||||
|
screen.moveCursorDown()
|
||||||
|
|
||||||
|
} else if event.Ch == 'k' || event.Key == termbox.KeyArrowUp {
|
||||||
|
screen.moveCursorUp()
|
||||||
|
}
|
||||||
|
return ScreenTask
|
||||||
|
}
|
||||||
|
|
||||||
|
func (screen *TaskScreen) drawScreen() {
|
||||||
|
screen.drawHeader()
|
||||||
|
|
||||||
|
yPos := 1
|
||||||
|
termboxUtil.DrawStringAtPoint(screen.displayTask.Todo, 0, yPos, DefaultFg, DefaultBg)
|
||||||
|
yPos++
|
||||||
|
termboxUtil.DrawStringAtPoint(fmt.Sprintf("Priority: %s", screen.displayTask.Priority), 0, yPos, DefaultFg, DefaultBg)
|
||||||
|
yPos++
|
||||||
|
termboxUtil.DrawStringAtPoint(fmt.Sprintf("Projects: %s", screen.displayTask.Projects), 0, yPos, DefaultFg, DefaultBg)
|
||||||
|
yPos++
|
||||||
|
termboxUtil.DrawStringAtPoint(fmt.Sprintf("Contexts: %s", screen.displayTask.Contexts), 0, yPos, DefaultFg, DefaultBg)
|
||||||
|
yPos++
|
||||||
|
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++
|
||||||
|
}
|
||||||
|
|
||||||
|
screen.drawFooter()
|
||||||
|
}
|
||||||
|
|
||||||
|
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() {
|
||||||
|
if screen.messageTimeout > 0 && time.Since(screen.messageTime) > screen.messageTimeout {
|
||||||
|
screen.clearMessage()
|
||||||
|
}
|
||||||
|
_, height := termbox.Size()
|
||||||
|
termboxUtil.DrawStringAtPoint(screen.message, 0, height-1, DefaultFg, DefaultBg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (screen *TaskScreen) moveCursorDown() bool {
|
||||||
|
screen.viewPort.cursor++
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (screen *TaskScreen) moveCursorUp() bool {
|
||||||
|
screen.viewPort.cursor--
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (screen *TaskScreen) setMessage(msg string) {
|
||||||
|
screen.message = msg
|
||||||
|
screen.messageTime = time.Now()
|
||||||
|
screen.messageTimeout = time.Second * 2
|
||||||
|
}
|
||||||
|
|
||||||
|
/* setMessageWithTimeout lets you specify the timeout for the message
|
||||||
|
* setting it to -1 means it won't timeout
|
||||||
|
*/
|
||||||
|
func (screen *TaskScreen) setMessageWithTimeout(msg string, timeout time.Duration) {
|
||||||
|
screen.message = msg
|
||||||
|
screen.messageTime = time.Now()
|
||||||
|
screen.messageTimeout = timeout
|
||||||
|
}
|
||||||
|
|
||||||
|
func (screen *TaskScreen) clearMessage() {
|
||||||
|
screen.message = ""
|
||||||
|
screen.messageTimeout = -1
|
||||||
|
}
|
140
task_ops.go
Normal file
140
task_ops.go
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
todotxt "github.com/JamesClonk/go-todotxt"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (a *AppState) opListTasks(args []string) int {
|
||||||
|
var lastIdx int
|
||||||
|
var filterString string
|
||||||
|
showAll := len(args) > 0 && args[0] == "-1"
|
||||||
|
if showAll {
|
||||||
|
args = args[1:]
|
||||||
|
}
|
||||||
|
if len(args) > 0 {
|
||||||
|
filterString = strings.Join(args, " ")
|
||||||
|
a.TaskList = a.getFilteredList(filterString)
|
||||||
|
}
|
||||||
|
for _, v := range *a.TaskList {
|
||||||
|
fmt.Println(a.getTaskString(v))
|
||||||
|
lastIdx++
|
||||||
|
}
|
||||||
|
if showAll {
|
||||||
|
if err := app.LoadDoneList(); err != nil {
|
||||||
|
fmt.Println("Error loading 'Done' list")
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
if filterString != "" {
|
||||||
|
a.DoneList = a.getFilteredList(filterString)
|
||||||
|
}
|
||||||
|
for _, v := range *a.DoneList {
|
||||||
|
fmt.Println(a.getDoneTaskString(v))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AppState) opAddTask(args []string) int {
|
||||||
|
taskString := strings.Join(args, " ")
|
||||||
|
if err := a.addTask(taskString); err != nil {
|
||||||
|
fmt.Println("Error adding task")
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
if err := a.WriteList(); err != nil {
|
||||||
|
fmt.Println("Error saving task list")
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AppState) opToggleTaskComplete(args []string) int {
|
||||||
|
if len(args) == 0 {
|
||||||
|
fmt.Println("No id given")
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
if len(args) == 0 {
|
||||||
|
fmt.Println("No id given")
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
for _, v := range args {
|
||||||
|
var id int
|
||||||
|
var err error
|
||||||
|
if id, err = strconv.Atoi(v); err != nil {
|
||||||
|
fmt.Printf("Invalid id given: %s\n", v)
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
if err := a.toggleTaskComplete(id); err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if a.WriteList() != nil {
|
||||||
|
fmt.Println("Error saving list")
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AppState) opArchiveTasks(args []string) int {
|
||||||
|
if err := app.LoadDoneList(); err != nil {
|
||||||
|
fmt.Println("Error loading 'Done' list")
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
if len(args) > 0 {
|
||||||
|
for _, v := range args {
|
||||||
|
var id int
|
||||||
|
var task *todotxt.Task
|
||||||
|
var err error
|
||||||
|
if id, err = strconv.Atoi(v); err != nil {
|
||||||
|
fmt.Printf("Invalid id given: %s\n", v)
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
if task, err = a.TaskList.GetTask(id); err != nil {
|
||||||
|
fmt.Printf("Error getting task %d\n", id)
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
if err = a.archiveTask(id); err != nil {
|
||||||
|
fmt.Printf("Error archiving task %d\n", id)
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
fmt.Println(a.getDoneTaskString(*task))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for _, v := range *a.TaskList {
|
||||||
|
if v.Completed {
|
||||||
|
if err := a.archiveTask(v.Id); err != nil {
|
||||||
|
fmt.Printf("Error archiving task %d\n", v.Id)
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
fmt.Println(a.getDoneTaskString(v))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if a.WriteDoneList() != nil {
|
||||||
|
fmt.Println("Error saving archive list")
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
if a.WriteList() != nil {
|
||||||
|
fmt.Println("Error saving task list")
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AppState) opPrintUsage(args []string) int {
|
||||||
|
for _, v := range a.ValidOperations {
|
||||||
|
for _, vv := range v {
|
||||||
|
fmt.Println(" " + vv)
|
||||||
|
fmt.Println("")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
56
ui_loop.go
Normal file
56
ui_loop.go
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"runtime"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
termbox "github.com/nsf/termbox-go"
|
||||||
|
)
|
||||||
|
|
||||||
|
func uiLoop() int {
|
||||||
|
err := termbox.Init()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
termbox.SetOutputMode(termbox.Output256)
|
||||||
|
app.BuildScreens()
|
||||||
|
displayScreen := app.screens[ScreenMain]
|
||||||
|
bundle := Bundle{}
|
||||||
|
bundle.setValue(MainBundleListKey, MainBundleListTodo)
|
||||||
|
|
||||||
|
displayScreen.initialize(bundle)
|
||||||
|
app.layoutAndDrawScreen(displayScreen)
|
||||||
|
eventChan := make(chan termbox.Event)
|
||||||
|
go readUserInput(eventChan)
|
||||||
|
for {
|
||||||
|
event := <-eventChan
|
||||||
|
if event.Type == termbox.EventKey {
|
||||||
|
if event.Key == termbox.KeyCtrlC {
|
||||||
|
break
|
||||||
|
} else if event.Key == termbox.KeyCtrlZ {
|
||||||
|
if runtime.GOOS != "windows" {
|
||||||
|
process, _ := os.FindProcess(os.Getpid())
|
||||||
|
termbox.Close()
|
||||||
|
process.Signal(syscall.SIGSTOP)
|
||||||
|
termbox.Init()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
newScreenIndex := displayScreen.handleKeyEvent(event)
|
||||||
|
if newScreenIndex < len(app.screens) {
|
||||||
|
displayScreen = app.screens[newScreenIndex]
|
||||||
|
app.layoutAndDrawScreen(displayScreen)
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if event.Type == termbox.EventResize {
|
||||||
|
app.layoutAndDrawScreen(displayScreen)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
termbox.Close()
|
||||||
|
// Any wrap up should be done here...
|
||||||
|
return 0
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user