// Copyright 2014 The gocui Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package gocui import "github.com/nsf/termbox-go" const MaxInt = int(^uint(0) >> 1) // handleEdit manages the edition mode. We do not handle errors here because if // an error happens, it is enough to keep the view without modifications. func (g *Gui) handleEdit(v *View, ev *termbox.Event) { switch { case ev.Ch != 0 && ev.Mod == 0: v.editWrite(ev.Ch) case ev.Key == termbox.KeySpace: v.editWrite(' ') case ev.Key == termbox.KeyBackspace || ev.Key == termbox.KeyBackspace2: v.editDelete(true) case ev.Key == termbox.KeyDelete: v.editDelete(false) case ev.Key == termbox.KeyInsert: v.overwrite = !v.overwrite case ev.Key == termbox.KeyEnter: v.editNewLine() case ev.Key == termbox.KeyArrowDown: v.moveCursor(0, 1, false) case ev.Key == termbox.KeyArrowUp: v.moveCursor(0, -1, false) case ev.Key == termbox.KeyArrowLeft: v.moveCursor(-1, 0, false) case ev.Key == termbox.KeyArrowRight: v.moveCursor(1, 0, false) } } // editWrite writes a rune at the cursor position. func (v *View) editWrite(ch rune) { v.writeRune(v.cx, v.cy, ch) v.moveCursor(1, 0, true) } // editDelete deletes a rune at the cursor position. back determines // the direction. func (v *View) editDelete(back bool) { x, y := v.ox+v.cx, v.oy+v.cy if y < 0 { return } else if y >= len(v.viewLines) { v.moveCursor(-1, 0, true) return } maxX, _ := v.Size() if back { if x == 0 { // start of the line if y < 1 { return } var maxPrevWidth int if v.Wrap { maxPrevWidth = maxX } else { maxPrevWidth = MaxInt } if v.viewLines[y].linesX == 0 { // regular line v.mergeLines(v.cy - 1) if len(v.viewLines[y-1].line) < maxPrevWidth { v.moveCursor(-1, 0, true) } } else { // wrapped line v.deleteRune(len(v.viewLines[y-1].line)-1, v.cy-1) v.moveCursor(-1, 0, true) } } else { // middle/end of the line v.deleteRune(v.cx-1, v.cy) v.moveCursor(-1, 0, true) } } else { if x == len(v.viewLines[y].line) { // end of the line v.mergeLines(v.cy) } else { // start/middle of the line v.deleteRune(v.cx, v.cy) } } } // editNewLine inserts a new line under the cursor. func (v *View) editNewLine() { v.breakLine(v.cx, v.cy) y := v.oy + v.cy if y >= len(v.viewLines) || (y >= 0 && y < len(v.viewLines) && !(v.Wrap && v.cx == 0 && v.viewLines[y].linesX > 0)) { // new line at the end of the buffer or // cursor is not at the beginning of a wrapped line v.ox = 0 v.cx = 0 v.moveCursor(0, 1, true) } } // moveCursor moves the cursor taking into account the line or view widths and // moves the origin when necessary. func (v *View) moveCursor(dx, dy int, writeMode bool) { maxX, maxY := v.Size() cx, cy := v.cx+dx, v.cy+dy x, y := v.ox+cx, v.oy+cy var curLineWidth, prevLineWidth int // get the width of the current line if writeMode { if v.Wrap { curLineWidth = maxX - 1 } else { curLineWidth = MaxInt } } else { if y >= 0 && y < len(v.viewLines) { curLineWidth = len(v.viewLines[y].line) if v.Wrap && curLineWidth >= maxX { curLineWidth = maxX - 1 } } else { curLineWidth = 0 } } // get the width of the previous line if y-1 >= 0 && y-1 < len(v.viewLines) { prevLineWidth = len(v.viewLines[y-1].line) } else { prevLineWidth = 0 } // adjust cursor's x position and view's x origin if x > curLineWidth { // move to next line if dx > 0 { // horizontal movement if !v.Wrap { v.ox = 0 } v.cx = 0 cy += 1 } else { // vertical movement if curLineWidth > 0 { // move cursor to the EOL if v.Wrap { v.cx = curLineWidth } else { ncx := curLineWidth - v.ox if ncx < 0 { v.ox += ncx if v.ox < 0 { v.ox = 0 } v.cx = 0 } else { v.cx = ncx } } } else { if !v.Wrap { v.ox = 0 } v.cx = 0 } } } else if cx < 0 { if !v.Wrap && v.ox > 0 { // move origin to the left v.ox -= 1 } else { // move to previous line if prevLineWidth > 0 { if !v.Wrap { // set origin so the EOL is visible nox := prevLineWidth - maxX + 1 if nox < 0 { v.ox = 0 } else { v.ox = nox } } v.cx = prevLineWidth } else { if !v.Wrap { v.ox = 0 } v.cx = 0 } cy -= 1 } } else { // stay on the same line if v.Wrap { v.cx = cx } else { if cx >= maxX { v.ox += 1 } else { v.cx = cx } } } // adjust cursor's y position and view's y origin if cy >= maxY { v.oy += 1 } else if cy < 0 { if v.oy > 0 { v.oy -= 1 } } else { v.cy = cy } }