Cleans up some display issues

This commit is contained in:
sloumdrone 2019-09-20 16:15:53 -07:00
parent 19f210f243
commit db1cf75d2e
5 changed files with 146 additions and 115 deletions

View File

@ -50,6 +50,7 @@ func (b *Bookmarks) ToggleOpen() {
b.IsFocused = true b.IsFocused = true
} else { } else {
b.IsFocused = false b.IsFocused = false
cui.Clear("screen")
} }
} }

183
client.go
View File

@ -3,7 +3,6 @@ package main
import ( import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"net"
"os" "os"
"os/exec" "os/exec"
// "os/user" // "os/user"
@ -73,7 +72,8 @@ func (c *client) GetSize() {
if h != c.Height || w != c.Width { if h != c.Height || w != c.Width {
c.Height = h c.Height = h
c.Width = w c.Width = w
c.Scroll(0) c.SetPercentRead()
c.Draw()
} }
time.Sleep(500 * time.Millisecond) time.Sleep(500 * time.Millisecond)
@ -86,7 +86,7 @@ func (c *client) Draw() {
screen.WriteString("\033[0m") screen.WriteString("\033[0m")
screen.WriteString(c.TopBar.Render(c.Width, c.Options["theme"])) screen.WriteString(c.TopBar.Render(c.Width, c.Options["theme"]))
screen.WriteString("\n") screen.WriteString("\n")
pageContent := c.PageState.Render(c.Height, c.Width) pageContent := c.PageState.Render(c.Height, c.Width - 1)
if c.Options["theme"] == "inverse" { if c.Options["theme"] == "inverse" {
screen.WriteString("\033[7m") screen.WriteString("\033[7m")
} }
@ -122,11 +122,11 @@ func (c *client) Draw() {
} }
} else { } else {
for i := 0; i < c.Height - 3; i++ { for i := 0; i < c.Height - 3; i++ {
if i < len(pageContent) - 1 { if i < len(pageContent) {
screen.WriteString(fmt.Sprintf("%-*.*s", c.Width, c.Width, pageContent[i])) screen.WriteString(fmt.Sprintf("%-*.*s", c.Width - 1, c.Width - 1, pageContent[i]))
screen.WriteString("\n") screen.WriteString("\n")
} else { } else {
screen.WriteString(fmt.Sprintf("%*.*s", c.Width, c.Width, " ")) screen.WriteString(fmt.Sprintf("%-*.*s", c.Width, c.Width, " "))
screen.WriteString("\n") screen.WriteString("\n")
} }
} }
@ -301,6 +301,28 @@ func (c *client) doCommand(action string, values []string) {
c.displayConfigValue(values[0]) c.displayConfigValue(values[0])
case "SEARCH": case "SEARCH":
c.search(strings.Join(values, " ")) c.search(strings.Join(values, " "))
case "WRITE", "W":
if values[0] == "." {
values[0] = c.PageState.History[c.PageState.Position].Location.Full
}
u, err := MakeUrl(values[0])
if err != nil {
c.SetMessage(err.Error(), true)
c.DrawMessage()
return
}
fns := strings.Split(u.Resource, "/")
var fn string
if len(fns) > 0 {
fn = strings.Trim(fns[len(fns) - 1], "\t\r\n \a\f\v")
} else {
fn = "index"
}
if fn == "" {
fn = "index"
}
c.saveFile(u, fn)
default: default:
c.SetMessage(fmt.Sprintf("Unknown action %q", action), true) c.SetMessage(fmt.Sprintf("Unknown action %q", action), true)
c.DrawMessage() c.DrawMessage()
@ -340,28 +362,15 @@ func (c *client) doCommandAs(action string, values []string) {
} }
case "WRITE", "W": case "WRITE", "W":
// TODO figure out how best to handle file u, err := MakeUrl(values[0])
// writing... it will depend on request model if err != nil {
// using fetch would be best c.SetMessage(err.Error(), true)
// - - - - - - - - - - - - - - - - - - - - - c.DrawMessage()
// var data []byte return
// if values[0] == "." { }
// d, err := c.getCurrentPageRawData() fileName := strings.Join(values[1:], "-")
// if err != nil { fileName = strings.Trim(fileName, " \t\r\n\a\f\v")
// c.SetMessage(err.Error(), true) c.saveFile(u, fileName)
// c.DrawMessage()
// return
// }
// data = []byte(d)
// }
// fp, err := c.saveFile(data, strings.Join(values[1:], " "))
// if err != nil {
// c.SetMessage(err.Error(), true)
// c.DrawMessage()
// return
// }
// c.SetMessage(fmt.Sprintf("File saved to: %s", fp), false)
// c.DrawMessage()
case "SET", "S": case "SET", "S":
if _, ok := c.Options[values[0]]; ok { if _, ok := c.Options[values[0]]; ok {
@ -424,8 +433,10 @@ func (c *client) doLinkCommandAs(action, target string, values []string) {
c.Draw() c.Draw()
} }
case "WRITE", "W": case "WRITE", "W":
// TODO get file writing working in some semblance of universal way out := make([]string, 0, len(values) + 1)
// return saveFile(links[num-1], strings.Join(values, " ")) out = append(out, links[num])
out = append(out, values...)
c.doCommandAs(action, out)
default: default:
c.SetMessage(fmt.Sprintf("Unknown command structure"), true) c.SetMessage(fmt.Sprintf("Unknown command structure"), true)
} }
@ -446,14 +457,35 @@ func (c *client) getCurrentPageRawData() (string, error) {
return c.PageState.History[c.PageState.Position].RawContent, nil return c.PageState.History[c.PageState.Position].RawContent, nil
} }
func (c *client) saveFile(data []byte, name string) (string, error) { func (c *client) saveFile(u Url, name string) {
savePath := c.Options["savelocation"] + name var file []byte
err := ioutil.WriteFile(savePath, data, 0644) var err error
if err != nil { switch u.Scheme {
return "", err case "gopher":
file, err = gopher.Retrieve(u.Host, u.Port, u.Resource)
case "gemini":
file, err = gemini.Fetch(u.Host, u.Port, u.Resource)
default:
c.SetMessage(fmt.Sprintf("Saving files over %s is not supported", u.Scheme), true)
c.DrawMessage()
return
} }
return savePath, nil if err != nil {
c.SetMessage(err.Error(), true)
c.DrawMessage()
return
}
savePath := c.Options["savelocation"] + name
err = ioutil.WriteFile(savePath, file, 0644)
if err != nil {
c.SetMessage("Error writing file to disk", true)
c.DrawMessage()
return
}
c.SetMessage(fmt.Sprintf("File saved to: %s", savePath), false)
c.DrawMessage()
} }
func (c *client) doLinkCommand(action, target string) { func (c *client) doLinkCommand(action, target string) {
@ -503,6 +535,30 @@ func (c *client) doLinkCommand(action, target string) {
link := links[num] link := links[num]
c.SetMessage(fmt.Sprintf("[%d] %s", num + 1, link), false) c.SetMessage(fmt.Sprintf("[%d] %s", num + 1, link), false)
c.DrawMessage() c.DrawMessage()
case "WRITE", "W":
links := c.PageState.History[c.PageState.Position].Links
if len(links) < num || num < 1 {
c.SetMessage("Invalid link ID", true)
c.DrawMessage()
return
}
u, err := MakeUrl(links[num-1])
if err != nil {
c.SetMessage(err.Error(), true)
c.DrawMessage()
return
}
fns := strings.Split(u.Resource, "/")
var fn string
if len(fns) > 0 {
fn = strings.Trim(fns[len(fns) - 1], "\t\r\n \a\f\v")
} else {
fn = "index"
}
if fn == "" {
fn = "index"
}
c.saveFile(u, fn)
default: default:
c.SetMessage(fmt.Sprintf("Action %q does not exist for target %q", action, target), true) c.SetMessage(fmt.Sprintf("Action %q does not exist for target %q", action, target), true)
c.DrawMessage() c.DrawMessage()
@ -576,7 +632,6 @@ func (c *client) Scroll(amount int) {
} }
c.BookMarks.Position = newScrollPosition c.BookMarks.Position = newScrollPosition
c.Draw()
} else { } else {
var percentRead int var percentRead int
page := c.PageState.History[c.PageState.Position] page := c.PageState.History[c.PageState.Position]
@ -609,8 +664,8 @@ func (c *client) Scroll(amount int) {
percentRead = int(float32(newScrollPosition + c.Height - 3) / float32(len(page.WrappedContent)) * 100.0) percentRead = int(float32(newScrollPosition + c.Height - 3) / float32(len(page.WrappedContent)) * 100.0)
} }
c.FootBar.SetPercentRead(percentRead) c.FootBar.SetPercentRead(percentRead)
c.Draw()
} }
c.Draw()
} }
func (c *client) SetPercentRead() { func (c *client) SetPercentRead() {
@ -731,9 +786,9 @@ func (c *client) Visit(url string) {
return return
} }
pg := MakePage(u, content, links) pg := MakePage(u, content, links)
pg.WrapContent(c.Width) pg.WrapContent(c.Width - 1)
c.PageState.Add(pg) c.PageState.Add(pg)
c.Scroll(0) // to update percent read c.SetPercentRead()
c.ClearMessage() c.ClearMessage()
c.SetHeaderUrl() c.SetHeaderUrl()
c.Draw() c.Draw()
@ -748,9 +803,9 @@ func (c *client) Visit(url string) {
case 2: case 2:
if capsule.MimeMaj == "text" { if capsule.MimeMaj == "text" {
pg := MakePage(u, capsule.Content, capsule.Links) pg := MakePage(u, capsule.Content, capsule.Links)
pg.WrapContent(c.Width) pg.WrapContent(c.Width - 1)
c.PageState.Add(pg) c.PageState.Add(pg)
c.Scroll(0) c.SetPercentRead()
c.ClearMessage() c.ClearMessage()
c.SetHeaderUrl() c.SetHeaderUrl()
c.Draw() c.Draw()
@ -808,51 +863,7 @@ func (c *client) Visit(url string) {
//--------------------------------------------------\\ //--------------------------------------------------\\
func MakeClient(name string) *client { func MakeClient(name string) *client {
// var userinfo, _ = user.Current()
// var options = map[string]string{
// "homeurl": "gopher://colorfield.space:70/1/bombadillo-info",
// "savelocation": userinfo.HomeDir,
// "searchengine": "gopher://gopher.floodgap.com:70/7/v2/vs",
// "openhttp": "false",
// "httpbrowser": "lynx",
// "configlocation": userinfo.HomeDir,
// }
c := client{0, 0, defaultOptions, "", false, MakePages(), MakeBookmarks(), MakeHeadbar(name), MakeFootbar()} c := client{0, 0, defaultOptions, "", false, MakePages(), MakeBookmarks(), MakeHeadbar(name), MakeFootbar()}
return &c return &c
} }
// Retrieve a byte slice of raw response dataa
// from a url string
func Fetch(url string) ([]byte, error) {
u, err := MakeUrl(url)
if err != nil {
return []byte(""), err
}
timeOut := time.Duration(5) * time.Second
if u.Host == "" || u.Port == "" {
return []byte(""), fmt.Errorf("Incomplete request url")
}
addr := u.Host + ":" + u.Port
conn, err := net.DialTimeout("tcp", addr, timeOut)
if err != nil {
return []byte(""), err
}
send := u.Resource + "\n"
_, err = conn.Write([]byte(send))
if err != nil {
return []byte(""), err
}
result, err := ioutil.ReadAll(conn)
if err != nil {
return []byte(""), err
}
return result, err
}

View File

@ -19,31 +19,3 @@ var defaultOptions = map[string]string{
"theme": "normal", // "normal", "inverted" "theme": "normal", // "normal", "inverted"
} }
// TODO decide whether or not to institute a color theme
// system. Preliminary testing implies it should be very
// doable.
var theme = map[string]string{
"topbar_title_bg": "",
"topbar_link_fg": "",
"body_bg": "237",
"body_fg": "",
"bookmarks_bg": "",
"bookmarks_fg": "",
"command_bg": "",
"message_fg": "",
"error_fg": "",
"bottombar_bg": "",
"bottombar_fg": "",
//
// text style options
//
"topbar_title_style": "bold",
"topbar_link_style": "plain",
"body_style": "plain",
"bookmark_body_style": "plain",
"bookmark_border_style": "plain",
"message_style": "italic",
"error_style": "bold",
"command_style": "plain",
"bottom_bar_style": "plain",
}

View File

@ -56,6 +56,53 @@ func Retrieve(host, port, resource string) (string, error) {
return string(result), nil return string(result), nil
} }
func Fetch(host, port, resource string) ([]byte, error) {
rawResp, err := Retrieve(host, port, resource)
if err != nil {
return make([]byte, 0), err
}
resp := strings.SplitN(rawResp, "\r\n", 2)
if len(resp) != 2 {
if err != nil {
return make([]byte, 0), fmt.Errorf("Invalid response from server")
}
}
header := strings.SplitN(resp[0], " ", 2)
if len([]rune(header[0])) != 2 {
header = strings.SplitN(resp[0], "\t", 2)
if len([]rune(header[0])) != 2 {
return make([]byte,0), fmt.Errorf("Invalid response format from server")
}
}
// Get status code single digit form
status, err := strconv.Atoi(string(header[0][0]))
if err != nil {
return make([]byte, 0), fmt.Errorf("Invalid status response from server")
}
if status != 2 {
switch status {
case 1:
return make([]byte, 0), fmt.Errorf("[1] Queries cannot be saved.")
case 3:
return make([]byte, 0), fmt.Errorf("[3] Redirects cannot be saved.")
case 4:
return make([]byte, 0), fmt.Errorf("[4] Temporary Failure.")
case 5:
return make([]byte, 0), fmt.Errorf("[5] Permanent Failure.")
case 6:
return make([]byte, 0), fmt.Errorf("[6] Client Certificate Required (Not supported by Bombadillo)")
default:
return make([]byte, 0), fmt.Errorf("Invalid response status from server")
}
}
return []byte(resp[1]), nil
}
func Visit(host, port, resource string) (Capsule, error) { func Visit(host, port, resource string) (Capsule, error) {
capsule := MakeCapsule() capsule := MakeCapsule()
rawResp, err := Retrieve(host, port, resource) rawResp, err := Retrieve(host, port, resource)

View File

@ -106,7 +106,7 @@ func main() {
} }
args := flag.Args() args := flag.Args()
cui.Tput("rmam") // turn off line wrapping cui.Tput("rmam") // turn off line wrapping
cui.Tput("smcup") // use alternate screen cui.Tput("smcup") // use alternate screen
defer cui.Exit() defer cui.Exit()
err := initClient() err := initClient()