diff options
Diffstat (limited to 'window.go')
-rw-r--r-- | window.go | 357 |
1 files changed, 0 insertions, 357 deletions
diff --git a/window.go b/window.go deleted file mode 100644 index 0e85bb7..0000000 --- a/window.go +++ /dev/null @@ -1,357 +0,0 @@ -package main - -import ( - "crypto/sha256" - "fmt" - "io" - "io/ioutil" - "os" - "path/filepath" - "strings" - "time" - - "github.com/gdamore/tcell" - "github.com/pkg/errors" - "github.com/prodhe/poe/gapbuffer" -) - -// File holds information about the file on disk. It is managed by Window and the bytes on disk are optionally loaded into a Text. -type File struct { - name string - read bool // true if file has been read - mtime time.Time // of file when last read/written - sha256 string // of file when last read/written -} - -// Window is a tagline and a body with an optional underlying file on disk. It is the main component and handles all events apart from the system wide shortcuts. -type Window struct { - x, y, w, h int - file *File - isdir bool - body *View - tagline *View - col *Column // reference to the column where in - hidden bool - collapsed bool // not implemented - qcnt int // quit count -} - -// NewWindow returns a fresh window associated with the given filename. For special case filenames, look at the package constants. -func NewWindow(fn string) *Window { - fnabs := fn - if fn != FnEmptyWin { - var err error - fnabs, err = filepath.Abs(fn) - if err != nil { - printMsg("%s\n", err) - fnabs = FnEmptyWin - } - } - win := &Window{ - body: &View{ - text: &Text{buf: gapbuffer.New()}, - what: ViewBody, - style: bodyStyle, - cursorStyle: bodyCursorStyle, - hilightStyle: bodyHilightStyle, - tabstop: 4, - focused: true, - }, - tagline: &View{ - text: &Text{buf: gapbuffer.New()}, - what: ViewTagline, - style: tagStyle, - cursorStyle: tagCursorStyle, - hilightStyle: tagHilightStyle, - tabstop: 4, - }, - file: &File{name: fnabs}, - } - - fmt.Fprintf(win.tagline, "%s%s Del ", - win.Flags(), - win.Name(), - ) - - return win -} - -func AllWindows() []*Window { - var ws []*Window - for _, col := range workspace.cols { - ws = append(ws, col.windows...) - } - return ws -} - -// FindWindow searches for the given name in current windows and returns a pointer if one already exists. Nil otherwise. Name assumes to be an absolute path. -func FindWindow(name string) *Window { - for _, win := range AllWindows() { - if win.NameAbs() == name { - return win - } - } - return nil -} - -func (win *Window) LoadBuffer() bool { - if win.file.read || win.Name() == FnEmptyWin || win.body.what == ViewScratch { - return false - } - - info, err := os.Stat(win.file.name) - if err != nil { - // if the file exists, print why we could not open it - // otherwise just close silently - if os.IsExist(err) { - printMsg("%s\n", err) - } - win.Close() - return false - } - - // name is a directory; list it's content into the buffer - if info.IsDir() { - files, err := ioutil.ReadDir(win.file.name) - if err != nil { - printMsg("%s\n", err) - return false - } - - win.body.what = ViewScratch - win.isdir = true - - // list files in dir - for _, f := range files { - dirchar := "" - if f.IsDir() { - dirchar = string(filepath.Separator) - } - fmt.Fprintf(win.body.text.buf, "%s%s\n", f.Name(), dirchar) - } - return true - } - - // name is a file - fh, err := os.OpenFile(win.file.name, os.O_RDWR|os.O_CREATE, 0644) - if err != nil { - printMsg("%s\n", err) - return false - } - defer fh.Close() - - if _, err := io.Copy(win.body.text.buf, fh); err != nil { - printMsg("%s\n", err) - return false - } - fh.Seek(0, 0) - - h := sha256.New() - if _, err := io.Copy(h, fh); err != nil { - printMsg("%s\n", err) - return false - } - win.file.sha256 = fmt.Sprintf("%x", h.Sum(nil)) - - win.file.mtime = info.ModTime() - win.file.read = true - - win.body.SetCursor(0, io.SeekStart) - - return true -} - -// SaveFile replaces disk file with buffer content. Returns error if no disk file is set. -func (win *Window) SaveFile() (int, error) { - if win.NameTag() == FnEmptyWin { - return 0, errors.New("no filename") - } - - if win.Name() == FnMessageWin { // can not save +poe window - return 0, nil - } - - // TODO: check this in real time in case of tag name change... - if win.isdir { // can not save a directory - return 0, nil - } - - // check for file existence if we recently changed the file name - openmasks := os.O_RDWR | os.O_CREATE - var namechange bool - if win.Name() != win.NameTag() { // user has changed name - openmasks |= os.O_EXCL // must not already exist - namechange = true // to skip sha256 checksum - } - - f, err := os.OpenFile(win.NameTag(), openmasks, 0644) - if err != nil { - if os.IsExist(err) { - printMsg("%s already exists\n", win.NameTag()) - return 0, nil - } - return 0, err - } - defer f.Close() - - h := sha256.New() - if _, err := io.Copy(h, f); err != nil { - return 0, errors.Wrap(err, "sha256") - } - hhex := fmt.Sprintf("%x", h.Sum(nil)) - - // verify checksum if the file is not newly created via a namechange - if !namechange && hhex != win.file.sha256 { - return 0, errors.Errorf("file has been modified outside of poe") - } - - n, err := f.WriteAt(win.body.text.buf.Bytes(), 0) - if err != nil { - return 0, err - } - f.Truncate(int64(n)) - f.Sync() - - win.file.sha256 = fmt.Sprintf("%x", sha256.Sum256(win.body.text.buf.Bytes())) - - info, err := f.Stat() - if err != nil { - return n, err - } - win.file.mtime = info.ModTime() - - win.body.dirty = false - - return n, nil -} - -// Name returns either the file from disk name or empty string if the buffer has no disk counterpart. -func (win *Window) Name() string { - if win.file.name == FnEmptyWin { - return FnEmptyWin - } - s := win.NameAbs() - if strings.HasPrefix(s, baseDir) { - s = "." + strings.TrimPrefix(s, baseDir) - s = filepath.Clean(s) - } - return s -} - -func (win *Window) NameAbs() string { - if win.file.name == FnEmptyWin { - return "" - } - s, _ := filepath.Abs(win.file.name) - return s -} - -func (win *Window) NameTag() string { - tstr := win.tagline.text.String() - if tstr == "" { - return "" - } - return strings.Split(tstr, " ")[0] -} - -// Dir returns the working directory of current window, without trailing path separator. -func (win *Window) Dir() string { - if win.isdir { - return win.NameAbs() - } - if win.Name() == FnEmptyWin { - return baseDir - } - return filepath.Dir(win.NameAbs()) -} - -// Flags returns the tagline flags for the window. -// -// modified: ' -func (win *Window) Flags() string { - var flags string - if win.body.dirty { - flags += "'" - } - return flags -} - -// Resize will set new values for position and width height. Meant to be used on a resize event for proper recalculation during the Draw(). -func (win *Window) Resize(x, y, w, h int) { - win.x, win.y, win.w, win.h = x, y, w, h - - win.tagline.Resize(win.x+3, win.y, win.w-3, 1) // 3 for tagbox - win.body.Resize(win.x, win.y+1, win.w, win.h-1) -} - -func (win *Window) UnFocus() { - win.body.focused = true - win.tagline.focused = false -} - -func (win *Window) HandleEvent(ev tcell.Event) { - switch ev := ev.(type) { - case *tcell.EventMouse: - _, my := ev.Position() - - // Set focus to either tagline or body - if my > win.tagline.y+win.tagline.h-1 { - win.tagline.focused = false - } else { - win.tagline.focused = true - } - case *tcell.EventKey: - switch ev.Key() { - case tcell.KeyCtrlS: // save - _, err := win.SaveFile() - if err != nil { - printMsg("%s\n", err) - } - return - } - } - - // Pass along the event down to current view, if we have not already done something and returned in the switch above. - if win.tagline.focused { - win.tagline.HandleEvent(ev) - } else { - win.body.HandleEvent(ev) - } -} - -func (win *Window) Draw() { - // Draw tag square - boxstyle := tagSquareStyle - if win.body.dirty { - boxstyle = tagSquareModifiedStyle - } - screen.SetContent(win.x, win.y, ' ', nil, boxstyle) - screen.SetContent(win.x+1, win.y, ' ', nil, boxstyle) - screen.SetContent(win.x+2, win.y, ' ', nil, win.tagline.style) - - // Tagline - win.tagline.Draw() - - // Main text buffer - win.body.Draw() -} - -func (win *Window) CanClose() bool { - ok := !win.body.dirty || win.qcnt > 0 - if !ok { - name := win.Name() - if name == FnEmptyWin { - name = "unnamed file" - } - printMsg("%s modified\n", name) - win.qcnt++ - } - return ok -} - -func (win *Window) Close() { - if !win.CanClose() { - return - } - win.col.CloseWindow(win) -} |