diff --git a/.gitignore b/.gitignore index 2b36552..486a0b9 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,7 @@ # Project *.db -config/config.yml +config/ build/ _old diff --git a/LICENSE b/LICENSE index eeb6558..cdbd6e3 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2020 Eoghan Conlon O'Neill (IdlePhysicist) +Copyright (c) 2020 Eoghan Conlon O'Neill (IdlePhysicist) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 7a42359..0a84985 100644 --- a/README.md +++ b/README.md @@ -3,8 +3,6 @@ ## Summary Cave Logger is a basic SQLite database interface written in Go, and it allows cavers to track the caves that they have been to, who with, and when. -I indend to make the code more generic to allow other outdoorsy people to use this app with less fuss. - ## What It Looks Like

@@ -35,22 +33,3 @@ To run in docker: 2. Follow step 3 from above 3. `./docker/run.sh` -## Help - -### Keybindings - -| Key | Function | -|:---:|:--------:| -| q | quit | -| n | new | -| u | update | -| d | delete | -| j | down | -| k | up | -| g | end | -| G | home | -| Tab | see menu | -| Enter | inspect record | - -### Menu -In the Menu the Tab key will select the highlighted item, and hitting Tab again will navigate to the Menu. diff --git a/assets/screenshot.png b/assets/screenshot.png index 097b2b1..6b84395 100644 Binary files a/assets/screenshot.png and b/assets/screenshot.png differ diff --git a/cmd/main.go b/cmd/main.go index 4e4ae29..0da5a20 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -1,8 +1,8 @@ package main import ( + "encoding/json" "fmt" - "gopkg.in/yaml.v2" "io/ioutil" "os" "strings" @@ -48,21 +48,21 @@ func main() { // Read config file - cfg := func (_yamlFile string) *model.Config { + cfg := func(_file string) *model.Config { var _cfg model.Config - if _yamlFile == `` { - _yamlFile = fmt.Sprintf("%s/.config/cave-logger/config.yml", os.Getenv("HOME")) + if _file == `` { + _file = fmt.Sprintf("%s/.config/cave-logger/config.json", os.Getenv("HOME")) } - yamlFile, err := ioutil.ReadFile(_yamlFile) + file, err := ioutil.ReadFile(_file) if err != nil { log.Fatalf("main.readfile: %v", err) } - err = yaml.Unmarshal(yamlFile, &_cfg) + err = json.Unmarshal(file, &_cfg) if err != nil { - log.Fatalf("main.unmarshalYAML: %v", err) + log.Fatalf("main.unmarshal: %v", err) } return &_cfg @@ -78,6 +78,7 @@ func main() { // Initialise the Gui / Tui gui := gui.New(db) + gui.ProcessColors(cfg.Colors) if err := gui.Start(); err != nil { log.Fatalf("main: Cannot start tui: %s", err) diff --git a/go.mod b/go.mod index 78cd834..753e990 100644 --- a/go.mod +++ b/go.mod @@ -14,5 +14,4 @@ require ( golang.org/x/sys v0.0.0-20190913121621-c3b328c6e5a7 // indirect golang.org/x/text v0.3.2 // indirect gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect - gopkg.in/yaml.v2 v2.2.2 ) diff --git a/internal/db/db.go b/internal/db/db.go index 31140c3..909ec2f 100644 --- a/internal/db/db.go +++ b/internal/db/db.go @@ -52,11 +52,11 @@ func (db *Database) AddTrip(date, location, names, notes string) error { if err != nil { return err } - + if err = db.conn.Begin(); err != nil { return err } - + // Insert the trip itself tripID, err := db.execute(query, params) if err != nil { @@ -163,14 +163,14 @@ func (db *Database) GetAllTrips() ([]*model.Log, error) { trips.notes AS 'notes' FROM trips, locations WHERE trips.caveid = locations.id` - + result, err := db.conn.Prepare(query) if err != nil { db.log.Errorf("db.prepare: Failed to query database", err) return nil, err } defer result.Close() - + trips := make([]*model.Log, 0) for { var stamp int64 @@ -185,7 +185,7 @@ func (db *Database) GetAllTrips() ([]*model.Log, error) { if !rowExists { break } - + err = result.Scan(&trip.ID, &stamp, &trip.Cave, &trip.Names, &trip.Notes) if err != nil { db.log.Error(err) @@ -193,9 +193,9 @@ func (db *Database) GetAllTrips() ([]*model.Log, error) { } trip.Date = time.Unix(stamp, 0).Format(date) - + // Add this formatted row to the rows map - trips = append(trips, &trip) + trips = append(trips, &trip) } return trips, err @@ -225,7 +225,7 @@ func (db *Database) GetTrip(logID string) (*model.Log, error) { //FIXME: return nil, err } defer result.Close() - + trips := make([]*model.Log, 0) for { var stamp int64 @@ -240,7 +240,7 @@ func (db *Database) GetTrip(logID string) (*model.Log, error) { //FIXME: if !rowExists { break } - + err = result.Scan(&trip.ID, &stamp, &trip.Cave, &trip.Names, &trip.Notes) if err != nil { db.log.Error(err) @@ -248,9 +248,9 @@ func (db *Database) GetTrip(logID string) (*model.Log, error) { //FIXME: } trip.Date = time.Unix(stamp, 0).Format(date) - + // Add this formatted row to the rows map - trips = append(trips, &trip) + trips = append(trips, &trip) } return trips[0], err @@ -281,7 +281,7 @@ func (db *Database) GetAllPeople() ([]*model.Caver, error) { cavers := make([]*model.Caver, 0) for { var c model.Caver - + rowExists, err := result.Step() if err != nil { db.log.Errorf("db.get: Step error: %s", err) @@ -291,7 +291,7 @@ func (db *Database) GetAllPeople() ([]*model.Caver, error) { if !rowExists { break } - + err = result.Scan(&c.ID, &c.Name, &c.Club, &c.Count) if err != nil { db.log.Errorf("Scan: %v", err) @@ -321,7 +321,7 @@ func (db *Database) GetTopPeople() ([]*model.Statistic, error) { cavers := make([]*model.Statistic, 0) for { var c model.Statistic - + rowExists, err := result.Step() if err != nil { db.log.Errorf("db.get: Step error: %s", err) @@ -331,7 +331,7 @@ func (db *Database) GetTopPeople() ([]*model.Statistic, error) { if !rowExists { break } - + err = result.Scan(&c.Name, &c.Value) if err != nil { db.log.Errorf("Scan: %v", err) @@ -363,7 +363,7 @@ func (db *Database) GetPerson(personID string) (*model.Caver, error) { return nil, err } defer result.Close() - + people := make([]*model.Caver, 0) for { var person model.Caver @@ -377,7 +377,7 @@ func (db *Database) GetPerson(personID string) (*model.Caver, error) { if !rowExists { break } - + err = result.Scan(&person.ID, &person.Name, &person.Club, &person.Count) if err != nil { db.log.Error(err) @@ -385,7 +385,7 @@ func (db *Database) GetPerson(personID string) (*model.Caver, error) { } // Add this formatted row to the rows map - people = append(people, &person) + people = append(people, &person) } return people[0], err @@ -418,7 +418,7 @@ func (db *Database) GetAllLocations() ([]*model.Cave, error) { caves := make([]*model.Cave, 0) for { var c model.Cave - + rowExists, err := result.Step() if err != nil { db.log.Errorf("db.get: Step error: %s", err) @@ -428,7 +428,7 @@ func (db *Database) GetAllLocations() ([]*model.Cave, error) { if !rowExists { break } - + err = result.Scan(&c.ID, &c.Name, &c.Region, &c.Country, &c.SRT, &c.Visits) if err != nil { db.log.Errorf("Scan: %v", err) @@ -458,7 +458,7 @@ func (db *Database) GetTopLocations() ([]*model.Statistic, error) { stats := make([]*model.Statistic, 0) for { var s model.Statistic - + rowExists, err := result.Step() if err != nil { db.log.Errorf("db.get: Step error: %s", err) @@ -468,7 +468,7 @@ func (db *Database) GetTopLocations() ([]*model.Statistic, error) { if !rowExists { break } - + err = result.Scan(&s.Name, &s.Value) if err != nil { db.log.Errorf("Scan: %v", err) @@ -500,7 +500,7 @@ func (db *Database) GetLocation(caveID string) (*model.Cave, error) { return nil, err } defer result.Close() - + caves := make([]*model.Cave, 0) for { //var caverIDstr string @@ -516,7 +516,7 @@ func (db *Database) GetLocation(caveID string) (*model.Cave, error) { if !rowExists { break } - + err = result.Scan(&cave.ID, &cave.Name, &cave.Region, &cave.Country, &cave.SRT, &cave.Visits) if err != nil { db.log.Error(err) @@ -524,7 +524,7 @@ func (db *Database) GetLocation(caveID string) (*model.Cave, error) { } // Add this formatted row to the rows map - caves = append(caves, &cave) + caves = append(caves, &cave) } return caves[0], err @@ -538,7 +538,7 @@ func (db *Database) RemoveTrip(id string) error { if err := db.conn.Begin(); err != nil { return err } - + // Delete trip entry err := db.conn.Exec(`DELETE FROM trips WHERE id = ?`, id) if err != nil { @@ -571,7 +571,7 @@ func (db *Database) RemovePerson(id string) error { if err := db.conn.Begin(); err != nil { return err } - + // Delete trip entry err := db.conn.Exec(`DELETE FROM people WHERE id = ?`, id) if err != nil { @@ -596,7 +596,7 @@ func (db *Database) RemoveLocation(id string) error { if err := db.conn.Begin(); err != nil { return err } - + // Delete trip entry err := db.conn.Exec(`DELETE FROM locations WHERE id = ?`, id) if err != nil { @@ -627,13 +627,13 @@ func (db *Database) ModifyTrip(id, date, location, names, notes string) error { if err != nil { return err } - + params = append(params, id) if err = db.conn.Begin(); err != nil { return err } - + // Update the trip itself _, err = db.execute(query, params) if err != nil { @@ -650,7 +650,7 @@ func (db *Database) ModifyTrip(id, date, location, names, notes string) error { panic(rb_err) } return err - } + } _, err = db.execute(db.addTripGroups(id, caverIDs)) if err != nil { diff --git a/internal/gui/cavers.go b/internal/gui/cavers.go index e4c5013..61b02f6 100644 --- a/internal/gui/cavers.go +++ b/internal/gui/cavers.go @@ -2,6 +2,7 @@ package gui import ( "strconv" + "strings" "time" "github.com/gdamore/tcell" @@ -13,7 +14,7 @@ import ( type cavers struct { *tview.Table cavers chan *model.Caver - filterWord string + filterCol, filterTerm string } func newCavers(g *Gui) *cavers { @@ -21,7 +22,7 @@ func newCavers(g *Gui) *cavers { Table: tview.NewTable().SetSelectable(true, false).Select(0,0).SetFixed(1,1), } - cavers.SetTitle(` Cavers `).SetTitleAlign(tview.AlignLeft) + cavers.SetTitle(``).SetTitleAlign(tview.AlignLeft) cavers.SetBorder(true) cavers.setEntries(g) cavers.setKeybinding(g) @@ -29,7 +30,7 @@ func newCavers(g *Gui) *cavers { } func (c *cavers) name() string { - return `people` + return `cavers` } func (c *cavers) setKeybinding(g *Gui) { @@ -37,10 +38,8 @@ func (c *cavers) setKeybinding(g *Gui) { g.setGlobalKeybinding(event) switch event.Key() { - case tcell.KeyEnter: - g.inspectPerson() - case tcell.KeyTAB: - g.switchPanel(`menu`) + case tcell.KeyCtrlR: + c.setEntries(g) } switch event.Rune() { @@ -71,25 +70,25 @@ func (c *cavers) setEntries(g *Gui) { Text: header, NotSelectable: true, Align: tview.AlignLeft, - Color: tcell.ColorWhite, - BackgroundColor: tcell.ColorDefault, + Color: tview.Styles.PrimaryTextColor, + BackgroundColor: tview.Styles.PrimitiveBackgroundColor, Attributes: tcell.AttrBold, }) } for i, caver := range g.state.resources.people { table.SetCell(i+1, 0, tview.NewTableCell(caver.Name). - SetTextColor(tcell.ColorWhite). + SetTextColor(tview.Styles.PrimaryTextColor). SetMaxWidth(30). SetExpansion(1)) table.SetCell(i+1, 1, tview.NewTableCell(caver.Club). - SetTextColor(tcell.ColorWhite). + SetTextColor(tview.Styles.PrimaryTextColor). SetMaxWidth(0). SetExpansion(1)) table.SetCell(i+1, 2, tview.NewTableCell(strconv.FormatInt(caver.Count, 10)). - SetTextColor(tcell.ColorWhite). + SetTextColor(tview.Styles.PrimaryTextColor). SetMaxWidth(0). SetExpansion(1)) } @@ -107,7 +106,14 @@ func (c *cavers) entries(g *Gui) { return } - g.state.resources.people = cavers + var filteredCavers []*model.Caver + for _, caver := range cavers { + if c.search(caver) { + continue + } + filteredCavers = append(filteredCavers, caver) + } + g.state.resources.people = filteredCavers } func (c *cavers) focus(g *Gui) { @@ -119,12 +125,13 @@ func (c *cavers) unfocus() { c.SetSelectable(false, false) } -func (c *cavers) setFilterWord(word string) { - c.filterWord = word +func (c *cavers) setFilter(col, term string) { + c.filterCol = col + c.filterTerm = term } func (c *cavers) monitoringCavers(g *Gui) { - ticker := time.NewTicker(5 * time.Second) + ticker := time.NewTicker(5 * time.Minute) LOOP: for { @@ -151,3 +158,20 @@ func (g *Gui) uniqueClubs(input []*model.Caver) []string { return uniq } + +func (c *cavers) search(caver *model.Caver) bool { + switch c.filterCol { + case "name", "": + if strings.Index(strings.ToLower(caver.Name), c.filterTerm) == -1 { + return true + } + return false + case "club": + if strings.Index(strings.ToLower(caver.Club), c.filterTerm) == -1 { + return true + } + return false + default: + return false + } +} diff --git a/internal/gui/caves.go b/internal/gui/caves.go index 637f043..dfcb398 100644 --- a/internal/gui/caves.go +++ b/internal/gui/caves.go @@ -2,6 +2,7 @@ package gui import ( "strconv" + "strings" "time" "github.com/gdamore/tcell" @@ -13,7 +14,7 @@ import ( type caves struct { *tview.Table caves chan *model.Cave - filterWord string + filterCol, filterTerm string } func newCaves(g *Gui) *caves { @@ -21,7 +22,7 @@ func newCaves(g *Gui) *caves { Table: tview.NewTable().SetSelectable(true, false).Select(0,0).SetFixed(1,1), } - caves.SetTitle(` Caves `).SetTitleAlign(tview.AlignLeft) + caves.SetTitle(``).SetTitleAlign(tview.AlignLeft) caves.SetBorder(true) caves.setEntries(g) caves.setKeybinding(g) @@ -29,7 +30,7 @@ func newCaves(g *Gui) *caves { } func (c *caves) name() string { - return `locations` + return `caves` } func (c *caves) setKeybinding(g *Gui) { @@ -37,10 +38,8 @@ func (c *caves) setKeybinding(g *Gui) { g.setGlobalKeybinding(event) switch event.Key() { - case tcell.KeyEnter: - g.inspectCave() - case tcell.KeyTAB: - g.switchPanel(`menu`) + case tcell.KeyCtrlR: + c.setEntries(g) } switch event.Rune() { @@ -62,7 +61,14 @@ func (c *caves) entries(g *Gui) { return } - g.state.resources.locations = caves + var filteredCaves []*model.Cave + for _, cave := range caves { + if c.search(cave) { + continue + } + filteredCaves = append(filteredCaves, cave) + } + g.state.resources.locations = filteredCaves } func (c *caves) setEntries(g *Gui) { @@ -82,35 +88,35 @@ func (c *caves) setEntries(g *Gui) { Text: header, NotSelectable: true, Align: tview.AlignLeft, - Color: tcell.ColorWhite, - BackgroundColor: tcell.ColorDefault, + Color: tview.Styles.PrimaryTextColor, + BackgroundColor: tview.Styles.PrimitiveBackgroundColor, Attributes: tcell.AttrBold, }) } for i, cave := range g.state.resources.locations { table.SetCell(i+1, 0, tview.NewTableCell(cave.Name). - SetTextColor(tcell.ColorWhite). + SetTextColor(tview.Styles.PrimaryTextColor). SetMaxWidth(30). SetExpansion(1)) table.SetCell(i+1, 1, tview.NewTableCell(cave.Region). - SetTextColor(tcell.ColorWhite). + SetTextColor(tview.Styles.PrimaryTextColor). SetMaxWidth(30). SetExpansion(1)) table.SetCell(i+1, 2, tview.NewTableCell(cave.Country). - SetTextColor(tcell.ColorWhite). + SetTextColor(tview.Styles.PrimaryTextColor). SetMaxWidth(0). SetExpansion(1)) - table.SetCell(i+1, 3, tview.NewTableCell(strconv.FormatBool(cave.SRT)). - SetTextColor(tcell.ColorWhite). + table.SetCell(i+1, 3, tview.NewTableCell(yesOrNo(cave.SRT)). + SetTextColor(tview.Styles.PrimaryTextColor). SetMaxWidth(0). SetExpansion(1)) table.SetCell(i+1, 4, tview.NewTableCell(strconv.FormatInt(cave.Visits, 10)). - SetTextColor(tcell.ColorWhite). + SetTextColor(tview.Styles.PrimaryTextColor). SetMaxWidth(0). SetExpansion(1)) } @@ -131,12 +137,13 @@ func (c *caves) unfocus() { c.SetSelectable(false, false) } -func (c *caves) setFilterWord(word string) { - c.filterWord = word +func (c *caves) setFilter(col, term string) { + c.filterCol = col + c.filterTerm = term } func (c *caves) monitoringCaves(g *Gui) { - ticker := time.NewTicker(5 * time.Second) + ticker := time.NewTicker(5 * time.Minute) LOOP: for { @@ -177,3 +184,33 @@ func (g *Gui) uniqueCountry(input []*model.Cave) []string { return uniq } + +func yesOrNo(val bool) string { + if val { + return `Y` + } else { + return `N` + } +} + +func (c *caves) search(cave *model.Cave) bool { + switch c.filterCol { + case "name", "": + if strings.Index(strings.ToLower(cave.Name), c.filterTerm) == -1 { + return true + } + return false + case "region": + if strings.Index(strings.ToLower(cave.Region), c.filterTerm) == -1 { + return true + } + return false + case "country": + if strings.Index(strings.ToLower(cave.Country), c.filterTerm) == -1 { + return true + } + return false + default: + return false + } +} diff --git a/internal/gui/cud.go b/internal/gui/cud.go index 9ee1159..a61f42e 100644 --- a/internal/gui/cud.go +++ b/internal/gui/cud.go @@ -47,8 +47,7 @@ func (g *Gui) createTripForm() { g.closeAndSwitchPanel("form", "trips") }) - g.pages.AddAndSwitchToPage("form", g.modal(form, 80, 29), true)//.ShowPage("main") - //REVIEW: main or trips ? ^^ + g.pages.AddAndSwitchToPage("form", g.modal(form, 80, 29), true) } func (g *Gui) createTrip(form *tview.Form) { @@ -64,9 +63,7 @@ func (g *Gui) createTrip(form *tview.Form) { } g.closeAndSwitchPanel(`form`, `trips`) - g.app.QueueUpdateDraw(func() { - g.tripsPanel().setEntries(g) - }) + g.tripsPanel().updateEntries(g) } func (g *Gui) createLocationForm() { @@ -116,11 +113,10 @@ func (g *Gui) createLocationForm() { g.createLocation(form) }). AddButton("Cancel", func() { - g.closeAndSwitchPanel("form", "locations") + g.closeAndSwitchPanel("form", "caves") }) - g.pages.AddAndSwitchToPage("form", g.modal(form, 80, 29), true)//.ShowPage("main") - //REVIEW: main or trips ? ^^ + g.pages.AddAndSwitchToPage("form", g.modal(form, 80, 29), true) } func (g *Gui) createLocation(form *tview.Form) { @@ -135,10 +131,8 @@ func (g *Gui) createLocation(form *tview.Form) { return } - g.closeAndSwitchPanel(`form`, `locations`) - g.app.QueueUpdateDraw(func() { - g.locationsPanel().setEntries(g) - }) + g.closeAndSwitchPanel(`form`, `caves`) + g.cavesPanel().updateEntries(g) } func (g *Gui) createPersonForm() { @@ -162,7 +156,7 @@ func (g *Gui) createPersonForm() { } return - }) + }) form. AddInputField("Name", "", inputWidth, nil, nil). @@ -171,11 +165,10 @@ func (g *Gui) createPersonForm() { g.createPerson(form) }). AddButton("Cancel", func() { - g.closeAndSwitchPanel("form", "people") + g.closeAndSwitchPanel("form", "cavers") }) g.pages.AddAndSwitchToPage("form", g.modal(form, 80, 29), true) - //REVIEW: main or trips ? ^^ } func (g *Gui) createPerson(form *tview.Form) { @@ -188,10 +181,8 @@ func (g *Gui) createPerson(form *tview.Form) { return } - g.closeAndSwitchPanel(`form`, `people`) - g.app.QueueUpdateDraw(func() { - g.peoplePanel().setEntries(g) - }) + g.closeAndSwitchPanel(`form`, `cavers`) + g.caversPanel().updateEntries(g) } // @@ -257,9 +248,7 @@ func (g *Gui) modifyTrip(id string, form *tview.Form) { } g.closeAndSwitchPanel(`form`, `trips`) - g.app.QueueUpdateDraw(func() { - g.tripsPanel().setEntries(g) - }) + g.tripsPanel().updateEntries(g) } @@ -289,7 +278,7 @@ func (g *Gui) modifyPersonForm() { } return - }) + }) form. AddInputField("Name", selectedPerson.Name, inputWidth, nil, nil). @@ -298,7 +287,7 @@ func (g *Gui) modifyPersonForm() { g.modifyPerson(selectedPerson.ID, form) }). AddButton("Cancel", func() { - g.closeAndSwitchPanel("form", "people") + g.closeAndSwitchPanel("form", "cavers") }) g.pages.AddAndSwitchToPage("form", g.modal(form, 80, 29), true) @@ -315,10 +304,8 @@ func (g *Gui) modifyPerson(id string, form *tview.Form) { return } - g.closeAndSwitchPanel(`form`, `people`) - g.app.QueueUpdateDraw(func() { - g.peoplePanel().updateEntries(g)//setEntries(g) - }) + g.closeAndSwitchPanel(`form`, `cavers`) + g.caversPanel().updateEntries(g) } @@ -376,7 +363,7 @@ func (g *Gui) modifyLocationForm() { g.modifyLocation(selectedLocation.ID, form) }). AddButton("Cancel", func() { - g.closeAndSwitchPanel("form", "locations") + g.closeAndSwitchPanel("form", "caves") }) g.pages.AddAndSwitchToPage("form", g.modal(form, 80, 29), true) @@ -395,10 +382,8 @@ func (g *Gui) modifyLocation(id string, form *tview.Form) { return } - g.closeAndSwitchPanel(`form`, `locations`) - g.app.QueueUpdateDraw(func() { - g.locationsPanel().setEntries(g) - }) + g.closeAndSwitchPanel(`form`, `caves`) + g.cavesPanel().updateEntries(g) } @@ -434,7 +419,7 @@ func (g *Gui) deleteLocation() { g.warning(err.Error(), `form`, []string{`OK`}, func() {return}) return } - g.locationsPanel().updateEntries(g) + g.cavesPanel().updateEntries(g) }) } @@ -451,6 +436,6 @@ func (g *Gui) deletePerson() { g.warning(err.Error(), `form`, []string{`OK`}, func() {return}) return } - g.peoplePanel().updateEntries(g) + g.caversPanel().updateEntries(g) }) } diff --git a/internal/gui/gui.go b/internal/gui/gui.go index a22f52e..6ebc0c4 100644 --- a/internal/gui/gui.go +++ b/internal/gui/gui.go @@ -1,10 +1,12 @@ package gui import ( - //"context" - //"fmt" + "fmt" + "strconv" + "strings" "github.com/rivo/tview" + "github.com/gdamore/tcell" "github.com/idlephysicist/cave-logger/internal/db" "github.com/idlephysicist/cave-logger/internal/model" @@ -19,15 +21,13 @@ type resources struct { trips []*model.Log people []*model.Caver locations []*model.Cave - statsLocations []*model.Statistic - statsPeople []*model.Statistic - timeWindow []*model.Statistic - menu []string + //statsLocations []*model.Statistic } type state struct { panels panels - navigate *navigate + navigate *navigate + tabBar *tview.TextView resources resources stopChans map[string]chan int } @@ -39,12 +39,11 @@ func newState() *state { } type Gui struct { - app *tview.Application - pages *tview.Pages - state *state - db *db.Database - statsLocations *statsLocations - statsPeople *statsPeople + app *tview.Application + pages *tview.Pages + state *state + db *db.Database + //statsLocations *statsLocations } func New(db *db.Database) *Gui { @@ -56,6 +55,38 @@ func New(db *db.Database) *Gui { } } +func (g *Gui) ProcessColors(colors map[string]string) { + for color, hex := range colors { + if hex == "" { + continue + } + switch color { + case "primitiveBackground": + tview.Styles.PrimitiveBackgroundColor = tcell.GetColor(hex) + case "contrastBackground": + tview.Styles.ContrastBackgroundColor = tcell.GetColor(hex) + case "moreContrastBackground": + tview.Styles.MoreContrastBackgroundColor = tcell.GetColor(hex) + case "border": + tview.Styles.BorderColor = tcell.GetColor(hex) + case "title": + tview.Styles.TitleColor = tcell.GetColor(hex) + case "graphics": + tview.Styles.GraphicsColor = tcell.GetColor(hex) + case "primaryText": + tview.Styles.PrimaryTextColor = tcell.GetColor(hex) + case "secondaryText": + tview.Styles.SecondaryTextColor = tcell.GetColor(hex) + case "tertiaryText": + tview.Styles.TertiaryTextColor = tcell.GetColor(hex) + case "inverseText": + tview.Styles.InverseTextColor = tcell.GetColor(hex) + case "contrastSecondaryText": + tview.Styles.ContrastSecondaryTextColor = tcell.GetColor(hex) + } + } +} + // Start start application func (g *Gui) Start() error { g.initPanels() @@ -84,34 +115,25 @@ func (g *Gui) tripsPanel() *trips { return nil } -func (g *Gui) locationsPanel() *caves { +func (g *Gui) cavesPanel() *caves { for _, panel := range g.state.panels.panel { - if panel.name() == `locations` { + if panel.name() == `caves` { return panel.(*caves) } } return nil } -func (g *Gui) peoplePanel() *cavers { +func (g *Gui) caversPanel() *cavers { for _, panel := range g.state.panels.panel { - if panel.name() == `people` { + if panel.name() == `cavers` { return panel.(*cavers) } } return nil } -func (g *Gui) inspectorPanel() *inspector { - for _, panel := range g.state.panels.panel { - if panel.name() == `inspector` { - return panel.(*inspector) - } - } - return nil -} - -func (g *Gui) statsLocationsPanel() *statsLocations { +/*func (g *Gui) statsLocationsPanel() *statsLocations { for _, panel := range g.state.panels.panel { if panel.name() == `statsLocations` { return panel.(*statsLocations) @@ -119,65 +141,55 @@ func (g *Gui) statsLocationsPanel() *statsLocations { } return nil } - -func (g *Gui) statsPeoplePanel() *statsPeople { - for _, panel := range g.state.panels.panel { - if panel.name() == `statsPeople` { - return panel.(*statsPeople) - } - } - return nil -} +*/ func (g *Gui) initPanels() { + + g.state.tabBar = newTabBar(g) + // Page definitions trips := newTrips(g) cavers := newCavers(g) caves := newCaves(g) + /* + // NOTE: I would really like to get this working as it would be far neater. + // The issue is with the three pages being of different types. + // cannot use pg (type panel) as type tview.Primitive in argument to g.pages.AddPage: + // panel does not implement tview.Primitive (missing Blur method) + for idx, pg := range []panel{trips, cavers, caves} { + name := pg.name() + g.pages.AddPage(name, pg, true, idx == 0) + fmt.Fprintf(g.state.tabBar, ` %d ["%d"][darkcyan]%s[white][""] `, idx+1, idx, strings.Title(name)) + } + g.state.tabBar.Highlight("0") + */ + // Add pages to the "book" g.pages.AddPage(`trips`, trips, true, true) - g.pages.AddPage(`people`, cavers, true, true) - g.pages.AddPage(`locations`, caves, true, true) - + fmt.Fprintf(g.state.tabBar, ` ["%d"]%d %s[""] `, 0, 1, strings.Title(trips.name())) + g.pages.AddPage(`cavers`, cavers, true, true) + fmt.Fprintf(g.state.tabBar, ` ["%d"]%d %s[""] `, 1, 2, strings.Title(cavers.name())) + g.pages.AddPage(`caves`, caves, true, true) + fmt.Fprintf(g.state.tabBar, ` ["%d"]%d %s[""] `, 2, 3, strings.Title(caves.name())) + + g.state.tabBar.Highlight("0") + // Panels - menu := newMenu(g) - statsPeople := newStatsPeople(g) - statsLocations := newStatsLocations(g) - timeWindow := newTimeWindow(g) - inspector := newInspector(g) - navigate := newNavigate() + statusBar := newNavigate() g.state.panels.panel = append(g.state.panels.panel, trips) g.state.panels.panel = append(g.state.panels.panel, cavers) g.state.panels.panel = append(g.state.panels.panel, caves) - g.state.panels.panel = append(g.state.panels.panel, menu) - g.state.panels.panel = append(g.state.panels.panel, statsPeople) - g.state.panels.panel = append(g.state.panels.panel, statsLocations) - g.state.panels.panel = append(g.state.panels.panel, timeWindow) - g.state.panels.panel = append(g.state.panels.panel, inspector) - g.state.navigate = navigate + g.state.navigate = statusBar // Arange the windows / tiles - layout := tview.NewFlex().SetDirection(tview.FlexColumn). - AddItem(tview.NewFlex(). - SetDirection(tview.FlexRow). - AddItem(menu, 0, 5, false). - AddItem(statsPeople, 0, 20, false). - AddItem(statsLocations, 0, 20, false). - AddItem(timeWindow, 0, 2, false), - 0, 1, false). - AddItem(tview.NewFlex(). - SetDirection(tview.FlexRow). - AddItem(g.pages, 0, 16, true). - AddItem(inspector, 0, 8, false). - AddItem(navigate, 0, 1, false), - 0, 6, true) - - g.statsPeople = statsPeople - g.statsLocations = statsLocations + layout := tview.NewFlex().SetDirection(tview.FlexRow). + AddItem(g.state.tabBar, 1, 1, false). + AddItem(g.pages, 0, 16, true). + AddItem(statusBar, 1, 1, false) g.app.SetRoot(layout, true) g.goTo(`trips`) @@ -194,6 +206,7 @@ func (g *Gui) switchPanel(panelName string) { g.state.navigate.update(panelName) panel.focus(g) g.state.panels.currentPanel = i + g.state.tabBar.Highlight(strconv.Itoa(i)).ScrollToHighlight() } else { panel.unfocus() } @@ -204,9 +217,9 @@ func (g *Gui) closeAndSwitchPanel(removePanel, switchTo string) { g.pages.RemovePage(removePanel).ShowPage("main") num := 0 switch switchTo { - case `people`: + case `cavers`: num = 1 - case `locations`: + case `caves`: num = 2 default: num = 0 @@ -239,6 +252,10 @@ func (g *Gui) warning(message, page string, labels []string, doneFunc func()) { g.pages.AddAndSwitchToPage("modal", g.modal(modal, 80, 29), true) } + +// +// Functions for returning the selected item in the table +// REVIEW: There might be better ways of doing this. func (g *Gui) selectedTrip() *model.Log { row, _ := g.tripsPanel().GetSelection() if len(g.state.resources.trips) == 0 { @@ -252,7 +269,7 @@ func (g *Gui) selectedTrip() *model.Log { } func (g *Gui) selectedLocation() *model.Cave { - row, _ := g.locationsPanel().GetSelection() + row, _ := g.cavesPanel().GetSelection() if len(g.state.resources.locations) == 0 { return nil } @@ -264,7 +281,7 @@ func (g *Gui) selectedLocation() *model.Cave { } func (g *Gui) selectedPerson() *model.Caver { - row, _ := g.peoplePanel().GetSelection() + row, _ := g.caversPanel().GetSelection() if len(g.state.resources.people) == 0 { return nil } diff --git a/internal/gui/inspector.go b/internal/gui/inspector.go index 3fd9351..e7b8341 100644 --- a/internal/gui/inspector.go +++ b/internal/gui/inspector.go @@ -1,53 +1,90 @@ package gui import ( + "fmt" + "github.com/rivo/tview" + "github.com/gdamore/tcell" + + "github.com/idlephysicist/cave-logger/internal/model" ) -type inspector struct { - *tview.TextView +var inspectorFormat = map[string]string{ + `trips` : "\tDate: %s\n\tCave: %s\n\tCavers: %s\n\tNotes: %s", + `cavers`: "\tName: %s\n\tClub: %s\n\tCount: %d", + `caves` : "\tName: %s\n\tRegion: %s\n\tCountry: %s\n\tSRT: %v\n\tVisits: %d", } -func newInspector(g *Gui) (insp *inspector) { - insp = &inspector{ - //Frame: tview.NewFrame(tview.NewTextView()),//.SetBorder(true).SetTitle(" Inspector "), - TextView: tview.NewTextView(), - } +func (g *Gui) displayInspect(data, page string) { + text := tview.NewTextView() + text.SetTitle(" Detail ").SetTitleAlign(tview.AlignLeft) + text.SetBorder(true) + text.SetText(data) - insp.SetTitle(` Inspector `).SetTitleAlign(tview.AlignLeft) - insp.SetBorder(true) - insp.setInitEntry() - return -} + text.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { + if event.Key() == tcell.KeyEsc || event.Rune() == 'q' { + g.closeAndSwitchPanel("detail", page) + } + return event + }) -func (i *inspector) name() string { - return `inspector` + g.pages.AddAndSwitchToPage("detail", text, true) } -func (i *inspector) setEntry(text string) { - i.SetText(text) +// +// INSPECTION FUNCS +// + +func (g *Gui) inspectTrip() { + selected := g.selectedTrip() + + if selected == nil { + g.warning("No trips in table", `trips`, []string{`OK`}, func() {return}) + return + } + + trip, err := g.db.GetTrip(selected.ID) + if err != nil { + return + } + + g.displayInspect(g.formatTrip(trip), "trips") } -func (i *inspector) setKeybinding(g *Gui) {} +/*func (g *Gui) inspectCave() { + selected := g.selectedLocation() -func (i *inspector) setEntries(g *Gui) {} + cave, err := g.db.GetLocation(selected.ID) + if err != nil { + return + } -func (i *inspector) setInitEntry() { - i.SetText(` - __ __ __ __ - | ||_ | / / \|\/||_ - |/\||__|__\__\__/| ||__ `) + g.inspectorPanel().setEntry(g.formatCave(cave)) } -func (i *inspector) entries(g *Gui) {} +func (g *Gui) inspectPerson() { + selected := g.selectedPerson() -func (i *inspector) updateEntries(g *Gui) {} + caver, err := g.db.GetPerson(selected.ID) + if err != nil { + return + } + + g.inspectorPanel().setEntry(g.formatPerson(caver)) +}*/ -func (i *inspector) focus(g *Gui) { - g.app.SetFocus(i) +// +// Formatting Functions +// +func (g *Gui) formatTrip(trip *model.Log) string { + return fmt.Sprintf(inspectorFormat[`trips`], trip.Date, trip.Cave, trip.Names, trip.Notes) } -func (i *inspector) unfocus() { +/*func (g *Gui) formatCave(l *model.Cave) string { + return fmt.Sprintf(inspectorFormat[`caves`], l.Name, l.Region, l.Country, l.SRT, l.Visits) } -func (i *inspector) setFilterWord(word string) {} \ No newline at end of file +func (g *Gui) formatPerson(p *model.Caver) string { + return fmt.Sprintf(inspectorFormat[`cavers`], p.Name, p.Club, p.Count) +}*/ + diff --git a/internal/gui/keybindings.go b/internal/gui/keybindings.go index 5327ae5..abfd4ac 100644 --- a/internal/gui/keybindings.go +++ b/internal/gui/keybindings.go @@ -1,75 +1,73 @@ package gui import ( - "fmt" + "strings" "github.com/gdamore/tcell" - "github.com/idlephysicist/cave-logger/internal/model" + "github.com/rivo/tview" ) -var inspectorFormat = map[string]string{ - `trips` : "Date: %s\nCave: %s\nCavers: %s\nNotes: %s", - `people` : "Name: %s\nClub: %s\nCount: %d", - `locations`: "Name: %s\nRegion: %s\nCountry: %s\nSRT: %v\nVisits: %d", -} - func (g *Gui) setGlobalKeybinding(event *tcell.EventKey) { + /*switch event.Key() { + case tcell.KeyTAB: + g.nextPage() + }*/ + switch event.Rune() { case 'q': g.Stop() + case '1': + g.goTo("trips") + case '2': + g.goTo("cavers") + case '3': + g.goTo("caves") + case '/': + g.filter() } } -// -// INSPECTION FUNCS -// - -func (g *Gui) inspectTrip() { - selected := g.selectedTrip() - - trip, err := g.db.GetTrip(selected.ID) - if err != nil { - return - } - - g.inspectorPanel().setEntry(g.formatTrip(trip)) -} - -func (g *Gui) inspectCave() { - selected := g.selectedLocation() - - cave, err := g.db.GetLocation(selected.ID) - if err != nil { - return - } - - g.inspectorPanel().setEntry(g.formatCave(cave)) -} +func (g *Gui) filter() { + currentPanel := g.state.panels.panel[g.state.panels.currentPanel] + currentPanel.setFilter("", "") + currentPanel.updateEntries(g) -func (g *Gui) inspectPerson() { - selected := g.selectedPerson() + viewName := "filter" + searchInput := tview.NewInputField().SetLabel("Column/Parameter") + searchInput.SetLabelWidth(17) + searchInput.SetTitle(" Filter ") + searchInput.SetTitleAlign(tview.AlignLeft) + searchInput.SetBorder(true) - caver, err := g.db.GetPerson(selected.ID) - if err != nil { - return + closeSearchInput := func() { + g.closeAndSwitchPanel(viewName, g.state.panels.panel[g.state.panels.currentPanel].name()) } - g.inspectorPanel().setEntry(g.formatPerson(caver)) -} - -// -// Formatting Functions -// -func (g *Gui) formatTrip(trip *model.Log) string { - return fmt.Sprintf(inspectorFormat[`trips`], trip.Date, trip.Cave, trip.Names, trip.Notes) -} - -func (g *Gui) formatCave(l *model.Cave) string { - return fmt.Sprintf(inspectorFormat[`locations`], l.Name, l.Region, l.Country, l.SRT, l.Visits) -} - -func (g *Gui) formatPerson(p *model.Caver) string { - return fmt.Sprintf(inspectorFormat[`people`], p.Name, p.Club, p.Count) + searchInput.SetDoneFunc(func(key tcell.Key) { + if key == tcell.KeyEnter { + closeSearchInput() + } + }) + + searchInput.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { + if event.Key() == tcell.KeyEsc { + closeSearchInput() + } + return event + }) + + searchInput.SetChangedFunc(func(text string) { + if strings.Contains(text, "/") { + textSl := strings.Split(strings.ToLower(text), "/") + + if len(textSl) == 2 { + currentPanel.setFilter(textSl[0], textSl[1]) + currentPanel.updateEntries(g) + } + } + }) + + g.pages.AddAndSwitchToPage(viewName, g.modal(searchInput, 80, 3), true).ShowPage("main") } // @@ -82,9 +80,18 @@ func (g *Gui) selectPage(row, col int) string { case 0: p = `trips` case 1: - p = `people` + p = `cavers` case 2: - p = `locations` + p = `caves` } return p -} \ No newline at end of file +} + +/* +func (g *Gui) nextPage() { + slide, _ := strconv.Atoi(g.state.tabBar.GetHighlights()[0]) + slide = (slide + 1) % g.pages.GetPageCount() + //g.state.tabBar.Highlight(strconv.Itoa(slide)).ScrollToHighlight() + g.goTo(g.selectPage(slide - 1, 0)) // NOTE: If the Highlight func is fixed for the tab bar then this line will not be required +} +*/ diff --git a/internal/gui/menu.go b/internal/gui/menu.go deleted file mode 100644 index 55304cc..0000000 --- a/internal/gui/menu.go +++ /dev/null @@ -1,71 +0,0 @@ -package gui - -import ( - "github.com/gdamore/tcell" - "github.com/rivo/tview" -) - -type menu struct { - *tview.Table -} - -func newMenu(g *Gui) (m *menu) { - m = &menu{ - Table: tview.NewTable().SetSelectable(true, false).Select(0,0).SetFixed(1,1), - } - - m.SetTitle(` Menu `).SetTitleAlign(tview.AlignLeft) - m.SetBorder(true) - m.setEntries(g) - m.setKeybinding(g) - return -} - -func (m *menu) name() string { - return `menu` -} - -func (m *menu) setEntries(g *Gui) { - m.entries(g) - table := m.Clear() - - for i, option := range g.state.resources.menu { - table.SetCell(i, 0, tview.NewTableCell(option). - SetTextColor(tcell.ColorWhite). - SetMaxWidth(30). - SetExpansion(1)) - } -} - -func (m *menu) setKeybinding(g *Gui) { - m.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { - g.setGlobalKeybinding(event) - - switch event.Key() { - case tcell.KeyEnter: - g.goTo(g.selectPage(m.GetSelection())) - case tcell.KeyTAB: - g.goTo(g.selectPage(m.GetSelection())) - } - - return event - }) -} - -func (m *menu) entries(g *Gui) { - options := []string{`Trips`, `People`, `Locations`} - g.state.resources.menu = options -} - -func (m *menu) updateEntries(g *Gui) {} - -func (m *menu) focus(g *Gui) { - m.SetSelectable(true, false) - g.app.SetFocus(m) -} - -func (m *menu) unfocus() { - m.SetSelectable(false, false) -} - -func (m *menu) setFilterWord(word string) {} diff --git a/internal/gui/monitoring.go b/internal/gui/monitoring.go index e0abcd5..136be9b 100644 --- a/internal/gui/monitoring.go +++ b/internal/gui/monitoring.go @@ -6,8 +6,8 @@ func (g *Gui) startMonitoring() { g.state.stopChans["caves"] = stop g.state.stopChans["cavers"] = stop go g.tripsPanel().monitoringTrips(g) - go g.locationsPanel().monitoringCaves(g) - go g.peoplePanel().monitoringCavers(g) + go g.cavesPanel().monitoringCaves(g) + go g.caversPanel().monitoringCavers(g) } func (g *Gui) stopMonitoring() { @@ -16,8 +16,8 @@ func (g *Gui) stopMonitoring() { g.state.stopChans["cavers"] <- 1 } -func (g *Gui) updateTask() { +/*func (g *Gui) updateTask() { g.app.QueueUpdateDraw(func() { - g.peoplePanel().setEntries(g) + g.caversPanel().setEntries(g) // REVIEW: Why is this just people ? }) -} +}*/ diff --git a/internal/gui/navigate.go b/internal/gui/navigate.go index b80d371..5d0b2c9 100644 --- a/internal/gui/navigate.go +++ b/internal/gui/navigate.go @@ -1,9 +1,6 @@ package gui -import ( - "github.com/gdamore/tcell" - "github.com/rivo/tview" -) +import "github.com/rivo/tview" type navigate struct { *tview.TextView @@ -12,15 +9,16 @@ type navigate struct { func newNavigate() *navigate { navi := &navigate{ - TextView: tview.NewTextView().SetTextColor(tcell.ColorWhite), + TextView: tview.NewTextView().SetTextColor(tview.Styles.PrimaryTextColor), keybindings: map[string]string{ - "trips": " n: New Log Entry, m: Modify Log, d: Remove Log, /: filter, Enter: Inspect ", - "locations": " n: New Cave, m: Modify Cave, d: Remove Cave, /: filter, Enter: Inspect ", - "people": " n: New Caver, m: Modify Caver, d: Remove Caver, /: filter, Enter: Inspect ", + "trips" : " n: New Log Entry, m: Modify Log, d: Remove Log, /: Filter, Enter: Inspect Detail ", + "caves" : " n: New Cave, m: Modify Cave, d: Remove Cave, /: Filter ", + "cavers": " n: New Caver, m: Modify Caver, d: Remove Caver, /: Filter ", + "detail": " q | ESC: Exit Detail ", }, } - navi.SetBorder(true) + navi.SetBorder(false) return navi } diff --git a/internal/gui/panel.go b/internal/gui/panel.go index 0e1becc..aa0c718 100644 --- a/internal/gui/panel.go +++ b/internal/gui/panel.go @@ -8,5 +8,5 @@ type panel interface { setKeybinding(*Gui) focus(*Gui) unfocus() - setFilterWord(string) + setFilter(string, string) } diff --git a/internal/gui/statsLocations.go b/internal/gui/statsLocations.go deleted file mode 100644 index 3ef6708..0000000 --- a/internal/gui/statsLocations.go +++ /dev/null @@ -1,71 +0,0 @@ -package gui - -import ( - "github.com/gdamore/tcell" - "github.com/rivo/tview" -) - -type statsLocations struct { - *tview.Table -} - -func newStatsLocations(g *Gui) (s *statsLocations) { - s = &statsLocations{ - Table: tview.NewTable().SetSelectable(false, false).SetFixed(3,1), - } - - s.SetTitle(` Top Caves `).SetTitleAlign(tview.AlignLeft) - s.SetBorder(true) - s.setEntries(g) - s.setKeybinding(g) - return -} - -func (s *statsLocations) name() string { - return `statsLocations` -} - -func (s *statsLocations) setEntries(g *Gui) { - s.entries(g) - table := s.Clear() - - for i, stat := range g.state.resources.statsLocations { - table.SetCell(i, 0, tview.NewTableCell(stat.Name). - SetTextColor(tcell.ColorWhite). - SetMaxWidth(30). - SetExpansion(2)) - - table.SetCell(i, 1, tview.NewTableCell(stat.Value). - SetTextColor(tcell.ColorWhite). - SetMaxWidth(30). - SetExpansion(1)) - } -} - -func (s *statsLocations) setKeybinding(g *Gui) { - s.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { - g.setGlobalKeybinding(event) - return event - }) -} - -func (s *statsLocations) entries(g *Gui) { - stats, err := g.db.GetTopLocations() - if err != nil { - return - } - g.state.resources.statsLocations = stats -} - -func (s *statsLocations) updateEntries(g *Gui) {} - -func (s *statsLocations) focus(g *Gui) { - s.SetSelectable(true, false) - g.app.SetFocus(s) -} - -func (s *statsLocations) unfocus() { - s.SetSelectable(false, false) -} - -func (s *statsLocations) setFilterWord(word string) {} diff --git a/internal/gui/statsPeople.go b/internal/gui/statsPeople.go deleted file mode 100644 index 7beadd8..0000000 --- a/internal/gui/statsPeople.go +++ /dev/null @@ -1,72 +0,0 @@ -package gui - -import ( - "github.com/gdamore/tcell" - "github.com/rivo/tview" -) - -type statsPeople struct { - *tview.Table -} - -func newStatsPeople(g *Gui) (s *statsPeople) { - s = &statsPeople{ - Table: tview.NewTable().SetSelectable(false, false).SetFixed(3,1), - } - - s.SetTitle(` Top Cavers `).SetTitleAlign(tview.AlignLeft) - s.SetBorder(true) - s.setEntries(g) - s.setKeybinding(g) - return -} - -func (s *statsPeople) name() string { - return `statsPeople` -} - -func (s *statsPeople) setEntries(g *Gui) { - s.entries(g) - table := s.Clear() - - for i, stat := range g.state.resources.statsPeople { - table.SetCell(i, 0, tview.NewTableCell(stat.Name). - SetTextColor(tcell.ColorWhite). - SetMaxWidth(30). - SetExpansion(2)) - - table.SetCell(i, 1, tview.NewTableCell(stat.Value). - SetTextColor(tcell.ColorWhite). - SetMaxWidth(30). - SetExpansion(1)) - } -} - -func (s *statsPeople) setKeybinding(g *Gui) { - s.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { - g.setGlobalKeybinding(event) - - return event - }) -} - -func (s *statsPeople) entries(g *Gui) { - stats, err := g.db.GetTopPeople() - if err != nil { - return - } - g.state.resources.statsPeople = stats -} - -func (s *statsPeople) updateEntries(g *Gui) {} - -func (s *statsPeople) focus(g *Gui) { - s.SetSelectable(true, false) - g.app.SetFocus(s) -} - -func (s *statsPeople) unfocus() { - s.SetSelectable(false, false) -} - -func (s *statsPeople) setFilterWord(word string) {} diff --git a/internal/gui/tabbar.go b/internal/gui/tabbar.go new file mode 100644 index 0000000..0f2c644 --- /dev/null +++ b/internal/gui/tabbar.go @@ -0,0 +1,15 @@ +package gui + +import ( + "github.com/rivo/tview" +) + +func newTabBar(g *Gui) *tview.TextView { + return tview.NewTextView(). + SetDynamicColors(true). + SetRegions(true). + SetWrap(false)/*. + SetHighlightedFunc(func(added, removed, remaining []string) { + g.pages.SwitchToPage(added[0]) + })*/ +} diff --git a/internal/gui/time.go b/internal/gui/time.go deleted file mode 100644 index 3a4f8c7..0000000 --- a/internal/gui/time.go +++ /dev/null @@ -1,75 +0,0 @@ -package gui - -import ( - "time" - - "github.com/gdamore/tcell" - "github.com/rivo/tview" - - "github.com/idlephysicist/cave-logger/internal/model" -) - -type timeWindow struct { - *tview.Table -} - -func newTimeWindow(g *Gui) (t *timeWindow) { - t = &timeWindow{ - Table: tview.NewTable().SetSelectable(false, false).SetFixed(3,1), - } - - t.SetBorder(true) - t.setEntries(g) - t.setKeybinding(g) - return -} - -func (t *timeWindow) name() string { - return `timeWindow` -} - -func (t *timeWindow) setEntries(g *Gui) { - t.entries(g) - table := t.Clear() - - for i, stat := range g.state.resources.timeWindow { - table.SetCell(i, 0, tview.NewTableCell(stat.Name). - SetTextColor(tcell.ColorWhite). - SetMaxWidth(30). - SetExpansion(2)) - - table.SetCell(i, 1, tview.NewTableCell(stat.Value). - SetTextColor(tcell.ColorWhite). - SetMaxWidth(30). - SetExpansion(1)) - } -} - -func (t *timeWindow) setKeybinding(g *Gui) { - t.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { - g.setGlobalKeybinding(event) - return event - }) -} - -func (t *timeWindow) entries(g *Gui) { - timeSlice := make([]*model.Statistic, 0) - timeSlice = append( - timeSlice, - &model.Statistic{Name: `Today`, Value: time.Now().Format(`2006-01-02`)}, - ) - g.state.resources.timeWindow = timeSlice -} - -func (t *timeWindow) updateEntries(g *Gui) {} - -func (t *timeWindow) focus(g *Gui) { - t.SetSelectable(true, false) - g.app.SetFocus(t) -} - -func (t *timeWindow) unfocus() { - t.SetSelectable(false, false) -} - -func (t *timeWindow) setFilterWord(word string) {} diff --git a/internal/gui/trips.go b/internal/gui/trips.go index b114110..943c5a7 100644 --- a/internal/gui/trips.go +++ b/internal/gui/trips.go @@ -1,6 +1,7 @@ package gui import ( + "strings" "time" "github.com/gdamore/tcell" @@ -11,8 +12,8 @@ import ( type trips struct { *tview.Table - trips chan *model.Log - filterWord string + trips chan *model.Log + filterCol, filterTerm string } func newTrips(g *Gui) *trips { @@ -21,7 +22,7 @@ func newTrips(g *Gui) *trips { trips: make(chan *model.Log), } - trips.SetTitle(` Trips `).SetTitleAlign(tview.AlignLeft) + trips.SetTitle(``).SetTitleAlign(tview.AlignLeft) trips.SetBorder(true) trips.setEntries(g) trips.setKeybinding(g) @@ -38,11 +39,10 @@ func (t *trips) setKeybinding(g *Gui) { switch event.Key() { case tcell.KeyEnter: + g.state.navigate.update("detail") g.inspectTrip() - //case tcell.KeyCtrlR: - // t.setEntries(g) - case tcell.KeyTAB: - g.switchPanel(`menu`) + case tcell.KeyCtrlR: + t.setEntries(g) } switch event.Rune() { @@ -64,7 +64,14 @@ func (t *trips) entries(g *Gui) { return } - g.state.resources.trips = trips + var filteredTrips []*model.Log + for _, trip := range trips { + if t.search(trip) { + continue + } + filteredTrips = append(filteredTrips, trip) + } + g.state.resources.trips = filteredTrips } func (t *trips) setEntries(g *Gui) { @@ -82,25 +89,25 @@ func (t *trips) setEntries(g *Gui) { Text: header, NotSelectable: true, Align: tview.AlignLeft, - Color: tcell.ColorWhite, - BackgroundColor: tcell.ColorDefault, + Color: tview.Styles.PrimaryTextColor, + BackgroundColor: tview.Styles.PrimitiveBackgroundColor, Attributes: tcell.AttrBold, }) } for i, trip := range g.state.resources.trips { table.SetCell(i+1, 0, tview.NewTableCell(trip.Date). - SetTextColor(tcell.ColorWhite). + SetTextColor(tview.Styles.PrimaryTextColor). SetMaxWidth(30). SetExpansion(1)) table.SetCell(i+1, 1, tview.NewTableCell(trip.Cave). - SetTextColor(tcell.ColorWhite). + SetTextColor(tview.Styles.PrimaryTextColor). SetMaxWidth(30). SetExpansion(1)) table.SetCell(i+1, 2, tview.NewTableCell(trip.Names). - SetTextColor(tcell.ColorWhite). + SetTextColor(tview.Styles.PrimaryTextColor). SetMaxWidth(0). SetExpansion(2)) } @@ -121,12 +128,13 @@ func (t *trips) unfocus() { t.SetSelectable(false, false) } -func (t *trips) setFilterWord(word string) { - t.filterWord = word +func (t *trips) setFilter(col, term string) { + t.filterCol = col + t.filterTerm = term } func (t *trips) monitoringTrips(g *Gui) { - ticker := time.NewTicker(5 * time.Second) + ticker := time.NewTicker(5 * time.Minute) LOOP: for { @@ -139,3 +147,15 @@ LOOP: } } } + +func (t *trips) search(trip *model.Log) bool { + switch t.filterCol { + case "cave", "": + if strings.Index(strings.ToLower(trip.Cave), t.filterTerm) == -1 { + return true + } + return false + default: + return false + } +} diff --git a/internal/model/config.go b/internal/model/config.go index ec01644..980b225 100644 --- a/internal/model/config.go +++ b/internal/model/config.go @@ -5,5 +5,6 @@ type Config struct { Created string `json:"created"` Filename string `json:"filename"` } `json:"database"` + Colors map[string]string `json:"colors"` } diff --git a/scripts/csv2sqlite.py b/scripts/csv2sqlite.py index 9e094b7..cd9d0e5 100755 --- a/scripts/csv2sqlite.py +++ b/scripts/csv2sqlite.py @@ -4,7 +4,7 @@ import json import subprocess import sqlite3 -import yaml +import json import os import sys @@ -15,14 +15,14 @@ CSV = sys.argv[1] INITSCRIPT = "./scripts/make-db.py" -CFG = "./config/config.yml" +CFG = "./config/config.json" class App(object): def __init__(self): out = subprocess.run(INITSCRIPT, capture_output=True) with open(CFG, 'r') as c: - self.config = yaml.safe_load(c) + self.config = json.load(c) self.conn = sqlite3.connect( self.config['database']['filename'], diff --git a/scripts/make-db.py b/scripts/make-db.py index 7cf4d34..d294484 100755 --- a/scripts/make-db.py +++ b/scripts/make-db.py @@ -1,22 +1,14 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 from datetime import datetime import os import sys import sqlite3 import uuid -import yaml +import json NOW = datetime.now().strftime("%Y-%m-%dT%H_%M") HOME = os.environ["HOME"] -NEWPATH = "{}/.config/cave-logger".format(HOME) - -if sys.version_info < (3,): - print(""" -Why are you not using Python 3 by now? -Even I am... -Alas this script will still try to run.\n""" - ) - +NEWPATH = f"{HOME}/.config/cave-logger" try: os.makedirs(NEWPATH, 0o755) @@ -32,12 +24,12 @@ for f in files: if '.db' in f: db = f - elif 'config.yml' == f: + elif 'config.json' == f: cfg = f with open(cfg, 'r') as f: try: - fn = yaml.safe_load(f)['database']['filename'] + fn = json.load(f)['database']['filename'] if db in fn: print("Found pre-existing configs aborting...") sys.exit() @@ -95,13 +87,26 @@ print("Created {} database tables".format(len(tables))) conn.close() -CONFIG_FN = '{}/config.yml'.format(NEWPATH) +CONFIG_FN = '{}/config.json'.format(NEWPATH) with open(CONFIG_FN, 'w') as c: config = { 'database': { - 'filename': '/'.join([NEWPATH, sqliteFile]), + 'filename': '/'.join([".config/cave-logger", sqliteFile]), 'created' : NOW + }, + 'colors': { + 'primitiveBackground': '', + 'contrastBackground': '', + 'moreContrastBackground': '', + 'border': '', + 'title': '', + 'graphics': '', + 'primaryText': '', + 'secondaryText': '', + 'tertiaryText': '', + 'inverseText': '', + 'contrastSecondaryText': '' } } - yaml.dump(config, c) + json.dump(config, c, indent=2) print("Wrote database name to config file") diff --git a/scripts/move-configs.py b/scripts/move-configs.py index 9d7d22d..2aa1682 100755 --- a/scripts/move-configs.py +++ b/scripts/move-configs.py @@ -1,11 +1,11 @@ #!/usr/bin/env python3 -import yaml +import json import os import shutil HOME = os.environ["HOME"] -CFGFILE = "./config/config.yml" +CFGFILE = "./config/config.json" NEWPATH = ".config/cave-logger" @@ -15,7 +15,7 @@ print("Directory exists moving on") with open(CFGFILE, 'r') as c: - cfg = yaml.safe_load(c) + cfg = json.load(c) shutil.copy( '/'.join([HOME,cfg['database']['filename']]), @@ -25,5 +25,5 @@ cfg['database']['filename'] = f"{HOME}/{NEWPATH}/{cfg['database']['filename'].split('/')[-1]}" -with open(f"{HOME}/{NEWPATH}/config.yml", 'w') as c: - yaml.dump(cfg, c) +with open(f"{HOME}/{NEWPATH}/config.json", 'w') as c: + json.dump(cfg, c, indent=2)