Working on v2

This commit is contained in:
2022-01-19 12:56:31 -06:00
parent 1895739c71
commit a2924b8a61
32 changed files with 2121 additions and 20 deletions

283
old/app_state.go Normal file
View File

@@ -0,0 +1,283 @@
package main
import (
"fmt"
"os"
"strings"
timertxt "git.bullercodeworks.com/brian/go-timertxt"
userConfig "github.com/br0xen/user-config"
)
type AppState struct {
Name string
Version int
config *userConfig.Config
directory string
fileTimer string
fileDone string
fileReport string
ValidOperations map[string][]string
OpFuncs map[string]func([]string) int
TimerList *timertxt.TimerList
DoneList *timertxt.TimerList
screens []Screen
}
func NewApp() *AppState {
app := &AppState{Name: AppName, Version: AppVersion}
app.initialize()
app.doVersionCheck()
if err := app.LoadTimerList(); err != nil {
if len(os.Args) > 1 && os.Args[1] != "--reinit" {
panic(err)
}
}
return app
}
func (a *AppState) filterList(list *timertxt.TimerList, filter string) *timertxt.TimerList {
return list.Filter(a.getFilterPredicate(filter))
}
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) getTimerFile() string {
return a.directory + a.fileTimer
}
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) getTimerString(timer timertxt.Timer) string {
return fmt.Sprintf("%d. %s", timer.Id, strings.TrimPrefix(timer.String(), "x "))
}
func (a *AppState) getDoneTimerString(timer timertxt.Timer) string {
return fmt.Sprintf("--. %s", timer.String())
}
func (a *AppState) doVersionCheck() {
confVer, _ := a.config.GetInt("version")
for confVer < a.Version {
confVer = a.migrate(confVer, a.Version)
}
a.config.SetInt("version", confVer)
}
// Data Migrations
// 0 -> 1: Initialize Config
// 1 -> 2: No migration Needed
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("i3status",
[]string{
"i3status - Output JSON for i3status-rust to consume",
},
a.opI3Status,
)
a.addOperation("toggle",
[]string{
"toggle - Pause/Resume the most recent timer",
},
a.opToggleTimer,
)
a.addOperation("time",
[]string{
"time [contexts] [projects] - Only output the total time value",
" --a - Include done.txt file",
" [start] - List entries after this date",
" [end] - List entries before this date",
" [@contexts] - Filter entries with the given contexts",
" [+projects] - Filter entries with the given projects",
},
a.opShowTime,
)
a.addOperation("ls",
[]string{
"ls [--a] [start] [end] [contexts] [projects] - List Timers",
" --a - Include done.txt file",
" [start] - List entries after this date",
" [end] - List entries before this date",
" [@contexts] - List entries with the given contexts",
" [+projects] - List entries with the given projects",
},
a.opListTimers,
)
a.addOperation("lsa",
[]string{
"lsa [start] [end] [contexts] [projects] - Shortcut for 'ls --a'",
},
func(args []string) int {
return a.opListTimers(append([]string{"--a"}, args...))
},
)
a.addOperation("start",
[]string{
"start [time] [@contexts...] [+projects...] [tag:value...]",
" - Start a timer with the given details",
" If the first argument looks like a time,",
" the timer will be started then (past or future)",
},
a.opStartTimer,
)
a.addOperation("stop",
[]string{
"stop [time] - Stops the current timer",
" If the first argument looks like a time,",
" the timer will be stopped then (past or future)",
},
a.opStopTimer,
)
a.addOperation("switch",
[]string{
"switch [time] [@contexts...] [+projects...] [tag:value...]",
" - Stops all active timers and starts a new one",
" with the given arguments",
},
a.opSwitchTimer,
)
a.addOperation("rm",
[]string{
"rm [id] - Removes the timer with the given id",
" WARNING: CANNOT BE UNDONE",
},
a.opRemoveTimer,
)
a.addOperation("archive",
[]string{
"archive [id] [@context] [+project] - Archive the timer with the given id,context,project",
},
a.opArchiveTimer,
)
a.addOperation("status",
[]string{
"status - Prints the status of all active timers",
},
a.opStatus,
)
a.addOperation("mod",
[]string{
"mod <id> [start=<start>] [end=<end>] [projects=<project,...>] [contexts=<context,...>]",
" - Modify timer with id <id> changes are absolute, so, e.g., if you pass a",
" 'projects' tag, the projects on the timer will be replaced with the value",
},
a.opModifyTimer,
)
a.addOperation("fuzzyparse",
[]string{
"fuzzyparse [date string] - Parses the passed string and print the RFC3339 result (for testing)",
},
a.opFuzzyParse,
)
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("config",
[]string{
"config - Print or edit config",
},
a.opConfig,
)
a.addOperation("--h",
[]string{
"--h - Print this message",
},
a.opPrintUsage,
)
a.addOperation("editor",
[]string{
"editor - Open the timer file in $EDITOR",
},
a.opEditor,
)
a.directory = a.config.Get("directory")
a.fileTimer = a.config.Get("timerfile")
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 timer.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 timer.txt directory to: " + resp)
a.config.Set("directory", resp)
break
}
}
a.config.Set("timerfile", "timer.txt")
a.config.Set("donefile", "done.txt")
a.config.Set("reportfile", "report.txt")
}

28
old/bundle.go Normal file
View 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
}

305
old/helpers.go Normal file
View File

@@ -0,0 +1,305 @@
package main
import (
"errors"
"fmt"
"strings"
"time"
timertxt "git.bullercodeworks.com/brian/go-timertxt"
)
// TimerToString takes a TimeEntry and gives a nicely formatted string
func TimerToString(t *timertxt.Timer) string {
var ret string
var end string
if t.StartsToday() {
ret = t.StartDate.Format("15:04 - ")
end = "**:**"
} else {
ret = t.StartDate.Format("2006/01/02 15:04:05 - ")
end = "**:**:**"
}
if !t.FinishDate.IsZero() {
if t.EndsToday() {
end = t.FinishDate.Format("15:04")
} else {
end = t.FinishDate.Format("2006/01/02 15:04:05")
}
}
ret += end
if len(t.Contexts) > 0 {
ret += " " + fmt.Sprint(t.Contexts)
}
if len(t.Projects) > 0 {
ret += " " + fmt.Sprint(t.Projects)
}
if len(t.AdditionalTags) > 0 {
ret += " [ "
for k, v := range t.AdditionalTags {
ret += k + ":" + v + " "
}
ret += "]"
}
return ret
}
func GetRoundToDuration() time.Duration {
var dur time.Duration
var err error
if dur, err = time.ParseDuration(app.config.Get("roundto")); err != nil {
app.config.Set("roundto", DefRoundTo)
dur, _ = time.ParseDuration(DefRoundTo)
}
return dur
}
func DurationToDecimal(dur time.Duration) float64 {
mins := dur.Minutes() - (dur.Hours() * 60)
return dur.Hours() + (mins / 60)
}
// getContextsFromSlice pulls all '@' (contexts) out of the
// string slice and return those contexts and the remaining
// strings from the slice
func getContextsFromSlice(args []string) ([]string, []string) {
return splitSlice(args, func(v string) bool {
return strings.HasPrefix(v, "@")
})
}
// getProjectsFromSlice pulls all '+' (projects) out of the
// string slice and return those projects and the remaining
// strings from the slice
func getProjectsFromSlice(args []string) ([]string, []string) {
return splitSlice(args, func(v string) bool {
return strings.HasPrefix(v, "+")
})
}
// getAdditionalTagsFromSlice pulls all '*:*' (tags) out of the
// string slice and returns those tags and the remaining
// strings from the slice
func getAdditionalTagsFromSlice(args []string) ([]string, []string) {
return splitSlice(args, func(v string) bool {
return strings.Contains(v, ":")
})
}
func splitSlice(args []string, predicate func(string) bool) ([]string, []string) {
var rem1, rem2 []string
for _, v := range args {
if predicate(v) {
rem1 = append(rem1, v)
} else {
rem2 = append(rem2, v)
}
}
return rem1, rem2
}
func parseFuzzyTime(t string) (time.Time, error) {
var ret time.Time
var err error
for i := range fuzzyFormats {
ret, err = time.Parse(fuzzyFormats[i], t)
if err == nil {
// Make sure it's in the local timezone
tz := time.Now().Format("Z07:00")
t = ret.Format("2006-01-02T15:04:05") + tz
if ret, err = time.Parse(time.RFC3339, t); err != nil {
return ret, err
}
// Check for zero on year/mo/day
if ret.Year() == 0 && ret.Month() == time.January && ret.Day() == 1 {
ret = ret.AddDate(time.Now().Year(), int(time.Now().Month())-1, time.Now().Day()-1)
}
return ret, nil
}
}
return time.Time{}, errors.New("Unable to parse time: " + t)
}
var fuzzyFormats = []string{
"1504",
"15:04", // Kitchen, 24hr
time.Kitchen,
time.RFC3339,
"2006-01-02T15:04:05", // RFC3339 without timezone
"2006-01-02T15:04", // RFC3339 without seconds or timezone
time.Stamp,
"02 Jan 06 15:04:05", // RFC822 with second
time.RFC822,
"01/02/2006 15:04", // U.S. Format
"01/02/2006 15:04:05", // U.S. Format with seconds
"01/02/06 15:04", // U.S. Format, short year
"01/02/06 15:04:05", // U.S. Format, short year, with seconds
"2006-01-02",
"2006-01-02 15:04",
"2006-01-02 15:04:05",
"20060102",
"20060102 15:04",
"20060102 15:04:05",
"20060102 1504",
"20060102 150405",
"20060102T15:04",
"20060102T15:04:05",
"20060102T1504",
"20060102T150405",
}
func timerToFriendlyString(t *timertxt.Timer) string {
var start, end, contexts, projects, tags string
start = t.StartDate.Format(friendlyFormatForTime(t.StartDate))
if t.FinishDate.IsZero() {
end = "**:**"
} else {
end = t.FinishDate.Format(friendlyFormatForTime(t.FinishDate))
}
for _, v := range t.Contexts {
contexts += "@" + v + " "
}
for _, v := range t.Projects {
projects += "+" + v + " "
}
for k, v := range t.AdditionalTags {
tags = fmt.Sprintf("%s%s:%s ", tags, k, v)
}
var dur time.Duration
if t.FinishDate.IsZero() {
dur = time.Now().Sub(t.StartDate)
} else {
dur = t.FinishDate.Sub(t.StartDate)
}
dur = dur.Round(GetRoundToDuration())
return fmt.Sprintf("% 2d. %s - %s [ %s] [ %s] [ %s] %s ( %.2f )", t.Id, start, end, contexts, projects, tags, t.Notes, DurationToDecimal(dur))
}
func friendlyFormatForTime(t time.Time) string {
nowTime := time.Now()
if t.Year() != nowTime.Year() || t.Month() != nowTime.Month() {
return "2006-01-02 15:04"
} else if t.Day() != nowTime.Day() {
return "01/02 15:04"
}
return "15:04"
}
// timeToFriendlyString returns an easier to read version of the time
// giving enough details that the user should be fine inferring the rest
func timeToFriendlyString(t time.Time) string {
return t.Format(friendlyFormatForTime(t))
}
func sinceToString(tm time.Time) string {
return diffToString(tm, time.Now())
}
func diffToString(tm1, tm2 time.Time) string {
ret := ""
yr, mo, dy, hr, mn, sc := diff(tm1, tm2)
higher := false
if yr > 0 {
ret += fmt.Sprintf("%4dy ", yr)
higher = true
}
if mo > 0 || higher {
ret += fmt.Sprintf("%2dm ", mo)
higher = true
}
if dy > 0 || higher {
ret += fmt.Sprintf("%2dd ", dy)
higher = true
}
if hr > 0 || higher {
ret += fmt.Sprintf("%2dh ", hr)
higher = true
}
if mn > 0 || higher {
ret += fmt.Sprintf("%2dm ", mn)
higher = true
}
if sc > 0 || higher {
ret += fmt.Sprintf("%2ds", sc)
}
return ret
}
func padRight(st string, l int) string {
for len(st) < l {
st = st + " "
}
return st
}
func padLeft(st string, l int) string {
for len(st) < l {
st = " " + st
}
return st
}
func diff(a, b time.Time) (year, month, day, hour, min, sec int) {
if a.Location() != b.Location() {
b = b.In(a.Location())
}
if a.After(b) {
a, b = b, a
}
y1, M1, d1 := a.Date()
y2, M2, d2 := b.Date()
h1, m1, s1 := a.Clock()
h2, m2, s2 := b.Clock()
year = int(y2 - y1)
month = int(M2 - M1)
day = int(d2 - d1)
hour = int(h2 - h1)
min = int(m2 - m1)
sec = int(s2 - s1)
// Normalize negative values
if sec < 0 {
sec += 60
min--
}
if min < 0 {
min += 60
hour--
}
if hour < 0 {
hour += 24
day--
}
if day < 0 {
// days in month:
t := time.Date(y1, M1, 32, 0, 0, 0, 0, time.UTC)
day += 32 - t.Day()
month--
}
if month < 0 {
month += 12
year--
}
return
}
func BeginningOfDay() time.Time {
now := time.Now()
return time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location())
}
func BeginningOfWeek() time.Time {
now := time.Now()
t := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location())
weekday := int(t.Weekday())
return t.AddDate(0, 0, -weekday)
}
func BeginningOfMonth() time.Time {
now := time.Now()
return time.Date(now.Year(), now.Month(), 1, 0, 0, 0, 0, now.Location())
}

233
old/model.go Normal file
View File

@@ -0,0 +1,233 @@
package main
import (
"fmt"
"strings"
"time"
timertxt "git.bullercodeworks.com/brian/go-timertxt"
)
// If we have an active timer in timer.txt, return the most recent one
// Otherwise, check if we have a more recent completed timer in done.txt
// Return the most recent done from timer.txt or dont.txt
func (a *AppState) getMostRecentTimer() (*timertxt.Timer, error) {
work, wErr := a.TimerList.GetMostRecentTimer()
if wErr == nil && work.FinishDate.IsZero() {
return work, nil
}
if err := a.LoadDoneList(); err != nil {
return nil, err
}
done, dErr := a.DoneList.GetMostRecentTimer()
if dErr != nil {
return nil, dErr
}
if !done.FinishDate.IsZero() && work == nil || done.FinishDate.After(work.FinishDate) {
return done, nil
}
return work, nil
}
func (a *AppState) getFilteredTimerList(args []string) *timertxt.TimerList {
var includeArchive bool
var err error
start := time.Time{}
end := time.Now()
var contextFilters []string
var projectFilters []string
var allFilters []func(timertxt.Timer) bool
if len(args) > 0 {
contextFilters, args = getContextsFromSlice(args)
projectFilters, args = getProjectsFromSlice(args)
}
if len(args) > 0 {
if args[0] == "--a" {
includeArchive = true
args = args[1:]
}
}
if len(args) > 0 {
if start, err = parseFuzzyTime(args[0]); err != nil {
y, m, d := time.Now().Date()
start = time.Date(y, m, d, 0, 0, 0, 0, time.Now().Location())
} else {
args = args[1:]
}
if len(args) > 0 {
if end, err = parseFuzzyTime(args[0]); err != nil {
y, m, d := time.Now().Date()
end = time.Date(y, m, d, 23, 59, 59, 0, time.Now().Location())
} else {
args = args[1:]
}
}
}
list := a.TimerList.GetTimersInRange(start, end)
if includeArchive {
if err = a.LoadDoneList(); err != nil {
fmt.Println("Error loading done.txt entries")
fmt.Println(err.Error())
return nil
}
*list = append(*list, (*a.DoneList.GetTimersInRange(start, end))...)
}
if len(contextFilters) > 0 {
allFilters = append(allFilters, func(t timertxt.Timer) bool {
for _, v := range contextFilters {
v = strings.TrimPrefix(v, "@")
if !t.HasContext(v) {
return false
}
}
return true
})
}
if len(projectFilters) > 0 {
allFilters = append(allFilters, func(t timertxt.Timer) bool {
for _, v := range projectFilters {
v = strings.TrimPrefix(v, "+")
if !t.HasProject(v) {
return false
}
}
return true
})
}
doFilters := func(t *timertxt.Timer) bool {
for _, v := range allFilters {
if !v(*t) {
return false
}
}
// If we made it all the way down here, it matches
return true
}
return list.Filter(doFilters)
}
func (a *AppState) SetTimerFinished(id int, end time.Time) error {
var t *timertxt.Timer
var err error
if t, err = a.TimerList.GetTimer(id); err != nil {
return err
}
t.FinishDate = end
t.Finished = true
return nil
}
func (a *AppState) addTimer(timerString string) error {
t, err := timertxt.ParseTimer(timerString)
if err != nil {
return err
}
if t.StartDate.IsZero() {
t.StartDate = time.Now()
}
a.TimerList.AddTimer(t)
return a.WriteList()
}
func (a *AppState) archiveTimer(id int) error {
var err error
var timer *timertxt.Timer
if timer, err = a.TimerList.GetTimer(id); err != nil {
return err
}
if err := a.TimerList.ArchiveTimerToFile(*timer, app.getDoneFile()); err != nil {
return err
}
a.TimerList.RemoveTimer(*timer)
return a.WriteList()
}
func (a *AppState) unarchiveTimer(id int) error {
var err error
var timer *timertxt.Timer
if timer, err = a.DoneList.GetTimer(id); err != nil {
return err
}
a.TimerList.AddTimer(timer)
if err = a.WriteList(); err != nil {
return err
}
a.DoneList.RemoveTimer(*timer)
return a.WriteDoneList()
}
func (a *AppState) LoadTimerList() error {
var err error
var tl timertxt.TimerList
tl, err = timertxt.LoadFromFilename(a.getTimerFile())
tl.Sort(timertxt.SORT_UNFINISHED_START)
a.TimerList = &tl
return err
}
func (a *AppState) WriteList() error {
return a.TimerList.WriteToFilename(a.getTimerFile())
}
func (a *AppState) LoadDoneList() error {
var err error
var tl timertxt.TimerList
tl, err = timertxt.LoadFromFilename(a.getDoneFile())
a.DoneList = &tl
return err
}
func (a *AppState) WriteDoneList() error {
return a.DoneList.WriteToFilename(a.getDoneFile())
}
func (a *AppState) getFilterPredicate(filter string) func(*timertxt.Timer) bool {
var predicates []func(*timertxt.Timer) 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 *timertxt.Timer) bool {
for _, v := range t.Contexts {
if "@"+v == part {
return true
}
}
return false
})
} else if strings.HasPrefix(part, "+") {
predicates = append(predicates, func(t *timertxt.Timer) bool {
for _, v := range t.Projects {
if "+"+v == part {
return true
}
}
return false
})
} else {
predicates = append(predicates, func(t *timertxt.Timer) bool {
val := t.Original
if !checkCase {
val = strings.ToLower(t.Original)
}
return strings.Contains(val, part)
})
}
}
return func(t *timertxt.Timer) bool {
for _, v := range predicates {
if v(t) {
return true
}
}
return false
}
}

74
old/screen.go Normal file
View File

@@ -0,0 +1,74 @@
package main
import (
"errors"
"time"
termbox "github.com/nsf/termbox-go"
)
type Screen interface {
handleKeyEvent(termbox.Event) int
initialize(Bundle) error
drawScreen()
}
const (
ScreenMain = iota
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{}
aboutScreen := AboutScreen{}
a.screens = append(a.screens, &mainScreen)
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()
}
func readUserInput(e chan termbox.Event) {
for {
e <- termbox.PollEvent()
}
}
func checkForUpdate(e chan termbox.Event) {
for {
time.Sleep(time.Minute)
// Check if the on-disk tasklist has changed
//app.LoadTasklist()
//app.LoadDoneList()
if false {
e <- termbox.Event{
Type: termbox.EventError,
Err: errors.New("List changed elsewhere"),
}
}
}
}
/*
* ViewPort helps keep track of what's being displayed on the screen
*/
type ViewPort struct {
bytesPerRow int
numberOfRows int
firstRow int
}

93
old/screen_about.go Normal file
View File

@@ -0,0 +1,93 @@
package main
import (
"fmt"
"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
titleTemplate []string
commandsCol1 []Command
commandsCol2 []Command
}
type Command struct {
key string
description string
}
func (screen *AboutScreen) initialize(bundle Bundle) error {
screen.titleTemplate = []string{
" .__ ",
" ____ |__| _____ ____ ",
" / ___\\| |/ \\_/ __ \\ ",
" / /_/ > | Y Y \\ ___/ ",
" \\___ /|__|__|_| /\\___ >",
"/_____/ \\/ \\/ ",
}
screen.commandsCol1 = []Command{
Command{"j,↓", "down"},
Command{"k,↑", "up"},
Command{"l,→", "open task"},
Command{"------", "---------"},
Command{"g", "goto top"},
Command{"G", "goto bottom"},
Command{"ctrl+f", "jump down"},
Command{"ctrl+b", "jump up"},
}
screen.commandsCol2 = []Command{
Command{"D", "archive timer to done.txt"},
Command{"------", "---------"},
Command{"?", "this screen"},
Command{"q", "quit program"},
}
return nil
}
func (screen *AboutScreen) handleKeyEvent(event termbox.Event) int {
return ScreenMain
}
func (screen *AboutScreen) drawScreen() {
width, height := termbox.Size()
xPos := (width - len(screen.titleTemplate[0])) / 2
yPos := 1
for _, line := range screen.titleTemplate {
termboxUtil.DrawStringAtPoint(line, xPos, yPos, DefaultFg, DefaultBg)
yPos++
}
numCols := 2
if width < 80 {
numCols = 1
}
col1XPos := (width - (width * 3 / 4))
col2XPos := (width - (width * 2 / 4))
if numCols == 1 {
col2XPos = col1XPos
}
screen.drawCommandsAtPoint(screen.commandsCol1, col1XPos, yPos)
screen.drawCommandsAtPoint(screen.commandsCol2, col2XPos, yPos)
exitTxt := "Press any key to return to tasks"
termboxUtil.DrawStringAtPoint(exitTxt, (width-len(exitTxt))/2, height-1, TitleFg, TitleBg)
}
func (screen *AboutScreen) drawCommandsAtPoint(commands []Command, x, y int) {
xPos, yPos := x, y
for _, cmd := range commands {
termboxUtil.DrawStringAtPoint(fmt.Sprintf("%6s", cmd.key), xPos, yPos, DefaultFg, DefaultBg)
termboxUtil.DrawStringAtPoint(cmd.description, xPos+8, yPos, DefaultFg, DefaultBg)
yPos++
}
}

327
old/screen_main.go Normal file
View File

@@ -0,0 +1,327 @@
package main
import (
"fmt"
"strings"
"time"
timertxt "git.bullercodeworks.com/brian/go-timertxt"
termboxUtil "github.com/br0xen/termbox-util"
termbox "github.com/nsf/termbox-go"
)
const (
MainBundleListKey = "mainscreen.list"
MainBundleFilterKey = "mainscreen.filter"
MainBundleListRecent = "mainscreen.list.recent"
MainBundleListArchive = "mainscreen.list.archive"
MainBackspaceNothing = iota
MainBackspaceMain
MainBackspaceFilter
InputIDFilter = "filter"
InputIDAddTimer = "add timer"
InputIDUnArchiveTask = "move timer to active list? (y/n)"
)
type MainScreen struct {
viewPort ViewPort
message string
messageTimeout time.Duration
messageTime time.Time
mode int
cursor map[string]int
inputField *termboxUtil.InputField
currentList string
currentFilter string
backspaceDoes int
displayList *timertxt.TimerList
activeList *timertxt.TimerList
}
func (screen *MainScreen) initialize(bundle Bundle) error {
width, height := termbox.Size()
screen.inputField = termboxUtil.CreateInputField(0, (height - 3), width, 1, DefaultFg, DefaultBg)
screen.cursor = make(map[string]int)
if bundle != nil {
if err := screen.reloadList(bundle); err != nil {
return err
}
screen.inputField.SetID("")
screen.inputField.SetBordered(false)
}
return nil
}
func (screen *MainScreen) reloadList(bundle Bundle) error {
screen.displayList = timertxt.NewTimerList()
screen.currentList = bundle.getString(MainBundleListKey, MainBundleListRecent)
switch screen.currentList {
case MainBundleListRecent:
screen.setActiveList(app.TimerList)
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.AddTimer(av)
break
}
}
}
} else {
for _, av := range *screen.activeList {
screen.displayList.AddTimer(av)
}
}
case MainBundleListArchive:
//screen.setActiveList(
}
if screen.cursor[screen.currentList] > len(*screen.displayList)-1 {
screen.cursor[screen.currentList] = len(*screen.displayList) - 1
}
return nil
}
func (screen *MainScreen) reloadCurrentView() {
bundle := Bundle{}
bundle.setValue(MainBundleListKey, screen.currentList)
bundle.setValue(MainBundleFilterKey, screen.currentFilter)
screen.reloadList(bundle)
}
func (screen *MainScreen) handleKeyEvent(event termbox.Event) int {
if screen.inputField.GetID() != "" {
return screen.handleInputKeyEvent(event)
}
if event.Ch == '?' {
// Go to About Screen
b := Bundle{}
if err := app.screens[ScreenAbout].initialize(b); err != nil {
screen.setMessage(err.Error())
}
return ScreenAbout
} else if event.Ch == 'g' {
screen.cursor[screen.currentList] = 0
} else if event.Ch == 'G' {
screen.cursor[screen.currentList] = len(*screen.displayList) - 1
} else if event.Key == termbox.KeyCtrlF {
// Jump forward half a screen
_, h := termbox.Size()
screen.cursor[screen.currentList] += (h / 2)
if screen.cursor[screen.currentList] >= len(*screen.displayList) {
screen.cursor[screen.currentList] = len(*screen.displayList) - 1
}
} else if event.Key == termbox.KeyCtrlB {
// Jump back half a screen
_, h := termbox.Size()
screen.cursor[screen.currentList] -= (h / 2)
if screen.cursor[screen.currentList] < 0 {
screen.cursor[screen.currentList] = 0
}
} 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 == 'L' {
return screen.toggleViewList()
} else if event.Ch == 'q' {
return ScreenExit
}
return ScreenMain
}
func (screen *MainScreen) handleInputKeyEvent(event termbox.Event) int {
switch screen.inputField.GetID() {
case InputIDFilter:
if event.Key == termbox.KeyEnter {
// Apply the filter
filter := screen.inputField.GetValue()
screen.inputField.SetID("")
screen.inputField.SetValue("")
screen.backspaceDoes = MainBackspaceFilter
screen.reloadList(screen.buildBundle(screen.currentList, filter))
return ScreenMain
}
case InputIDAddTimer:
if event.Key == termbox.KeyEnter {
// Create the new item
err := app.addTimer(screen.inputField.GetValue())
if err != nil {
screen.setMessage(err.Error())
}
screen.inputField.SetID("")
screen.inputField.SetValue("")
screen.reloadList(screen.buildBundle(screen.currentList, screen.currentFilter))
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
}
} else if event.Key == termbox.KeyEsc {
screen.reloadList(screen.buildBundle(screen.currentList, screen.currentFilter))
screen.inputField.SetID("")
screen.inputField.SetValue("")
return ScreenMain
}
screen.inputField.HandleEvent(event)
return ScreenMain
}
func (screen *MainScreen) drawScreen() {
_, height := termbox.Size()
screen.viewPort.numberOfRows = height - 1
if screen.inputField.GetID() != "" {
screen.viewPort.numberOfRows--
}
screen.viewPort.firstRow = 1
displayOffset := 0
maxCursor := screen.viewPort.numberOfRows * 2 / 3
if screen.cursor[screen.currentList] > maxCursor {
displayOffset = screen.cursor[screen.currentList] - maxCursor
}
if screen.message == "" {
screen.setMessageWithTimeout("Press '?' for help", -1)
}
screen.drawHeader()
topId := 0
for _, v := range *screen.displayList {
if v.Id > topId {
topId = v.Id
}
}
padCnt := fmt.Sprintf("%d", topId)
for k, v := range *screen.displayList {
pad := strings.Repeat(" ", len(padCnt)-len(fmt.Sprintf("%d", v.Id)))
useFg, useBg := DefaultFg, DefaultBg
if k == screen.cursor[screen.currentList] {
useFg, useBg = CursorFg, CursorBg
}
lineY := k + 1 - displayOffset
if lineY > 0 && lineY < screen.viewPort.numberOfRows {
termboxUtil.DrawStringAtPoint(pad+app.getTimerString(*v), 0, lineY, useFg, useBg)
}
}
screen.drawFooter()
}
func (screen *MainScreen) drawHeader() {
width, _ := termbox.Size()
headerString := screen.currentFilter
if headerString == "" {
if screen.currentList == MainBundleListRecent {
headerString = "Timers"
} else if screen.currentList == MainBundleListArchive {
headerString = "Timer Archive"
}
}
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()
}
width, height := termbox.Size()
if screen.inputField.GetID() != "" {
screen.inputField.SetX(len(screen.inputField.GetID()) + 2)
pad := width - len(screen.inputField.GetID()+":")
field := screen.inputField.GetID() + ":" + strings.Repeat(" ", pad)
termboxUtil.DrawStringAtPoint(field, 0, height-2, DefaultFg, DefaultBg)
screen.inputField.Draw()
}
// And the 'message'
termboxUtil.DrawStringAtPoint(screen.message, 0, height-1, DefaultFg, DefaultBg)
}
func (screen *MainScreen) toggleViewList() int {
bundle := Bundle{}
if screen.currentList == MainBundleListRecent {
bundle.setValue(MainBundleListKey, MainBundleListArchive)
screen.backspaceDoes = MainBackspaceMain
} else {
bundle.setValue(MainBundleListKey, MainBundleListRecent)
}
bundle.setValue(MainBundleFilterKey, screen.currentFilter)
screen.reloadList(bundle)
return ScreenMain
}
func (screen *MainScreen) moveCursorDown() bool {
screen.cursor[screen.currentList]++
if screen.cursor[screen.currentList] >= len(*screen.displayList) {
screen.cursor[screen.currentList] = len(*screen.displayList) - 1
return false
}
return true
}
func (screen *MainScreen) moveCursorUp() bool {
screen.cursor[screen.currentList]--
if screen.cursor[screen.currentList] < 0 {
screen.cursor[screen.currentList] = 0
return false
}
return true
}
func (screen *MainScreen) startFilter() int {
screen.inputField.SetID(InputIDFilter)
return ScreenMain
}
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 = fmt.Sprintf("%d Total Tasks", len(*screen.activeList))
screen.messageTimeout = -1
}
func (screen *MainScreen) buildBundle(list, filter string) Bundle {
bundle := Bundle{}
bundle.setValue(MainBundleListKey, list)
bundle.setValue(MainBundleFilterKey, filter)
return bundle
}
func (screen *MainScreen) setActiveList(list *timertxt.TimerList) {
screen.activeList = list
}

578
old/timer_ops.go Normal file
View File

@@ -0,0 +1,578 @@
package main
import (
"fmt"
"os"
"strconv"
"strings"
"time"
timertxt "git.bullercodeworks.com/brian/go-timertxt"
)
func (a *AppState) opStatus(args []string) int {
if len(*a.TimerList.GetActiveTimers()) == 0 {
fmt.Println("No timers running")
return 0
}
var currDur time.Duration
for _, v := range *a.TimerList {
if v.ActiveToday() {
currDur += v.Duration()
}
}
d := currDur.Round(GetRoundToDuration())
fmt.Printf("%s ( %.2f hrs )\n", time.Now().Format(time.Stamp), DurationToDecimal(d))
for _, v := range *a.TimerList.GetActiveTimers() {
fmt.Println(timerToFriendlyString(v))
}
return 0
}
/**
* Just output the time given the filters
*/
func (a *AppState) opShowTime(args []string) int {
list := a.getFilteredTimerList(args)
var isActive bool
var total time.Duration
for _, v := range *list {
dur := v.FinishDate.Sub(v.StartDate)
if v.FinishDate.IsZero() {
dur = time.Now().Sub(v.StartDate)
isActive = true
}
total += dur
}
total = total.Round(GetRoundToDuration())
if isActive {
fmt.Printf("%.2f+\n", DurationToDecimal(total))
} else {
fmt.Printf("%.2f\n", DurationToDecimal(total))
}
return 0
}
/**
* List timers for a given time span
* By default, only list Today
*/
func (a *AppState) opListTimers(args []string) int {
var includeArchive bool
var err error
start := time.Time{}
end := time.Now()
var contextFilters []string
var projectFilters []string
var allFilters []func(timertxt.Timer) bool
if len(args) > 0 {
contextFilters, args = getContextsFromSlice(args)
projectFilters, args = getProjectsFromSlice(args)
}
if len(args) > 0 {
if args[0] == "--a" {
includeArchive = true
args = args[1:]
}
}
if len(args) > 0 {
if start, err = parseFuzzyTime(args[0]); err != nil {
y, m, d := time.Now().Date()
start = time.Date(y, m, d, 0, 0, 0, 0, time.Now().Location())
} else {
args = args[1:]
}
if len(args) > 0 {
if end, err = parseFuzzyTime(args[0]); err != nil {
y, m, d := time.Now().Date()
end = time.Date(y, m, d, 23, 59, 59, 0, time.Now().Location())
} else {
args = args[1:]
}
}
}
if includeArchive {
if err = a.LoadDoneList(); err != nil {
fmt.Println("Error loading done.txt entries")
fmt.Println(err.Error())
return 1
}
}
list := a.TimerList.GetTimersInRange(start, end)
if includeArchive {
*list = append(*list, (*a.DoneList.GetTimersInRange(start, end))...)
}
if len(contextFilters) > 0 {
allFilters = append(allFilters, func(t timertxt.Timer) bool {
for _, v := range contextFilters {
v = strings.TrimPrefix(v, "@")
if !t.HasContext(v) {
return false
}
}
return true
})
}
if len(projectFilters) > 0 {
allFilters = append(allFilters, func(t timertxt.Timer) bool {
for _, v := range projectFilters {
v = strings.TrimPrefix(v, "+")
if !t.HasProject(v) {
return false
}
}
return true
})
}
doFilters := func(t *timertxt.Timer) bool {
for _, v := range allFilters {
if !v(*t) {
return false
}
}
// If we made it all the way down here, it matches
return true
}
list = list.Filter(doFilters)
dayTotals := make(map[string]time.Duration)
for _, v := range *list {
dur := v.FinishDate.Sub(v.StartDate)
if v.FinishDate.IsZero() {
dur = time.Now().Sub(v.StartDate)
}
dayTotals[v.StartDate.Format("2006/01/02")] += dur
}
var oldDayStr, dayStr string
for _, v := range *list {
oldDayStr = dayStr
dayStr = v.StartDate.Format("2006/01/02")
if dayStr != oldDayStr {
wrkDur := dayTotals[dayStr].Round(GetRoundToDuration())
fmtStr := dayStr + " ( %.2f )\n"
fmt.Printf(fmtStr, DurationToDecimal(wrkDur))
}
fmt.Println(" " + timerToFriendlyString(v))
}
return 0
}
func (a *AppState) opStartTimer(args []string) int {
var contexts, projects, strTags []string
t := timertxt.NewTimer()
if len(args) > 0 {
if start, err := parseFuzzyTime(args[0]); err == nil {
t.StartDate = start
args = args[1:]
}
}
contexts, args = getContextsFromSlice(args)
projects, args = getProjectsFromSlice(args)
strTags, args = getAdditionalTagsFromSlice(args)
for _, v := range contexts {
t.Contexts = append(t.Contexts, strings.TrimPrefix(v, "@"))
}
for _, v := range projects {
t.Projects = append(t.Projects, strings.TrimPrefix(v, "+"))
}
for _, v := range strTags {
tgPts := strings.Split(v, ":")
t.AdditionalTags[tgPts[0]] = tgPts[1]
}
a.TimerList.AddTimer(t)
if err := a.WriteList(); err != nil {
fmt.Println(err.Error())
return 1
}
fmt.Println("Started: ", TimerToString(t))
return 0
}
func (a *AppState) opToggleTimer(args []string) int {
wrk, err := a.getMostRecentTimer()
if err != nil {
fmt.Print("{\"icon\":\"time\",\"state\":\"Critical\", \"text\": \"Error loading timer entry\"}")
return 0
}
var startArgs []string
if wrk.Finished {
// Start a new timer with the same data
for _, v := range wrk.Contexts {
startArgs = append(startArgs, "@"+v)
}
for _, v := range wrk.Projects {
startArgs = append(startArgs, "+"+v)
}
for k, v := range wrk.AdditionalTags {
startArgs = append(startArgs, k+":"+v)
}
a.opStartTimer(startArgs)
} else {
// Stop the active timer
a.opStopTimer([]string{})
}
return 0
}
func (a *AppState) opStopTimer(args []string) int {
var err error
var wrk time.Time
end := time.Now()
id := -1
if len(args) > 0 {
if wrk, err = parseFuzzyTime(args[0]); err != nil {
id, err = strconv.Atoi(args[0])
} else {
end = wrk
args = args[1:]
}
}
fmt.Println("Stopping at : " + end.Format(time.RFC3339))
var timerIds []int
if id == -1 {
for _, v := range *a.TimerList.GetActiveTimers() {
timerIds = append(timerIds, v.Id)
}
} else {
timerIds = append(timerIds, id)
}
for _, v := range timerIds {
var stopped *timertxt.Timer
if stopped, err = a.TimerList.GetTimer(v); err != nil {
fmt.Println(err.Error())
}
if err = a.SetTimerFinished(v, end); err != nil {
fmt.Println(err.Error())
continue
}
fmt.Println("Stopped Timer:", timerToFriendlyString(stopped))
}
if err = a.WriteList(); err != nil {
fmt.Println(err.Error())
return 1
}
return 0
}
func (a *AppState) opSwitchTimer(args []string) int {
var timerIds []int
var err error
end := time.Now()
// Stop all running timers and start a new one with the given args
for _, v := range *a.TimerList.GetActiveTimers() {
timerIds = append(timerIds, v.Id)
}
fmt.Print("Stopping ", timerIds, "\n")
for _, v := range timerIds {
var stopped *timertxt.Timer
if stopped, err = a.TimerList.GetTimer(v); err != nil {
fmt.Println(err.Error())
continue
}
if err = a.SetTimerFinished(v, end); err != nil {
fmt.Println(err.Error())
continue
}
fmt.Println("Stopped Timer:", timerToFriendlyString(stopped))
}
return a.opStartTimer(args)
}
func (a *AppState) opArchiveTimer(args []string) int {
if len(args) > 0 {
for _, v := range args {
var id int
var timer *timertxt.Timer
var err error
if strings.HasPrefix(v, "@") {
// We're archiving by context
v = strings.TrimPrefix(v, "@")
fmt.Println("Archiving by context:", v)
timers := a.TimerList.GetTimersWithContext(v)
fmt.Println("Found timers: ", len(*timers))
for _, tmr := range *timers {
if tmr.Finished {
if err = a.archiveTimer(tmr.Id); err != nil {
fmt.Printf("Error archiving timer %d\n", tmr.Id)
continue
}
fmt.Println(a.getDoneTimerString(*tmr))
} else {
fmt.Println("Refusing to archive running timer:", tmr.Id)
}
}
} else if strings.HasPrefix(v, "+") {
// We're archiving by projcet
v = strings.TrimPrefix(v, "+")
fmt.Println("Archiving by project:", v)
timers := a.TimerList.GetTimersWithProject(v)
fmt.Println("Found timers: ", len(*timers))
for _, tmr := range *timers {
if tmr.Finished {
if err = a.archiveTimer(tmr.Id); err != nil {
fmt.Printf("Error archiving timer %d\n", tmr.Id)
continue
}
fmt.Println(a.getDoneTimerString(*tmr))
} else {
fmt.Println("Refusing to archive running timer:", tmr.Id)
}
}
} else {
// I guess we're archiving by timer id
if id, err = strconv.Atoi(v); err != nil {
fmt.Printf("Invalid id given: %s\n", v)
return 1
}
if timer, err = a.TimerList.GetTimer(id); err != nil {
fmt.Printf("Error getting timer %d\n", id)
return 1
}
if timer.Finished {
if err = a.archiveTimer(id); err != nil {
fmt.Printf("Error archiving timer %d\n", id)
return 1
}
fmt.Println(a.getDoneTimerString(*timer))
} else {
fmt.Println("Refusing to archive running timer:", timer.Id)
}
}
}
} else {
for _, v := range *a.TimerList {
if v.Finished {
if err := a.archiveTimer(v.Id); err != nil {
fmt.Printf("Error archiving task %d\n", v.Id)
return 1
}
fmt.Println(a.getDoneTimerString(*v))
}
}
}
return 0
}
func (a *AppState) opRemoveTimer(args []string) int {
if len(args) == 0 {
fmt.Println("No timer id given")
return 1
}
id, err := strconv.Atoi(args[0])
if err != nil {
fmt.Println("Invalid timer id given: " + args[0])
}
t, err := a.TimerList.GetTimer(id)
if err != nil {
fmt.Println("Error getting timer with id: " + args[0])
return 1
}
if err = a.TimerList.RemoveTimerById(id); err != nil {
fmt.Println("Error Removing Timer: " + err.Error())
return 1
}
fmt.Println("Timer removed")
fmt.Println(TimerToString(t))
if err := a.WriteList(); err != nil {
fmt.Println(err.Error())
return 1
}
return 0
}
func (a *AppState) opModifyTimer(args []string) int {
var timer *timertxt.Timer
var contexts, projects []string
id, err := strconv.Atoi(args[0])
if err != nil {
// We didn't have a timer id, so try to modify the first active timer
if len(*a.TimerList.GetActiveTimers()) > 0 {
timer = (*a.TimerList.GetActiveTimers())[0]
} else {
// And we don't have any active timers
fmt.Println("No active timers, 'id' must be provided.")
return 1
}
} else {
args = args[1:]
if timer, err = a.TimerList.GetTimer(id); err != nil {
fmt.Printf("Error getting timer %d\n", id)
return 1
}
}
var start, end time.Time
for _, v := range args {
pts := strings.Split(v, "=")
switch pts[0] {
case "beginning", "start":
if start, err = parseFuzzyTime(pts[1]); err != nil {
fmt.Println("Error parsing start time.")
return 1
}
case "stop", "finish", "end":
if end, err = parseFuzzyTime(pts[1]); err != nil {
fmt.Println("Error parsing end time.")
return 1
}
case "project", "projects":
projects = strings.Split(pts[1], ",")
case "context", "contexts":
contexts = strings.Split(pts[1], ",")
}
}
if len(contexts) > 0 {
for k := range contexts {
contexts[k] = strings.TrimPrefix(contexts[k], "@")
}
timer.Contexts = contexts
}
if len(projects) > 0 {
for k := range projects {
projects[k] = strings.TrimPrefix(projects[k], "+")
}
timer.Projects = projects
}
if !start.IsZero() {
timer.StartDate = start
}
if !end.IsZero() {
timer.FinishDate = end
timer.Finished = true
}
fmt.Println("Modified Timer:")
fmt.Println(timerToFriendlyString(timer))
if err := a.WriteList(); err != nil {
fmt.Println(err.Error())
return 1
}
return 0
}
func (a *AppState) opFuzzyParse(args []string) int {
if len(args) > 0 {
if start, err := parseFuzzyTime(args[0]); err == nil {
fmt.Println(start.Format(time.RFC3339))
} else {
fmt.Println(err.Error())
}
}
return 0
}
func (a *AppState) opConfig(args []string) int {
if len(args) == 0 {
fmt.Println("Config Keys:")
for _, v := range a.config.GetKeyList() {
fmt.Println(" " + v)
}
fmt.Println(" timerpath")
} else {
if args[0] == "timerpath" {
fmt.Println(a.directory + a.fileTimer)
} else {
fmt.Println(a.config.Get(args[0]))
}
}
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
}
func (a *AppState) opI3Status(args []string) int {
state := "Idle"
wrk, err := a.getMostRecentTimer()
if err != nil {
fmt.Print("{\"icon\":\"time\",\"state\":\"Critical\", \"text\": \"Error loading timer entry\"}")
return 0
}
var text string
if wrk.FinishDate.IsZero() {
wrkDur := wrk.Duration().Round(time.Minute * 15)
hrs := int(wrkDur.Hours())
mins := int(wrkDur.Minutes()) - hrs*60
if hrs > 0 {
text = fmt.Sprintf("%dh%dm", hrs, mins)
} else {
text = fmt.Sprintf("%dm", mins)
}
if !wrk.Finished {
// If the current time is before 7AM, after 5PM, or a weekend, use a Warning state
cTime := time.Now()
if cTime.Weekday() == time.Sunday || cTime.Weekday() == time.Saturday || cTime.Hour() < 7 || cTime.Hour() > 17 {
state = "Warning"
} else {
state = "Good"
}
}
for _, ctx := range wrk.Contexts {
text = fmt.Sprintf("%s @%s", text, ctx)
}
for _, prj := range wrk.Projects {
text = fmt.Sprintf("%s +%s", text, prj)
}
} else {
text = "("
for _, ctx := range wrk.Contexts {
text = fmt.Sprintf("%s@%s ", text, ctx)
}
for _, prj := range wrk.Projects {
text = fmt.Sprintf("%s+%s ", text, prj)
}
if text[len(text)-1] == ' ' {
text = text[:len(text)-1]
}
text = text + ")"
getListTotal := func(list *timertxt.TimerList) string {
var isActive bool
var total time.Duration
for _, v := range *list {
dur := v.FinishDate.Sub(v.StartDate)
if v.FinishDate.IsZero() {
dur = time.Now().Sub(v.StartDate)
isActive = true
}
total += dur
}
total = total.Round(GetRoundToDuration())
if isActive {
return fmt.Sprintf("%.2f+", DurationToDecimal(total))
} else {
return fmt.Sprintf("%.2f", DurationToDecimal(total))
}
}
dayList := a.getFilteredTimerList([]string{"--a", BeginningOfDay().Format("2006-01-02"), "@bcw"})
text = text + " d:" + getListTotal(dayList)
weekList := a.getFilteredTimerList([]string{"--a", BeginningOfWeek().Format("2006-01-02"), "@bcw"})
text = text + " w:" + getListTotal(weekList)
monthList := a.getFilteredTimerList([]string{"--a", BeginningOfMonth().Format("2006-01-02"), "@bcw"})
text = text + " m:" + getListTotal(monthList)
}
fmt.Printf("{\"icon\":\"time\",\"state\":\"%s\", \"text\": \"%s\"}", state, text)
return 0
}
func (a *AppState) opEditor(args []string) int {
editor := os.Getenv("EDITOR")
if editor == "" {
fmt.Println("No $EDITOR set")
return 1
}
fmt.Println(editor, a.directory+a.fileTimer)
return 0
}

58
old/ui_loop.go Normal file
View File

@@ -0,0 +1,58 @@
// +build !windows
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, MainBundleListRecent)
displayScreen.initialize(bundle)
app.layoutAndDrawScreen(displayScreen)
eventChan := make(chan termbox.Event)
go readUserInput(eventChan)
go checkForUpdate(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 {
displayScreen.initialize(nil)
app.layoutAndDrawScreen(displayScreen)
}
}
termbox.Close()
// Any wrap up should be done here...
return 0
}

48
old/ui_loop_windows.go Normal file
View File

@@ -0,0 +1,48 @@
// +build windows
package main
import (
"fmt"
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, MainBundleListRecent)
displayScreen.initialize(bundle)
app.layoutAndDrawScreen(displayScreen)
eventChan := make(chan termbox.Event)
go readUserInput(eventChan)
go checkForUpdate(eventChan)
for {
event := <-eventChan
if event.Type == termbox.EventKey {
if event.Key == termbox.KeyCtrlC {
break
}
newScreenIndex := displayScreen.handleKeyEvent(event)
if newScreenIndex < len(app.screens) {
displayScreen = app.screens[newScreenIndex]
app.layoutAndDrawScreen(displayScreen)
} else {
break
}
}
if event.Type == termbox.EventResize {
displayScreen.initialize(nil)
app.layoutAndDrawScreen(displayScreen)
}
}
termbox.Close()
// Any wrap up should be done here...
return 0
}