1 /* $OpenBSD: grid-reader.c,v 1.1 2020/12/22 09:22:14 nicm Exp $ */ 2 3 /* 4 * Copyright (c) 2020 Anindya Mukherjee <anindya49@hotmail.com> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER 15 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 16 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include "tmux.h" 20 21 /* Initialise virtual cursor. */ 22 void 23 grid_reader_start(struct grid_reader *gr, struct grid *gd, u_int cx, u_int cy) 24 { 25 gr->gd = gd; 26 gr->cx = cx; 27 gr->cy = cy; 28 } 29 30 /* Get cursor position from reader. */ 31 void 32 grid_reader_get_cursor(struct grid_reader *gr, u_int *cx, u_int *cy) 33 { 34 *cx = gr->cx; 35 *cy = gr->cy; 36 } 37 38 /* Get length of line containing the cursor. */ 39 u_int 40 grid_reader_line_length(struct grid_reader *gr) 41 { 42 return (grid_line_length(gr->gd, gr->cy)); 43 } 44 45 /* Move cursor forward one position. */ 46 void 47 grid_reader_cursor_right(struct grid_reader *gr, int wrap, int all) 48 { 49 u_int px; 50 struct grid_cell gc; 51 52 if (all) 53 px = gr->gd->sx; 54 else 55 px = grid_reader_line_length(gr); 56 57 if (wrap && gr->cx >= px && gr->cy < gr->gd->hsize + gr->gd->sy - 1) { 58 grid_reader_cursor_start_of_line(gr, 0); 59 grid_reader_cursor_down(gr); 60 } else if (gr->cx < px) { 61 gr->cx++; 62 while (gr->cx < px) { 63 grid_get_cell(gr->gd, gr->cx, gr->cy, &gc); 64 if (~gc.flags & GRID_FLAG_PADDING) 65 break; 66 gr->cx++; 67 } 68 } 69 } 70 71 /* Move cursor back one position. */ 72 void 73 grid_reader_cursor_left(struct grid_reader *gr) 74 { 75 struct grid_cell gc; 76 77 while (gr->cx > 0) { 78 grid_get_cell(gr->gd, gr->cx, gr->cy, &gc); 79 if (~gc.flags & GRID_FLAG_PADDING) 80 break; 81 gr->cx--; 82 } 83 if (gr->cx == 0 && gr->cy > 0) { 84 grid_reader_cursor_up(gr); 85 grid_reader_cursor_end_of_line(gr, 0, 0); 86 } else if (gr->cx > 0) 87 gr->cx--; 88 } 89 90 /* Move cursor down one line. */ 91 void 92 grid_reader_cursor_down(struct grid_reader *gr) 93 { 94 struct grid_cell gc; 95 96 if (gr->cy < gr->gd->hsize + gr->gd->sy - 1) 97 gr->cy++; 98 while (gr->cx > 0) { 99 grid_get_cell(gr->gd, gr->cx, gr->cy, &gc); 100 if (~gc.flags & GRID_FLAG_PADDING) 101 break; 102 gr->cx--; 103 } 104 } 105 106 /* Move cursor up one line. */ 107 void 108 grid_reader_cursor_up(struct grid_reader *gr) 109 { 110 struct grid_cell gc; 111 112 if (gr->cy > 0) 113 gr->cy--; 114 while (gr->cx > 0) { 115 grid_get_cell(gr->gd, gr->cx, gr->cy, &gc); 116 if (~gc.flags & GRID_FLAG_PADDING) 117 break; 118 gr->cx--; 119 } 120 } 121 122 /* Move cursor to the start of the line. */ 123 void 124 grid_reader_cursor_start_of_line(struct grid_reader *gr, int wrap) 125 { 126 if (wrap) { 127 while (gr->cy > 0 && 128 grid_get_line(gr->gd, gr->cy - 1)->flags & 129 GRID_LINE_WRAPPED) 130 gr->cy--; 131 } 132 gr->cx = 0; 133 } 134 135 /* Move cursor to the end of the line. */ 136 void 137 grid_reader_cursor_end_of_line(struct grid_reader *gr, int wrap, int all) 138 { 139 u_int yy; 140 141 if (wrap) { 142 yy = gr->gd->hsize + gr->gd->sy - 1; 143 while (gr->cy < yy && grid_get_line(gr->gd, gr->cy)->flags & 144 GRID_LINE_WRAPPED) 145 gr->cy++; 146 } 147 if (all) 148 gr->cx = gr->gd->sx; 149 else 150 gr->cx = grid_reader_line_length(gr); 151 } 152 153 /* Check if character under cursor is in set. */ 154 int 155 grid_reader_in_set(struct grid_reader *gr, const char *set) 156 { 157 struct grid_cell gc; 158 159 grid_get_cell(gr->gd, gr->cx, gr->cy, &gc); 160 if (gc.flags & GRID_FLAG_PADDING) 161 return (0); 162 return (utf8_cstrhas(set, &gc.data)); 163 } 164 165 /* Move cursor to the start of the next word. */ 166 void 167 grid_reader_cursor_next_word(struct grid_reader *gr, const char *separators) 168 { 169 u_int xx, yy; 170 int expected = 0; 171 172 /* Do not break up wrapped words. */ 173 if (grid_get_line(gr->gd, gr->cy)->flags & GRID_LINE_WRAPPED) 174 xx = grid_reader_line_length(gr) - 1; 175 else 176 xx = grid_reader_line_length(gr); 177 yy = gr->gd->hsize + gr->gd->sy - 1; 178 179 /* 180 * If we started inside a word, skip over word characters. Then skip 181 * over separators till the next word. 182 * 183 * expected is initially set to 0 for the former and then 1 for the 184 * latter. It is finally set to 0 when the beginning of the next word is 185 * found. 186 */ 187 do { 188 while (gr->cx > xx || 189 grid_reader_in_set(gr, separators) == expected) { 190 /* Move down if we are past the end of the line. */ 191 if (gr->cx > xx) { 192 if (gr->cy == yy) 193 return; 194 grid_reader_cursor_start_of_line(gr, 0); 195 grid_reader_cursor_down(gr); 196 197 if (grid_get_line(gr->gd, gr->cy)->flags & 198 GRID_LINE_WRAPPED) 199 xx = grid_reader_line_length(gr) - 1; 200 else 201 xx = grid_reader_line_length(gr); 202 } else 203 gr->cx++; 204 } 205 expected = !expected; 206 } while (expected == 1); 207 } 208 209 /* Move cursor to the end of the next word. */ 210 void 211 grid_reader_cursor_next_word_end(struct grid_reader *gr, const char *separators) 212 { 213 u_int xx, yy; 214 int expected = 1; 215 216 /* Do not break up wrapped words. */ 217 if (grid_get_line(gr->gd, gr->cy)->flags & GRID_LINE_WRAPPED) 218 xx = grid_reader_line_length(gr) - 1; 219 else 220 xx = grid_reader_line_length(gr); 221 yy = gr->gd->hsize + gr->gd->sy - 1; 222 223 /* 224 * If we started on a separator, skip over separators. Then skip over 225 * word characters till the next separator. 226 * 227 * expected is initially set to 1 for the former and then 1 for the 228 * latter. It is finally set to 1 when the end of the next word is 229 * found. 230 */ 231 do { 232 while (gr->cx > xx || 233 grid_reader_in_set(gr, separators) == expected) { 234 /* Move down if we are past the end of the line. */ 235 if (gr->cx > xx) { 236 if (gr->cy == yy) 237 return; 238 grid_reader_cursor_start_of_line(gr, 0); 239 grid_reader_cursor_down(gr); 240 241 if (grid_get_line(gr->gd, gr->cy)->flags & 242 GRID_LINE_WRAPPED) 243 xx = grid_reader_line_length(gr) - 1; 244 else 245 xx = grid_reader_line_length(gr); 246 } else 247 gr->cx++; 248 } 249 expected = !expected; 250 } while (expected == 0); 251 } 252 253 /* Move to the previous place where a word begins. */ 254 void 255 grid_reader_cursor_previous_word(struct grid_reader *gr, const char *separators, 256 int already) 257 { 258 int oldx, oldy, r; 259 260 /* Move back to the previous word character. */ 261 if (already || grid_reader_in_set(gr, separators)) { 262 for (;;) { 263 if (gr->cx > 0) { 264 gr->cx--; 265 if (!grid_reader_in_set(gr, separators)) 266 break; 267 } else { 268 if (gr->cy == 0) 269 return; 270 grid_reader_cursor_up(gr); 271 grid_reader_cursor_end_of_line(gr, 0, 0); 272 273 /* Stop if separator at EOL. */ 274 if (gr->cx > 0) { 275 oldx = gr->cx; 276 gr->cx--; 277 r = grid_reader_in_set(gr, separators); 278 gr->cx = oldx; 279 if (r) 280 break; 281 } 282 } 283 } 284 } 285 286 /* Move back to the beginning of this word. */ 287 do { 288 oldx = gr->cx; 289 oldy = gr->cy; 290 if (gr->cx == 0) { 291 if (gr->cy == 0 || 292 ~grid_get_line(gr->gd, gr->cy - 1)->flags & 293 GRID_LINE_WRAPPED) 294 break; 295 grid_reader_cursor_up(gr); 296 grid_reader_cursor_end_of_line(gr, 0, 0); 297 } 298 if (gr->cx > 0) 299 gr->cx--; 300 } while (!grid_reader_in_set(gr, separators)); 301 gr->cx = oldx; 302 gr->cy = oldy; 303 } 304