jcs ratpoison hax
at jcs 584 lines 15 kB view raw
1/* Copyright (C) 2000, 2001, 2002, 2003, 2004 Shawn Betts <sabetts@vcn.bc.ca> 2 * 3 * This file is part of ratpoison. 4 * 5 * ratpoison is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 2, or (at your option) 8 * any later version. 9 * 10 * ratpoison is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this software; see the file COPYING. If not, write to 17 * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, 18 * Boston, MA 02111-1307 USA 19 */ 20 21#include <ctype.h> 22#include <stdlib.h> 23#include <stdio.h> 24#include <string.h> 25#include <unistd.h> 26#include <X11/Xlib.h> 27#include <X11/keysym.h> 28#include <X11/Xutil.h> 29#include <X11/Xatom.h> 30 31#include "ratpoison.h" 32 33/* bind functions */ 34static edit_status editor_forward_char (rp_input_line *line); 35static edit_status editor_backward_char (rp_input_line *line); 36static edit_status editor_forward_word (rp_input_line *line); 37static edit_status editor_backward_word (rp_input_line *line); 38static edit_status editor_beginning_of_line (rp_input_line *line); 39static edit_status editor_end_of_line (rp_input_line *line); 40static edit_status editor_delete_char (rp_input_line *line); 41static edit_status editor_backward_delete_char (rp_input_line *line); 42static edit_status editor_kill_word (rp_input_line *line); 43static edit_status editor_backward_kill_word (rp_input_line *line); 44static edit_status editor_kill_line (rp_input_line *line); 45static edit_status editor_paste_selection (rp_input_line *line); 46static edit_status editor_abort (rp_input_line *line); 47static edit_status editor_no_action (rp_input_line *line); 48static edit_status editor_enter (rp_input_line *line); 49static edit_status editor_history_previous (rp_input_line *line); 50static edit_status editor_history_next (rp_input_line *line); 51static edit_status editor_backward_kill_line (rp_input_line *line); 52static edit_status editor_complete_prev (rp_input_line *line); 53static edit_status editor_complete_next (rp_input_line *line); 54 55/* default edit action */ 56static edit_status editor_insert (rp_input_line *line, char *keysym_buf); 57 58 59static char *saved_command = NULL; 60 61typedef struct edit_binding edit_binding; 62 63struct edit_binding 64{ 65 struct rp_key key; 66 edit_status (*func)(rp_input_line *); 67}; 68 69static edit_binding edit_bindings[] = 70 { {{XK_g, RP_CONTROL_MASK}, editor_abort}, 71 {{XK_Escape, 0}, editor_abort}, 72 {{XK_f, RP_CONTROL_MASK}, editor_forward_char}, 73 {{XK_Right, 0}, editor_forward_char}, 74 {{XK_b, RP_CONTROL_MASK}, editor_backward_char}, 75 {{XK_Left, 0}, editor_backward_char}, 76 {{XK_f, RP_META_MASK}, editor_forward_word}, 77 {{XK_b, RP_META_MASK}, editor_backward_word}, 78 {{XK_a, RP_CONTROL_MASK}, editor_beginning_of_line}, 79 {{XK_Home, 0}, editor_beginning_of_line}, 80 {{XK_e, RP_CONTROL_MASK}, editor_end_of_line}, 81 {{XK_End, 0}, editor_end_of_line}, 82 {{XK_d, RP_CONTROL_MASK}, editor_delete_char}, 83 {{XK_Delete, 0}, editor_delete_char}, 84 {{XK_BackSpace, 0}, editor_backward_delete_char}, 85 {{XK_h, RP_CONTROL_MASK}, editor_backward_delete_char}, 86 {{XK_BackSpace, RP_META_MASK}, editor_backward_kill_word}, 87 {{XK_d, RP_META_MASK}, editor_kill_word}, 88 {{XK_k, RP_CONTROL_MASK}, editor_kill_line}, 89 {{XK_u, RP_CONTROL_MASK}, editor_backward_kill_line}, 90 {{XK_y, RP_CONTROL_MASK}, editor_paste_selection}, 91 {{XK_p, RP_CONTROL_MASK}, editor_history_previous}, 92 {{XK_Up, 0}, editor_history_previous}, 93 {{XK_n, RP_CONTROL_MASK}, editor_history_next}, 94 {{XK_Down, 0}, editor_history_next}, 95 {{XK_Return, 0}, editor_enter}, 96 {{XK_m, RP_CONTROL_MASK}, editor_enter}, 97 {{XK_KP_Enter, 0}, editor_enter}, 98 {{XK_Tab, 0}, editor_complete_next}, 99 {{XK_ISO_Left_Tab, 0}, editor_complete_prev}, 100 { {0, 0}, 0} }; 101 102rp_input_line * 103input_line_new (char *prompt, char *preinput, int history_id, 104 enum completion_styles style, completion_fn fn) 105{ 106 rp_input_line *line; 107 size_t length; 108 109 line = xmalloc (sizeof (rp_input_line)); 110 line->prompt = prompt; 111 line->compl = completions_new (fn, style); 112 line->history_id = history_id; 113 114 /* Allocate some memory to start with (100 extra bytes) */ 115 length = strlen (preinput); 116 line->size = length + 1 + 100; 117 line->buffer = xmalloc (line->size); 118 119 /* load in the preinput */ 120 memcpy (line->buffer, preinput, length); 121 line->buffer[length] = '\0'; 122 line->position = line->length = length; 123 124 return line; 125} 126 127void 128input_line_free (rp_input_line *line) 129{ 130 completions_free (line->compl); 131 free (line->buffer); 132 free (line); 133} 134 135edit_status 136execute_edit_action (rp_input_line *line, KeySym ch, unsigned int modifier, char *keysym_buf) 137{ 138 struct edit_binding *binding = NULL; 139 int found_binding = 0; 140 edit_status status; 141 142 for (binding = edit_bindings; binding->func; binding++) 143 { 144 if (ch == binding->key.sym && modifier == binding->key.state) 145 { 146 found_binding = 1; 147 break; 148 } 149 } 150 151 if (found_binding) 152 status = binding->func (line); 153 else if (modifier) 154 status = editor_no_action (line); 155 else 156 status = editor_insert (line, keysym_buf); 157 158 return status; 159} 160 161static edit_status 162editor_forward_char (rp_input_line *line) 163{ 164 if (line->position == line->length) 165 return EDIT_NO_OP; 166 167 if (isu8start (line->buffer[line->position])) 168 { 169 do 170 line->position++; 171 while (isu8cont (line->buffer[line->position])); 172 } 173 else 174 line->position++; 175 176 return EDIT_MOVE; 177} 178 179static edit_status 180editor_backward_char (rp_input_line *line) 181{ 182 if (line->position == 0) 183 return EDIT_NO_OP; 184 185 do 186 line->position--; 187 while (line->position > 0 && isu8cont (line->buffer[line->position])); 188 189 return EDIT_MOVE; 190} 191 192static edit_status 193editor_forward_word (rp_input_line *line) 194{ 195 if (line->position == line->length) 196 return EDIT_NO_OP; 197 198 while (line->position < line->length 199 && !isalnum ((unsigned char)line->buffer[line->position])) 200 line->position++; 201 202 while (line->position < line->length 203 && (isalnum ((unsigned char)line->buffer[line->position]) 204 || isu8char (line->buffer[line->position]))) 205 line->position++; 206 207 return EDIT_MOVE; 208} 209 210static edit_status 211editor_backward_word (rp_input_line *line) 212{ 213 if (line->position == 0) 214 return EDIT_NO_OP; 215 216 while (line->position > 0 && !isalnum ((unsigned char)line->buffer[line->position])) 217 line->position--; 218 219 while (line->position > 0 220 && (isalnum ((unsigned char)line->buffer[line->position]) 221 || isu8char (line->buffer[line->position]))) 222 line->position--; 223 224 return EDIT_MOVE; 225} 226 227static edit_status 228editor_beginning_of_line (rp_input_line *line) 229{ 230 if (line->position == 0) 231 return EDIT_NO_OP; 232 else 233 { 234 line->position = 0; 235 return EDIT_MOVE; 236 } 237} 238 239static edit_status 240editor_end_of_line (rp_input_line *line) 241{ 242 if (line->position == line->length) 243 return EDIT_NO_OP; 244 else 245 { 246 line->position = line->length; 247 return EDIT_MOVE; 248 } 249} 250 251static edit_status 252editor_delete_char (rp_input_line *line) 253{ 254 size_t diff = 0; 255 256 if (line->position == line->length) 257 return EDIT_NO_OP; 258 259 if (isu8start (line->buffer[line->position])) 260 { 261 do 262 diff++; 263 while (isu8cont (line->buffer[line->position + diff])); 264 } 265 else 266 diff++; 267 268 memmove (&line->buffer[line->position], 269 &line->buffer[line->position + diff], 270 line->length - line->position + diff + 1); 271 272 line->length -= diff; 273 274 return EDIT_DELETE; 275} 276 277static edit_status 278editor_backward_delete_char (rp_input_line *line) 279{ 280 size_t diff = 1; 281 282 if (line->position == 0) 283 return EDIT_NO_OP; 284 285 while (line->position - diff > 0 286 && isu8cont (line->buffer[line->position - diff])) 287 diff++; 288 289 memmove (&line->buffer[line->position - diff], 290 &line->buffer[line->position], 291 line->length - line->position + 1); 292 293 line->position -= diff; 294 line->length -= diff; 295 296 return EDIT_DELETE; 297} 298 299static edit_status 300editor_kill_word (rp_input_line *line) 301{ 302 size_t diff = 0; 303 304 if (line->position == line->length) 305 return EDIT_NO_OP; 306 307 while (line->position + diff < line->length && 308 !isalnum ((unsigned char)line->buffer[line->position + diff])) 309 diff++; 310 311 while (line->position + diff < line->length 312 && (isalnum ((unsigned char)line->buffer[line->position + diff]) 313 || isu8char (line->buffer[line->position + diff]))) 314 diff++; 315 316 /* Add the word to the X11 selection. */ 317 set_nselection (&line->buffer[line->position], diff); 318 319 memmove (&line->buffer[line->position], 320 &line->buffer[line->position + diff], 321 line->length - line->position + diff + 1); 322 323 line->length -= diff; 324 325 return EDIT_DELETE; 326} 327 328static edit_status 329editor_backward_kill_word (rp_input_line *line) 330{ 331 size_t diff = 1; 332 333 if (line->position == 0) 334 return EDIT_NO_OP; 335 336 while (line->position - diff > 0 && 337 !isalnum ((unsigned char)line->buffer[line->position - diff])) 338 diff++; 339 340 while (line->position - diff > 0 341 && (isalnum ((unsigned char)line->buffer[line->position - diff]) 342 || isu8char (line->buffer[line->position - diff]))) 343 diff++; 344 345 /* Add the word to the X11 selection. */ 346 set_nselection (&line->buffer[line->position - diff], diff); 347 348 memmove (&line->buffer[line->position - diff], 349 &line->buffer[line->position], 350 line->length - line->position + 1); 351 352 line->position -= diff; 353 line->length -= diff; 354 355 return EDIT_DELETE; 356} 357 358static edit_status 359editor_kill_line (rp_input_line *line) 360{ 361 if (line->position == line->length) 362 return EDIT_NO_OP; 363 364 /* Add the line to the X11 selection. */ 365 set_selection (&line->buffer[line->position]); 366 367 line->length = line->position; 368 line->buffer[line->length] = '\0'; 369 370 return EDIT_DELETE; 371} 372 373/* Do the dirty work of killing a line backwards. */ 374static void 375backward_kill_line (rp_input_line *line) 376{ 377 memmove (&line->buffer[0], 378 &line->buffer[line->position], 379 line->length - line->position + 1); 380 381 line->length -= line->position; 382 line->position = 0; 383} 384 385static edit_status 386editor_backward_kill_line (rp_input_line *line) 387{ 388 if (line->position == 0) 389 return EDIT_NO_OP; 390 391 /* Add the line to the X11 selection. */ 392 set_nselection (line->buffer, line->position); 393 394 backward_kill_line (line); 395 396 return EDIT_DELETE; 397} 398 399static edit_status 400editor_history_previous (rp_input_line *line) 401{ 402 const char *entry = history_previous (line->history_id); 403 404 if (entry) 405 { 406 if (!saved_command) 407 { 408 line->buffer[line->length] = '\0'; 409 saved_command = xstrdup (line->buffer); 410 PRINT_DEBUG (("saved current command line: \'%s\'\n", saved_command)); 411 } 412 413 free (line->buffer); 414 line->buffer = xstrdup (entry); 415 line->length = strlen (line->buffer); 416 line->size = line->length + 1; 417 line->position = line->length; 418 PRINT_DEBUG (("entry: \'%s\'\n", line->buffer)); 419 } 420 else 421 { 422 PRINT_DEBUG (("- do nothing -\n")); 423 return EDIT_NO_OP; 424 } 425 426 return EDIT_INSERT; 427} 428 429static edit_status 430editor_history_next (rp_input_line *line) 431{ 432 const char *entry = history_next (line->history_id); 433 434 if (entry) 435 { 436 free (line->buffer); 437 line->buffer = xstrdup (entry); 438 PRINT_DEBUG (("entry: \'%s\'\n", line->buffer)); 439 } 440 else if (saved_command) 441 { 442 free (line->buffer); 443 line->buffer = saved_command; 444 saved_command = NULL; 445 PRINT_DEBUG (("restored command line: \'%s\'\n", line->buffer)); 446 } 447 else 448 { 449 PRINT_DEBUG (("- do nothing -\n")); 450 return EDIT_NO_OP; 451 } 452 453 line->length = strlen (line->buffer); 454 line->size = line->length + 1; 455 line->position = line->length; 456 457 return EDIT_INSERT; 458} 459 460static edit_status 461editor_abort (rp_input_line *line UNUSED) 462{ 463 return EDIT_ABORT; 464} 465 466static edit_status 467editor_no_action (rp_input_line *line UNUSED) 468{ 469 return EDIT_NO_OP; 470} 471 472static edit_status 473editor_insert (rp_input_line *line, char *keysym_buf) 474{ 475 size_t nbytes; 476 477 PRINT_DEBUG (("keysym_buf: '%s'\n", keysym_buf)); 478 479 nbytes = strlen (keysym_buf); 480 if (line->length + nbytes > line->size - 1) 481 { 482 line->size += nbytes + 100; 483 line->buffer = xrealloc (line->buffer, line->size); 484 } 485 486 memmove (&line->buffer[line->position + nbytes], 487 &line->buffer[line->position], 488 line->length - line->position + 1); 489 memcpy (&line->buffer[line->position], keysym_buf, nbytes); 490 491 line->length += nbytes; 492 line->position += nbytes; 493 494 return EDIT_INSERT; 495} 496 497static edit_status 498editor_enter (rp_input_line *line) 499{ 500 int result; 501 char *expansion; 502 503 line->buffer[line->length] = '\0'; 504 505 if (!defaults.history_expansion) { 506 history_add (line->history_id, line->buffer); 507 return EDIT_DONE; 508 } 509 510 result = history_expand_line (line->history_id, line->buffer, &expansion); 511 512 PRINT_DEBUG (("History Expansion - result: %d\n", result)); 513 PRINT_DEBUG (("History Expansion - expansion: \'%s\'\n", expansion)); 514 515 if (result == -1 || result == 2) 516 { 517 marked_message_printf (0, 0, "%s", expansion); 518 free (expansion); 519 return EDIT_ABORT; 520 } 521 else /* result == 0 || result == 1 */ 522 { 523 history_add (line->history_id, expansion); 524 free (line->buffer); 525 line->buffer = expansion; 526 } 527 528 return EDIT_DONE; 529} 530 531static edit_status 532editor_paste_selection (rp_input_line *line) 533{ 534 char *text; 535 536 text = get_selection (); 537 if (text) 538 { 539 editor_insert (line, text); 540 free (text); 541 return EDIT_INSERT; 542 } 543 else 544 return EDIT_NO_OP; 545} 546 547static edit_status 548editor_complete (rp_input_line *line, int direction) 549{ 550 char *tmp; 551 char *s; 552 553 /* Create our partial string that will be used for completion. It is 554 the characters up to the position of the cursor. */ 555 tmp = xmalloc (line->position + 1); 556 memcpy (tmp, line->buffer, line->position); 557 tmp[line->position] = '\0'; 558 559 /* We don't need to free s because it's a string from the completion 560 list. */ 561 s = completions_complete (line->compl, tmp, direction); 562 free (tmp); 563 564 if (s == NULL) 565 return EDIT_NO_OP; 566 567 /* Insert the completion. */ 568 backward_kill_line (line); 569 editor_insert (line, s); 570 571 return EDIT_COMPLETE; 572} 573 574static edit_status 575editor_complete_next (rp_input_line *line) 576{ 577 return editor_complete (line, COMPLETION_NEXT); 578} 579 580static edit_status 581editor_complete_prev (rp_input_line *line) 582{ 583 return editor_complete (line, COMPLETION_PREVIOUS); 584}