9fans.net/go@v0.0.7/cmd/acme/internal/ui/ui.go (about) 1 // #include <u.h> 2 // #include <libc.h> 3 // #include <draw.h> 4 // #include <thread.h> 5 // #include <cursor.h> 6 // #include <mouse.h> 7 // #include <keyboard.h> 8 // #include <frame.h> 9 // #include <fcall.h> 10 // #include <plumb.h> 11 // #include <libsec.h> 12 // #include "dat.h" 13 // #include "fns.h" 14 15 package ui 16 17 import ( 18 "unicode/utf8" 19 20 "9fans.net/go/cmd/acme/internal/adraw" 21 "9fans.net/go/cmd/acme/internal/bufs" 22 "9fans.net/go/cmd/acme/internal/disk" 23 "9fans.net/go/cmd/acme/internal/runes" 24 "9fans.net/go/cmd/acme/internal/util" 25 "9fans.net/go/cmd/acme/internal/wind" 26 "9fans.net/go/draw" 27 ) 28 29 func savemouse(w *wind.Window) { 30 prevmouse = Mouse.Point 31 mousew = w 32 } 33 34 func restoremouse(w *wind.Window) int { 35 did := 0 36 if mousew != nil && mousew == w { 37 adraw.Display.MoveCursor(prevmouse) 38 did = 1 39 } 40 mousew = nil 41 return did 42 } 43 44 func movetodel(w *wind.Window) { 45 n := wind.Delrunepos(w) 46 if n < 0 { 47 return 48 } 49 adraw.Display.MoveCursor(w.Tag.Fr.PointOf(n).Add(draw.Pt(4, w.Tag.Fr.Font.Height-4))) 50 } 51 52 func Clearmouse() { 53 mousew = nil 54 } 55 56 var Mouse *draw.Mouse 57 58 var Mousectl *draw.Mousectl 59 60 func Winmousebut(w *wind.Window) { 61 adraw.Display.MoveCursor(w.Tag.ScrollR.Min.Add(draw.Pt(w.Tag.ScrollR.Dx(), adraw.Font.Height).Div(2))) 62 } 63 64 var mousew *wind.Window 65 66 var prevmouse draw.Point 67 68 func XCut(et, t, _ *wind.Text, dosnarf, docut bool, _ []rune) { 69 70 /* 71 * if not executing a mouse chord (et != t) and snarfing (dosnarf) 72 * and executed Cut or Snarf in window tag (et->w != nil), 73 * then use the window body selection or the tag selection 74 * or do nothing at all. 75 */ 76 if et != t && dosnarf && et.W != nil { 77 if et.W.Body.Q1 > et.W.Body.Q0 { 78 t = &et.W.Body 79 if docut { 80 t.File.Mark() // seq has been incremented by execute 81 } 82 } else if et.W.Tag.Q1 > et.W.Tag.Q0 { 83 t = &et.W.Tag 84 } else { 85 t = nil 86 } 87 } 88 if t == nil { // no selection 89 return 90 } 91 92 locked := false 93 if t.W != nil && et.W != t.W { 94 locked = true 95 c := 'M' 96 if et.W != nil { 97 c = et.W.Owner 98 } 99 wind.Winlock(t.W, c) 100 } 101 if t.Q0 == t.Q1 { 102 if locked { 103 wind.Winunlock(t.W) 104 } 105 return 106 } 107 if dosnarf { 108 q0 := t.Q0 109 q1 := t.Q1 110 snarfbuf.Delete(0, snarfbuf.Len()) 111 r := bufs.AllocRunes() 112 for q0 < q1 { 113 n := q1 - q0 114 if n > bufs.RuneLen { 115 n = bufs.RuneLen 116 } 117 t.File.Read(q0, r[:n]) 118 snarfbuf.Insert(snarfbuf.Len(), r[:n]) 119 q0 += n 120 } 121 bufs.FreeRunes(r) 122 acmeputsnarf() 123 } 124 if docut { 125 wind.Textdelete(t, t.Q0, t.Q1, true) 126 wind.Textsetselect(t, t.Q0, t.Q0) 127 if t.W != nil { 128 wind.Textscrdraw(t) 129 wind.Winsettag(t.W) 130 } 131 } else if dosnarf { // Snarf command 132 wind.Argtext = t 133 } 134 if locked { 135 wind.Winunlock(t.W) 136 } 137 } 138 139 func XPaste(et, t, _ *wind.Text, selectall, tobody bool, _ []rune) { 140 141 // if(tobody), use body of executing window (Paste or Send command) 142 if tobody && et != nil && et.W != nil { 143 t = &et.W.Body 144 t.File.Mark() // seq has been incremented by execute 145 } 146 if t == nil { 147 return 148 } 149 150 acmegetsnarf() 151 if t == nil || snarfbuf.Len() == 0 { 152 return 153 } 154 if t.W != nil && et.W != t.W { 155 c := 'M' 156 if et.W != nil { 157 c = et.W.Owner 158 } 159 wind.Winlock(t.W, c) 160 } 161 XCut(t, t, nil, false, true, nil) 162 q := 0 163 q0 := t.Q0 164 q1 := t.Q0 + snarfbuf.Len() 165 r := bufs.AllocRunes() 166 for q0 < q1 { 167 n := q1 - q0 168 if n > bufs.RuneLen { 169 n = bufs.RuneLen 170 } 171 snarfbuf.Read(q, r[:n]) 172 wind.Textinsert(t, q0, r[:n], true) 173 q += n 174 q0 += n 175 } 176 bufs.FreeRunes(r) 177 if selectall { 178 wind.Textsetselect(t, t.Q0, q1) 179 } else { 180 wind.Textsetselect(t, q1, q1) 181 } 182 if t.W != nil { 183 wind.Textscrdraw(t) 184 wind.Winsettag(t.W) 185 } 186 if t.W != nil && et.W != t.W { 187 wind.Winunlock(t.W) 188 } 189 } 190 191 func XUndo(et, _, _ *wind.Text, isundo, _ bool, _ []rune) { 192 if et == nil || et.W == nil { 193 return 194 } 195 seq := seqof(et.W, isundo) 196 if seq == 0 { 197 // nothing to undo 198 return 199 } 200 /* 201 * Undo the executing window first. Its display will update. other windows 202 * in the same file will not call show() and jump to a different location in the file. 203 * Simultaneous changes to other files will be chaotic, however. 204 */ 205 wind.Winundo(et.W, isundo) 206 for i := 0; i < len(wind.TheRow.Col); i++ { 207 c := wind.TheRow.Col[i] 208 for j := 0; j < len(c.W); j++ { 209 w := c.W[j] 210 if w == et.W { 211 continue 212 } 213 if seqof(w, isundo) == seq { 214 wind.Winundo(w, isundo) 215 } 216 } 217 } 218 } 219 220 const ( 221 Kscrolloneup = draw.KeyFn | 0x20 222 Kscrollonedown = draw.KeyFn | 0x21 223 ) 224 225 /* 226 * /dev/snarf updates when the file is closed, so we must open our own 227 * fd here rather than use snarffd 228 */ 229 230 /* rio truncates larges snarf buffers, so this avoids using the 231 * service if the string is huge */ 232 233 const MAXSNARF = 100 * 1024 234 235 const ( 236 NSnarf = 1000 237 ) 238 239 var snarfrune [NSnarf + 1]rune 240 241 var snarfbuf disk.Buffer 242 243 func acmeputsnarf() { 244 if snarfbuf.Len() == 0 { 245 return 246 } 247 if snarfbuf.Len() > MAXSNARF { 248 return 249 } 250 251 var buf []byte 252 var n int 253 for i := 0; i < snarfbuf.Len(); i += n { 254 n = snarfbuf.Len() - i 255 if n >= NSnarf { 256 n = NSnarf 257 } 258 snarfbuf.Read(i, snarfrune[:n]) 259 var rbuf [utf8.UTFMax]byte 260 for _, r := range snarfrune[:n] { 261 w := utf8.EncodeRune(rbuf[:], r) 262 buf = append(buf, rbuf[:w]...) 263 } 264 } 265 if len(buf) > 0 { 266 adraw.Display.WriteSnarf(buf) 267 } 268 } 269 270 func acmegetsnarf() { 271 _, m, err := adraw.Display.ReadSnarf(nil) 272 if err != nil { 273 return 274 } 275 buf := make([]byte, m+100) 276 n, _, err := adraw.Display.ReadSnarf(buf) 277 if n == 0 || err != nil { 278 return 279 } 280 buf = buf[:n] 281 282 r := make([]rune, utf8.RuneCount(buf)) 283 _, nr, _ := runes.Convert(buf, r, true) 284 snarfbuf.Reset() 285 snarfbuf.Insert(0, r[:nr]) 286 } 287 288 func seqof(w *wind.Window, isundo bool) int { 289 // if it's undo, see who changed with us 290 if isundo { 291 return w.Body.File.Seq() 292 } 293 // if it's redo, see who we'll be sync'ed up with 294 return w.Body.File.RedoSeq() 295 } 296 297 /* 298 * Heuristic city. 299 */ 300 func Makenewwindow(t *wind.Text) *wind.Window { 301 var c *wind.Column 302 if wind.Activecol != nil { 303 c = wind.Activecol 304 } else if wind.Seltext != nil && wind.Seltext.Col != nil { 305 c = wind.Seltext.Col 306 } else if t != nil && t.Col != nil { 307 c = t.Col 308 } else { 309 if len(wind.TheRow.Col) == 0 && wind.RowAdd(&wind.TheRow, nil, -1) == nil { 310 util.Fatal("can't make column") 311 } 312 c = wind.TheRow.Col[len(wind.TheRow.Col)-1] 313 } 314 wind.Activecol = c 315 if t == nil || t.W == nil || len(c.W) == 0 { 316 return ColaddAndMouse(c, nil, nil, -1) 317 } 318 319 // find biggest window and biggest blank spot 320 emptyw := c.W[0] 321 bigw := emptyw 322 var w *wind.Window 323 for i := 1; i < len(c.W); i++ { 324 w = c.W[i] 325 // use >= to choose one near bottom of screen 326 if w.Body.Fr.MaxLines >= bigw.Body.Fr.MaxLines { 327 bigw = w 328 } 329 if w.Body.Fr.MaxLines-w.Body.Fr.NumLines >= emptyw.Body.Fr.MaxLines-emptyw.Body.Fr.NumLines { 330 emptyw = w 331 } 332 } 333 emptyb := &emptyw.Body 334 el := emptyb.Fr.MaxLines - emptyb.Fr.NumLines 335 var y int 336 // if empty space is big, use it 337 if el > 15 || (el > 3 && el > (bigw.Body.Fr.MaxLines-1)/2) { 338 y = emptyb.Fr.R.Min.Y + emptyb.Fr.NumLines*adraw.Font.Height 339 } else { 340 // if this window is in column and isn't much smaller, split it 341 if t.Col == c && t.W.R.Dy() > 2*bigw.R.Dy()/3 { 342 bigw = t.W 343 } 344 y = (bigw.R.Min.Y + bigw.R.Max.Y) / 2 345 } 346 w = ColaddAndMouse(c, nil, nil, y) 347 if w.Body.Fr.MaxLines < 2 { 348 wind.Colgrow(w.Col, w, 1) 349 } 350 return w 351 }