1 /* $Id: window-choose.c,v 1.1.1.2 2011/08/17 18:40:05 jmmv Exp $ */ 2 3 /* 4 * Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net> 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 <sys/types.h> 20 21 #include <string.h> 22 23 #include "tmux.h" 24 25 struct screen *window_choose_init(struct window_pane *); 26 void window_choose_free(struct window_pane *); 27 void window_choose_resize(struct window_pane *, u_int, u_int); 28 void window_choose_key(struct window_pane *, struct session *, int); 29 void window_choose_mouse( 30 struct window_pane *, struct session *, struct mouse_event *); 31 32 void window_choose_redraw_screen(struct window_pane *); 33 void window_choose_write_line( 34 struct window_pane *, struct screen_write_ctx *, u_int); 35 36 void window_choose_scroll_up(struct window_pane *); 37 void window_choose_scroll_down(struct window_pane *); 38 39 const struct window_mode window_choose_mode = { 40 window_choose_init, 41 window_choose_free, 42 window_choose_resize, 43 window_choose_key, 44 window_choose_mouse, 45 NULL, 46 }; 47 48 struct window_choose_mode_item { 49 char *name; 50 int idx; 51 }; 52 53 struct window_choose_mode_data { 54 struct screen screen; 55 56 struct mode_key_data mdata; 57 58 ARRAY_DECL(, struct window_choose_mode_item) list; 59 u_int top; 60 u_int selected; 61 62 void (*callbackfn)(void *, int); 63 void (*freefn)(void *); 64 void *data; 65 }; 66 67 int window_choose_key_index(struct window_choose_mode_data *, u_int); 68 int window_choose_index_key(struct window_choose_mode_data *, int); 69 70 void 71 window_choose_vadd(struct window_pane *wp, int idx, const char *fmt, va_list ap) 72 { 73 struct window_choose_mode_data *data = wp->modedata; 74 struct window_choose_mode_item *item; 75 76 ARRAY_EXPAND(&data->list, 1); 77 item = &ARRAY_LAST(&data->list); 78 xvasprintf(&item->name, fmt, ap); 79 item->idx = idx; 80 } 81 82 void printflike3 83 window_choose_add(struct window_pane *wp, int idx, const char *fmt, ...) 84 { 85 va_list ap; 86 87 va_start(ap, fmt); 88 window_choose_vadd(wp, idx, fmt, ap); 89 va_end(ap); 90 } 91 92 void 93 window_choose_ready(struct window_pane *wp, u_int cur, 94 void (*callbackfn)(void *, int), void (*freefn)(void *), void *cdata) 95 { 96 struct window_choose_mode_data *data = wp->modedata; 97 struct screen *s = &data->screen; 98 99 data->selected = cur; 100 if (data->selected > screen_size_y(s) - 1) 101 data->top = ARRAY_LENGTH(&data->list) - screen_size_y(s); 102 103 data->callbackfn = callbackfn; 104 data->freefn = freefn; 105 data->data = cdata; 106 107 window_choose_redraw_screen(wp); 108 } 109 110 struct screen * 111 window_choose_init(struct window_pane *wp) 112 { 113 struct window_choose_mode_data *data; 114 struct screen *s; 115 int keys; 116 117 wp->modedata = data = xmalloc(sizeof *data); 118 119 data->callbackfn = NULL; 120 data->freefn = NULL; 121 data->data = NULL; 122 123 ARRAY_INIT(&data->list); 124 data->top = 0; 125 126 s = &data->screen; 127 screen_init(s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0); 128 s->mode &= ~MODE_CURSOR; 129 if (options_get_number(&wp->window->options, "mode-mouse")) 130 s->mode |= MODE_MOUSE_STANDARD; 131 132 keys = options_get_number(&wp->window->options, "mode-keys"); 133 if (keys == MODEKEY_EMACS) 134 mode_key_init(&data->mdata, &mode_key_tree_emacs_choice); 135 else 136 mode_key_init(&data->mdata, &mode_key_tree_vi_choice); 137 138 return (s); 139 } 140 141 void 142 window_choose_free(struct window_pane *wp) 143 { 144 struct window_choose_mode_data *data = wp->modedata; 145 u_int i; 146 147 if (data->freefn != NULL && data->data != NULL) 148 data->freefn(data->data); 149 150 for (i = 0; i < ARRAY_LENGTH(&data->list); i++) 151 xfree(ARRAY_ITEM(&data->list, i).name); 152 ARRAY_FREE(&data->list); 153 154 screen_free(&data->screen); 155 xfree(data); 156 } 157 158 void 159 window_choose_resize(struct window_pane *wp, u_int sx, u_int sy) 160 { 161 struct window_choose_mode_data *data = wp->modedata; 162 struct screen *s = &data->screen; 163 164 data->top = 0; 165 if (data->selected > sy - 1) 166 data->top = data->selected - (sy - 1); 167 168 screen_resize(s, sx, sy); 169 window_choose_redraw_screen(wp); 170 } 171 172 /* ARGSUSED */ 173 void 174 window_choose_key(struct window_pane *wp, unused struct session *sess, int key) 175 { 176 struct window_choose_mode_data *data = wp->modedata; 177 struct screen *s = &data->screen; 178 struct screen_write_ctx ctx; 179 struct window_choose_mode_item *item; 180 u_int items; 181 int idx; 182 183 items = ARRAY_LENGTH(&data->list); 184 185 switch (mode_key_lookup(&data->mdata, key)) { 186 case MODEKEYCHOICE_CANCEL: 187 data->callbackfn(data->data, -1); 188 window_pane_reset_mode(wp); 189 break; 190 case MODEKEYCHOICE_CHOOSE: 191 item = &ARRAY_ITEM(&data->list, data->selected); 192 data->callbackfn(data->data, item->idx); 193 window_pane_reset_mode(wp); 194 break; 195 case MODEKEYCHOICE_UP: 196 if (items == 0) 197 break; 198 if (data->selected == 0) { 199 data->selected = items - 1; 200 if (data->selected > screen_size_y(s) - 1) 201 data->top = items - screen_size_y(s); 202 window_choose_redraw_screen(wp); 203 break; 204 } 205 data->selected--; 206 if (data->selected < data->top) 207 window_choose_scroll_up(wp); 208 else { 209 screen_write_start(&ctx, wp, NULL); 210 window_choose_write_line( 211 wp, &ctx, data->selected - data->top); 212 window_choose_write_line( 213 wp, &ctx, data->selected + 1 - data->top); 214 screen_write_stop(&ctx); 215 } 216 break; 217 case MODEKEYCHOICE_DOWN: 218 if (items == 0) 219 break; 220 if (data->selected == items - 1) { 221 data->selected = 0; 222 data->top = 0; 223 window_choose_redraw_screen(wp); 224 break; 225 } 226 data->selected++; 227 228 if (data->selected < data->top + screen_size_y(s)) { 229 screen_write_start(&ctx, wp, NULL); 230 window_choose_write_line( 231 wp, &ctx, data->selected - data->top); 232 window_choose_write_line( 233 wp, &ctx, data->selected - 1 - data->top); 234 screen_write_stop(&ctx); 235 } else 236 window_choose_scroll_down(wp); 237 break; 238 case MODEKEYCHOICE_SCROLLUP: 239 if (items == 0 || data->top == 0) 240 break; 241 if (data->selected == data->top + screen_size_y(s) - 1) { 242 data->selected--; 243 window_choose_scroll_up(wp); 244 screen_write_start(&ctx, wp, NULL); 245 window_choose_write_line( 246 wp, &ctx, screen_size_y(s) - 1); 247 screen_write_stop(&ctx); 248 } else 249 window_choose_scroll_up(wp); 250 break; 251 case MODEKEYCHOICE_SCROLLDOWN: 252 if (items == 0 || 253 data->top + screen_size_y(&data->screen) >= items) 254 break; 255 if (data->selected == data->top) { 256 data->selected++; 257 window_choose_scroll_down(wp); 258 screen_write_start(&ctx, wp, NULL); 259 window_choose_write_line(wp, &ctx, 0); 260 screen_write_stop(&ctx); 261 } else 262 window_choose_scroll_down(wp); 263 break; 264 case MODEKEYCHOICE_PAGEUP: 265 if (data->selected < screen_size_y(s)) { 266 data->selected = 0; 267 data->top = 0; 268 } else { 269 data->selected -= screen_size_y(s); 270 if (data->top < screen_size_y(s)) 271 data->top = 0; 272 else 273 data->top -= screen_size_y(s); 274 } 275 window_choose_redraw_screen(wp); 276 break; 277 case MODEKEYCHOICE_PAGEDOWN: 278 data->selected += screen_size_y(s); 279 if (data->selected > items - 1) 280 data->selected = items - 1; 281 data->top += screen_size_y(s); 282 if (screen_size_y(s) < items) { 283 if (data->top + screen_size_y(s) > items) 284 data->top = items - screen_size_y(s); 285 } else 286 data->top = 0; 287 if (data->selected < data->top) 288 data->top = data->selected; 289 window_choose_redraw_screen(wp); 290 break; 291 default: 292 idx = window_choose_index_key(data, key); 293 if (idx < 0 || (u_int) idx >= ARRAY_LENGTH(&data->list)) 294 break; 295 data->selected = idx; 296 297 item = &ARRAY_ITEM(&data->list, data->selected); 298 data->callbackfn(data->data, item->idx); 299 window_pane_reset_mode(wp); 300 break; 301 } 302 } 303 304 /* ARGSUSED */ 305 void 306 window_choose_mouse( 307 struct window_pane *wp, unused struct session *sess, struct mouse_event *m) 308 { 309 struct window_choose_mode_data *data = wp->modedata; 310 struct screen *s = &data->screen; 311 struct window_choose_mode_item *item; 312 u_int idx; 313 314 if ((m->b & 3) == 3) 315 return; 316 if (m->x >= screen_size_x(s)) 317 return; 318 if (m->y >= screen_size_y(s)) 319 return; 320 321 idx = data->top + m->y; 322 if (idx >= ARRAY_LENGTH(&data->list)) 323 return; 324 data->selected = idx; 325 326 item = &ARRAY_ITEM(&data->list, data->selected); 327 data->callbackfn(data->data, item->idx); 328 window_pane_reset_mode(wp); 329 } 330 331 void 332 window_choose_write_line( 333 struct window_pane *wp, struct screen_write_ctx *ctx, u_int py) 334 { 335 struct window_choose_mode_data *data = wp->modedata; 336 struct window_choose_mode_item *item; 337 struct options *oo = &wp->window->options; 338 struct screen *s = &data->screen; 339 struct grid_cell gc; 340 int utf8flag, key; 341 342 if (data->callbackfn == NULL) 343 fatalx("called before callback assigned"); 344 345 utf8flag = options_get_number(&wp->window->options, "utf8"); 346 memcpy(&gc, &grid_default_cell, sizeof gc); 347 if (data->selected == data->top + py) { 348 colour_set_fg(&gc, options_get_number(oo, "mode-fg")); 349 colour_set_bg(&gc, options_get_number(oo, "mode-bg")); 350 gc.attr |= options_get_number(oo, "mode-attr"); 351 } 352 353 screen_write_cursormove(ctx, 0, py); 354 if (data->top + py < ARRAY_LENGTH(&data->list)) { 355 item = &ARRAY_ITEM(&data->list, data->top + py); 356 key = window_choose_key_index(data, data->top + py); 357 if (key != -1) { 358 screen_write_nputs(ctx, screen_size_x(s) - 1, 359 &gc, utf8flag, "(%c) %s", key, item->name); 360 } else { 361 screen_write_nputs(ctx, screen_size_x(s) - 1, 362 &gc, utf8flag, " %s", item->name); 363 } 364 365 } 366 while (s->cx < screen_size_x(s)) 367 screen_write_putc(ctx, &gc, ' '); 368 } 369 370 int 371 window_choose_key_index(struct window_choose_mode_data *data, u_int idx) 372 { 373 static const char keys[] = "0123456789abcdefghijklmnopqrstuvwxyz"; 374 const char *ptr; 375 int mkey; 376 377 for (ptr = keys; *ptr != '\0'; ptr++) { 378 mkey = mode_key_lookup(&data->mdata, *ptr); 379 if (mkey != MODEKEY_NONE && mkey != MODEKEY_OTHER) 380 continue; 381 if (idx-- == 0) 382 return (*ptr); 383 } 384 return (-1); 385 } 386 387 int 388 window_choose_index_key(struct window_choose_mode_data *data, int key) 389 { 390 static const char keys[] = "0123456789abcdefghijklmnopqrstuvwxyz"; 391 const char *ptr; 392 int mkey; 393 u_int idx = 0; 394 395 for (ptr = keys; *ptr != '\0'; ptr++) { 396 mkey = mode_key_lookup(&data->mdata, *ptr); 397 if (mkey != MODEKEY_NONE && mkey != MODEKEY_OTHER) 398 continue; 399 if (key == *ptr) 400 return (idx); 401 idx++; 402 } 403 return (-1); 404 } 405 406 void 407 window_choose_redraw_screen(struct window_pane *wp) 408 { 409 struct window_choose_mode_data *data = wp->modedata; 410 struct screen *s = &data->screen; 411 struct screen_write_ctx ctx; 412 u_int i; 413 414 screen_write_start(&ctx, wp, NULL); 415 for (i = 0; i < screen_size_y(s); i++) 416 window_choose_write_line(wp, &ctx, i); 417 screen_write_stop(&ctx); 418 } 419 420 void 421 window_choose_scroll_up(struct window_pane *wp) 422 { 423 struct window_choose_mode_data *data = wp->modedata; 424 struct screen_write_ctx ctx; 425 426 if (data->top == 0) 427 return; 428 data->top--; 429 430 screen_write_start(&ctx, wp, NULL); 431 screen_write_cursormove(&ctx, 0, 0); 432 screen_write_insertline(&ctx, 1); 433 window_choose_write_line(wp, &ctx, 0); 434 if (screen_size_y(&data->screen) > 1) 435 window_choose_write_line(wp, &ctx, 1); 436 screen_write_stop(&ctx); 437 } 438 439 void 440 window_choose_scroll_down(struct window_pane *wp) 441 { 442 struct window_choose_mode_data *data = wp->modedata; 443 struct screen *s = &data->screen; 444 struct screen_write_ctx ctx; 445 446 if (data->top >= ARRAY_LENGTH(&data->list)) 447 return; 448 data->top++; 449 450 screen_write_start(&ctx, wp, NULL); 451 screen_write_cursormove(&ctx, 0, 0); 452 screen_write_deleteline(&ctx, 1); 453 window_choose_write_line(wp, &ctx, screen_size_y(s) - 1); 454 if (screen_size_y(&data->screen) > 1) 455 window_choose_write_line(wp, &ctx, screen_size_y(s) - 2); 456 screen_write_stop(&ctx); 457 } 458