root/lib/widget/input.c

/* [previous][next][first][last][top][bottom][index][help]  */

DEFINITIONS

This source file includes following definitions.
  1. get_history_length
  2. draw_history_button
  3. input_mark_cmd
  4. input_eval_marks
  5. delete_region
  6. do_show_hist
  7. input_history_strip_password
  8. push_history
  9. move_buffer_backward
  10. insert_char
  11. beginning_of_line
  12. end_of_line
  13. backward_char
  14. forward_char
  15. forward_word
  16. backward_word
  17. backward_delete
  18. delete_char
  19. copy_region
  20. kill_word
  21. back_kill_word
  22. yank
  23. kill_line
  24. clear_line
  25. ins_from_clip
  26. hist_prev
  27. hist_next
  28. port_region_marked_for_delete
  29. input_execute_cmd
  30. input_load_history
  31. input_save_history
  32. input_destroy
  33. input_screen_to_point
  34. input_mouse_callback
  35. input_new
  36. input_callback
  37. input_set_default_colors
  38. input_handle_char
  39. input_key_is_in_map
  40. input_assign_text
  41. input_is_empty
  42. input_insert
  43. input_set_point
  44. input_update
  45. input_enable_update
  46. input_disable_update
  47. input_clean
  48. input_free_completions

   1 /*
   2    Widgets for the Midnight Commander
   3 
   4    Copyright (C) 1994-2020
   5    Free Software Foundation, Inc.
   6 
   7    Authors:
   8    Radek Doulik, 1994, 1995
   9    Miguel de Icaza, 1994, 1995
  10    Jakub Jelinek, 1995
  11    Andrej Borsenkow, 1996
  12    Norbert Warmuth, 1997
  13    Andrew Borodin <aborodin@vmail.ru>, 2009-2016
  14 
  15    This file is part of the Midnight Commander.
  16 
  17    The Midnight Commander is free software: you can redistribute it
  18    and/or modify it under the terms of the GNU General Public License as
  19    published by the Free Software Foundation, either version 3 of the License,
  20    or (at your option) any later version.
  21 
  22    The Midnight Commander is distributed in the hope that it will be useful,
  23    but WITHOUT ANY WARRANTY; without even the implied warranty of
  24    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  25    GNU General Public License for more details.
  26 
  27    You should have received a copy of the GNU General Public License
  28    along with this program.  If not, see <http://www.gnu.org/licenses/>.
  29  */
  30 
  31 /** \file input.c
  32  *  \brief Source: WInput widget
  33  */
  34 
  35 #include <config.h>
  36 
  37 #include <stdlib.h>
  38 #include <sys/types.h>
  39 #include <sys/stat.h>
  40 
  41 #include "lib/global.h"
  42 
  43 #include "lib/tty/tty.h"
  44 #include "lib/tty/key.h"        /* XCTRL and ALT macros  */
  45 #include "lib/fileloc.h"
  46 #include "lib/skin.h"
  47 #include "lib/strutil.h"
  48 #include "lib/util.h"
  49 #include "lib/widget.h"
  50 #include "lib/event.h"          /* mc_event_raise() */
  51 #include "lib/mcconfig.h"       /* mc_config_history_*() */
  52 
  53 #include "input_complete.h"
  54 
  55 /*** global variables ****************************************************************************/
  56 
  57 gboolean quote = FALSE;
  58 
  59 const global_keymap_t *input_map = NULL;
  60 
  61 /* Color styles for input widgets */
  62 input_colors_t input_colors;
  63 
  64 /*** file scope macro definitions ****************************************************************/
  65 
  66 #define LARGE_HISTORY_BUTTON 1
  67 
  68 #ifdef LARGE_HISTORY_BUTTON
  69 #define HISTORY_BUTTON_WIDTH 3
  70 #else
  71 #define HISTORY_BUTTON_WIDTH 1
  72 #endif
  73 
  74 #define should_show_history_button(in) \
  75     (in->history.list != NULL && WIDGET (in)->cols > HISTORY_BUTTON_WIDTH * 2 + 1 \
  76          && WIDGET (in)->owner != NULL)
  77 
  78 /*** file scope type declarations ****************************************************************/
  79 
  80 /*** file scope variables ************************************************************************/
  81 
  82 /* Input widgets have a global kill ring */
  83 /* Pointer to killed data */
  84 static char *kill_buffer = NULL;
  85 
  86 /*** file scope functions ************************************************************************/
  87 /* --------------------------------------------------------------------------------------------- */
  88 
  89 static size_t
  90 get_history_length (const GList * history)
     /* [previous][next][first][last][top][bottom][index][help]  */
  91 {
  92     size_t len = 0;
  93 
  94     for (; history != NULL; history = (const GList *) g_list_previous (history))
  95         len++;
  96 
  97     return len;
  98 }
  99 
 100 /* --------------------------------------------------------------------------------------------- */
 101 
 102 static void
 103 draw_history_button (WInput * in)
     /* [previous][next][first][last][top][bottom][index][help]  */
 104 {
 105     char c;
 106     gboolean disabled;
 107 
 108     if (g_list_next (in->history.current) == NULL)
 109         c = '^';
 110     else if (g_list_previous (in->history.current) == NULL)
 111         c = 'v';
 112     else
 113         c = '|';
 114 
 115     widget_gotoyx (in, 0, WIDGET (in)->cols - HISTORY_BUTTON_WIDTH);
 116     disabled = widget_get_state (WIDGET (in), WST_DISABLED);
 117     tty_setcolor (disabled ? DISABLED_COLOR : in->color[WINPUTC_HISTORY]);
 118 
 119 #ifdef LARGE_HISTORY_BUTTON
 120     tty_print_string ("[ ]");
 121     widget_gotoyx (in, 0, WIDGET (in)->cols - HISTORY_BUTTON_WIDTH + 1);
 122 #endif
 123 
 124     tty_print_char (c);
 125 }
 126 
 127 /* --------------------------------------------------------------------------------------------- */
 128 
 129 static void
 130 input_mark_cmd (WInput * in, gboolean mark)
     /* [previous][next][first][last][top][bottom][index][help]  */
 131 {
 132     in->mark = mark ? in->point : -1;
 133 }
 134 
 135 /* --------------------------------------------------------------------------------------------- */
 136 
 137 static gboolean
 138 input_eval_marks (WInput * in, long *start_mark, long *end_mark)
     /* [previous][next][first][last][top][bottom][index][help]  */
 139 {
 140     if (in->mark >= 0)
 141     {
 142         *start_mark = MIN (in->mark, in->point);
 143         *end_mark = MAX (in->mark, in->point);
 144         return TRUE;
 145     }
 146 
 147     *start_mark = *end_mark = -1;
 148     return FALSE;
 149 }
 150 
 151 /* --------------------------------------------------------------------------------------------- */
 152 
 153 static void
 154 delete_region (WInput * in, int x_first, int x_last)
     /* [previous][next][first][last][top][bottom][index][help]  */
 155 {
 156     int first = MIN (x_first, x_last);
 157     int last = MAX (x_first, x_last);
 158 
 159     input_mark_cmd (in, FALSE);
 160     in->point = first;
 161     last = str_offset_to_pos (in->buffer, last);
 162     first = str_offset_to_pos (in->buffer, first);
 163     str_move (in->buffer + first, in->buffer + last);
 164     in->charpoint = 0;
 165     in->need_push = TRUE;
 166 }
 167 
 168 /* --------------------------------------------------------------------------------------------- */
 169 
 170 static void
 171 do_show_hist (WInput * in)
     /* [previous][next][first][last][top][bottom][index][help]  */
 172 {
 173     size_t len;
 174     history_descriptor_t hd;
 175 
 176     len = get_history_length (in->history.list);
 177 
 178     history_descriptor_init (&hd, WIDGET (in)->y, WIDGET (in)->x, in->history.list,
 179                              g_list_position (in->history.list, in->history.list));
 180     history_show (&hd);
 181 
 182     /* in->history.list was destroyed in history_show().
 183      * Apply new history and current postition to avoid use-after-free. */
 184     in->history.list = hd.list;
 185     in->history.current = in->history.list;
 186     if (hd.text != NULL)
 187     {
 188         input_assign_text (in, hd.text);
 189         g_free (hd.text);
 190     }
 191 
 192     /* Has history cleaned up or not? */
 193     if (len != get_history_length (in->history.list))
 194         in->history.changed = TRUE;
 195 }
 196 
 197 /* --------------------------------------------------------------------------------------------- */
 198 /**
 199  * Strip password from incomplete url (just user:pass@host without VFS prefix).
 200  *
 201  * @param url partial URL
 202  * @return newly allocated string without password
 203  */
 204 
 205 static char *
 206 input_history_strip_password (char *url)
     /* [previous][next][first][last][top][bottom][index][help]  */
 207 {
 208     char *at, *delim, *colon;
 209 
 210     at = strrchr (url, '@');
 211     if (at == NULL)
 212         return g_strdup (url);
 213 
 214     /* TODO: handle ':' and '@' in password */
 215 
 216     delim = strstr (url, VFS_PATH_URL_DELIMITER);
 217     if (delim != NULL)
 218         colon = strchr (delim + strlen (VFS_PATH_URL_DELIMITER), ':');
 219     else
 220         colon = strchr (url, ':');
 221 
 222     /* if 'colon' before 'at', 'colon' delimits user and password: user:password@host */
 223     /* if 'colon' after 'at', 'colon' delimits host and port: user@host:port */
 224     if (colon != NULL && colon > at)
 225         colon = NULL;
 226 
 227     if (colon == NULL)
 228         return g_strdup (url);
 229     *colon = '\0';
 230 
 231     return g_strconcat (url, at, (char *) NULL);
 232 }
 233 
 234 /* --------------------------------------------------------------------------------------------- */
 235 
 236 static void
 237 push_history (WInput * in, const char *text)
     /* [previous][next][first][last][top][bottom][index][help]  */
 238 {
 239     char *t;
 240     gboolean empty;
 241 
 242     if (text == NULL)
 243         return;
 244 
 245     t = g_strstrip (g_strdup (text));
 246     empty = *t == '\0';
 247     g_free (t);
 248     t = g_strdup (empty ? "" : text);
 249 
 250     if (!empty && in->history.name != NULL && in->strip_password)
 251     {
 252         /*
 253            We got string user:pass@host without any VFS prefixes
 254            and vfs_path_to_str_flags (t, VPF_STRIP_PASSWORD) doesn't work.
 255            Therefore we want to strip password in separate algorithm
 256          */
 257         char *url_with_stripped_password;
 258 
 259         url_with_stripped_password = input_history_strip_password (t);
 260         g_free (t);
 261         t = url_with_stripped_password;
 262     }
 263 
 264     if (in->history.list == NULL || in->history.list->data == NULL
 265         || strcmp (in->history.list->data, t) != 0 || in->history.changed)
 266     {
 267         in->history.list = list_append_unique (in->history.list, t);
 268         in->history.current = in->history.list;
 269         in->history.changed = TRUE;
 270     }
 271     else
 272         g_free (t);
 273 
 274     in->need_push = FALSE;
 275 }
 276 
 277 /* --------------------------------------------------------------------------------------------- */
 278 
 279 static void
 280 move_buffer_backward (WInput * in, int start, int end)
     /* [previous][next][first][last][top][bottom][index][help]  */
 281 {
 282     int i, pos, len;
 283     int str_len;
 284 
 285     str_len = str_length (in->buffer);
 286     if (start >= str_len || end > str_len + 1)
 287         return;
 288 
 289     pos = str_offset_to_pos (in->buffer, start);
 290     len = str_offset_to_pos (in->buffer, end) - pos;
 291 
 292     for (i = pos; in->buffer[i + len - 1]; i++)
 293         in->buffer[i] = in->buffer[i + len];
 294 }
 295 
 296 /* --------------------------------------------------------------------------------------------- */
 297 
 298 static cb_ret_t
 299 insert_char (WInput * in, int c_code)
     /* [previous][next][first][last][top][bottom][index][help]  */
 300 {
 301     int res;
 302     long m1, m2;
 303 
 304     if (input_eval_marks (in, &m1, &m2))
 305         delete_region (in, m1, m2);
 306 
 307     if (c_code == -1)
 308         return MSG_NOT_HANDLED;
 309 
 310     if (in->charpoint >= MB_LEN_MAX)
 311         return MSG_HANDLED;
 312 
 313     in->charbuf[in->charpoint] = c_code;
 314     in->charpoint++;
 315 
 316     res = str_is_valid_char (in->charbuf, in->charpoint);
 317     if (res < 0)
 318     {
 319         if (res != -2)
 320             in->charpoint = 0;  /* broken multibyte char, skip */
 321         return MSG_HANDLED;
 322     }
 323 
 324     in->need_push = TRUE;
 325     if (strlen (in->buffer) + 1 + in->charpoint >= in->current_max_size)
 326     {
 327         /* Expand the buffer */
 328         size_t new_length;
 329         char *narea;
 330 
 331         new_length = in->current_max_size + WIDGET (in)->cols + in->charpoint;
 332         narea = g_try_renew (char, in->buffer, new_length);
 333         if (narea != NULL)
 334         {
 335             in->buffer = narea;
 336             in->current_max_size = new_length;
 337         }
 338     }
 339 
 340     if (strlen (in->buffer) + in->charpoint < in->current_max_size)
 341     {
 342         size_t i;
 343         /* bytes from begin */
 344         size_t ins_point = str_offset_to_pos (in->buffer, in->point);
 345         /* move chars */
 346         size_t rest_bytes = strlen (in->buffer + ins_point);
 347 
 348         for (i = rest_bytes + 1; i > 0; i--)
 349             in->buffer[ins_point + i + in->charpoint - 1] = in->buffer[ins_point + i - 1];
 350 
 351         memcpy (in->buffer + ins_point, in->charbuf, in->charpoint);
 352         in->point++;
 353     }
 354 
 355     in->charpoint = 0;
 356     return MSG_HANDLED;
 357 }
 358 
 359 /* --------------------------------------------------------------------------------------------- */
 360 
 361 static void
 362 beginning_of_line (WInput * in)
     /* [previous][next][first][last][top][bottom][index][help]  */
 363 {
 364     in->point = 0;
 365     in->charpoint = 0;
 366 }
 367 
 368 /* --------------------------------------------------------------------------------------------- */
 369 
 370 static void
 371 end_of_line (WInput * in)
     /* [previous][next][first][last][top][bottom][index][help]  */
 372 {
 373     in->point = str_length (in->buffer);
 374     in->charpoint = 0;
 375 }
 376 
 377 /* --------------------------------------------------------------------------------------------- */
 378 
 379 static void
 380 backward_char (WInput * in)
     /* [previous][next][first][last][top][bottom][index][help]  */
 381 {
 382     const char *act;
 383 
 384     act = in->buffer + str_offset_to_pos (in->buffer, in->point);
 385     if (in->point > 0)
 386         in->point -= str_cprev_noncomb_char (&act, in->buffer);
 387     in->charpoint = 0;
 388 }
 389 
 390 /* --------------------------------------------------------------------------------------------- */
 391 
 392 static void
 393 forward_char (WInput * in)
     /* [previous][next][first][last][top][bottom][index][help]  */
 394 {
 395     const char *act;
 396 
 397     act = in->buffer + str_offset_to_pos (in->buffer, in->point);
 398     if (act[0] != '\0')
 399         in->point += str_cnext_noncomb_char (&act);
 400     in->charpoint = 0;
 401 }
 402 
 403 /* --------------------------------------------------------------------------------------------- */
 404 
 405 static void
 406 forward_word (WInput * in)
     /* [previous][next][first][last][top][bottom][index][help]  */
 407 {
 408     const char *p;
 409 
 410     p = in->buffer + str_offset_to_pos (in->buffer, in->point);
 411     while (p[0] != '\0' && (str_isspace (p) || str_ispunct (p)))
 412     {
 413         str_cnext_char (&p);
 414         in->point++;
 415     }
 416     while (p[0] != '\0' && !str_isspace (p) && !str_ispunct (p))
 417     {
 418         str_cnext_char (&p);
 419         in->point++;
 420     }
 421 }
 422 
 423 /* --------------------------------------------------------------------------------------------- */
 424 
 425 static void
 426 backward_word (WInput * in)
     /* [previous][next][first][last][top][bottom][index][help]  */
 427 {
 428     const char *p;
 429 
 430     p = in->buffer + str_offset_to_pos (in->buffer, in->point);
 431 
 432     while (p != in->buffer)
 433     {
 434         const char *p_tmp;
 435 
 436         p_tmp = p;
 437         str_cprev_char (&p);
 438         if (!str_isspace (p) && !str_ispunct (p))
 439         {
 440             p = p_tmp;
 441             break;
 442         }
 443         in->point--;
 444     }
 445     while (p != in->buffer)
 446     {
 447         str_cprev_char (&p);
 448         if (str_isspace (p) || str_ispunct (p))
 449             break;
 450 
 451         in->point--;
 452     }
 453 }
 454 
 455 /* --------------------------------------------------------------------------------------------- */
 456 
 457 static void
 458 backward_delete (WInput * in)
     /* [previous][next][first][last][top][bottom][index][help]  */
 459 {
 460     const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point);
 461     int start;
 462 
 463     if (in->point == 0)
 464         return;
 465 
 466     start = in->point - str_cprev_noncomb_char (&act, in->buffer);
 467     move_buffer_backward (in, start, in->point);
 468     in->charpoint = 0;
 469     in->need_push = TRUE;
 470     in->point = start;
 471 }
 472 
 473 /* --------------------------------------------------------------------------------------------- */
 474 
 475 static void
 476 delete_char (WInput * in)
     /* [previous][next][first][last][top][bottom][index][help]  */
 477 {
 478     const char *act;
 479     int end = in->point;
 480 
 481     act = in->buffer + str_offset_to_pos (in->buffer, in->point);
 482     end += str_cnext_noncomb_char (&act);
 483 
 484     move_buffer_backward (in, in->point, end);
 485     in->charpoint = 0;
 486     in->need_push = TRUE;
 487 }
 488 
 489 /* --------------------------------------------------------------------------------------------- */
 490 
 491 static void
 492 copy_region (WInput * in, int x_first, int x_last)
     /* [previous][next][first][last][top][bottom][index][help]  */
 493 {
 494     int first = MIN (x_first, x_last);
 495     int last = MAX (x_first, x_last);
 496 
 497     if (last == first)
 498     {
 499         /* Copy selected files to clipboard */
 500         mc_event_raise (MCEVENT_GROUP_FILEMANAGER, "panel_save_current_file_to_clip_file", NULL);
 501         /* try use external clipboard utility */
 502         mc_event_raise (MCEVENT_GROUP_CORE, "clipboard_file_to_ext_clip", NULL);
 503         return;
 504     }
 505 
 506     g_free (kill_buffer);
 507 
 508     first = str_offset_to_pos (in->buffer, first);
 509     last = str_offset_to_pos (in->buffer, last);
 510 
 511     kill_buffer = g_strndup (in->buffer + first, last - first);
 512 
 513     mc_event_raise (MCEVENT_GROUP_CORE, "clipboard_text_to_file", kill_buffer);
 514     /* try use external clipboard utility */
 515     mc_event_raise (MCEVENT_GROUP_CORE, "clipboard_file_to_ext_clip", NULL);
 516 }
 517 
 518 /* --------------------------------------------------------------------------------------------- */
 519 
 520 static void
 521 kill_word (WInput * in)
     /* [previous][next][first][last][top][bottom][index][help]  */
 522 {
 523     int old_point = in->point;
 524     int new_point;
 525 
 526     forward_word (in);
 527     new_point = in->point;
 528     in->point = old_point;
 529 
 530     delete_region (in, old_point, new_point);
 531     in->need_push = TRUE;
 532     in->charpoint = 0;
 533 }
 534 
 535 /* --------------------------------------------------------------------------------------------- */
 536 
 537 static void
 538 back_kill_word (WInput * in)
     /* [previous][next][first][last][top][bottom][index][help]  */
 539 {
 540     int old_point = in->point;
 541     int new_point;
 542 
 543     backward_word (in);
 544     new_point = in->point;
 545     in->point = old_point;
 546 
 547     delete_region (in, old_point, new_point);
 548     in->need_push = TRUE;
 549 }
 550 
 551 /* --------------------------------------------------------------------------------------------- */
 552 
 553 static void
 554 yank (WInput * in)
     /* [previous][next][first][last][top][bottom][index][help]  */
 555 {
 556     if (kill_buffer != NULL)
 557     {
 558         char *p;
 559 
 560         in->charpoint = 0;
 561         for (p = kill_buffer; *p != '\0'; p++)
 562             insert_char (in, *p);
 563         in->charpoint = 0;
 564     }
 565 }
 566 
 567 /* --------------------------------------------------------------------------------------------- */
 568 
 569 static void
 570 kill_line (WInput * in)
     /* [previous][next][first][last][top][bottom][index][help]  */
 571 {
 572     int chp;
 573 
 574     chp = str_offset_to_pos (in->buffer, in->point);
 575     g_free (kill_buffer);
 576     kill_buffer = g_strdup (&in->buffer[chp]);
 577     in->buffer[chp] = '\0';
 578     in->charpoint = 0;
 579 }
 580 
 581 /* --------------------------------------------------------------------------------------------- */
 582 
 583 static void
 584 clear_line (WInput * in)
     /* [previous][next][first][last][top][bottom][index][help]  */
 585 {
 586     in->need_push = TRUE;
 587     in->buffer[0] = '\0';
 588     in->point = 0;
 589     in->mark = -1;
 590     in->charpoint = 0;
 591 }
 592 
 593 /* --------------------------------------------------------------------------------------------- */
 594 
 595 static void
 596 ins_from_clip (WInput * in)
     /* [previous][next][first][last][top][bottom][index][help]  */
 597 {
 598     char *p = NULL;
 599     ev_clipboard_text_from_file_t event_data;
 600 
 601     /* try use external clipboard utility */
 602     mc_event_raise (MCEVENT_GROUP_CORE, "clipboard_file_from_ext_clip", NULL);
 603 
 604     event_data.text = &p;
 605     mc_event_raise (MCEVENT_GROUP_CORE, "clipboard_text_from_file", &event_data);
 606     if (event_data.ret)
 607     {
 608         char *pp;
 609 
 610         for (pp = p; *pp != '\0'; pp++)
 611             insert_char (in, *pp);
 612 
 613         g_free (p);
 614     }
 615 }
 616 
 617 /* --------------------------------------------------------------------------------------------- */
 618 
 619 static void
 620 hist_prev (WInput * in)
     /* [previous][next][first][last][top][bottom][index][help]  */
 621 {
 622     GList *prev;
 623 
 624     if (in->history.list == NULL)
 625         return;
 626 
 627     if (in->need_push)
 628         push_history (in, in->buffer);
 629 
 630     prev = g_list_previous (in->history.current);
 631     if (prev != NULL)
 632     {
 633         input_assign_text (in, (char *) prev->data);
 634         in->history.current = prev;
 635         in->history.changed = TRUE;
 636         in->need_push = FALSE;
 637     }
 638 }
 639 
 640 /* --------------------------------------------------------------------------------------------- */
 641 
 642 static void
 643 hist_next (WInput * in)
     /* [previous][next][first][last][top][bottom][index][help]  */
 644 {
 645     GList *next;
 646 
 647     if (in->need_push)
 648     {
 649         push_history (in, in->buffer);
 650         input_assign_text (in, "");
 651         return;
 652     }
 653 
 654     if (in->history.list == NULL)
 655         return;
 656 
 657     next = g_list_next (in->history.current);
 658     if (next == NULL)
 659     {
 660         input_assign_text (in, "");
 661         in->history.current = in->history.list;
 662     }
 663     else
 664     {
 665         input_assign_text (in, (char *) next->data);
 666         in->history.current = next;
 667         in->history.changed = TRUE;
 668         in->need_push = FALSE;
 669     }
 670 }
 671 
 672 /* --------------------------------------------------------------------------------------------- */
 673 
 674 static void
 675 port_region_marked_for_delete (WInput * in)
     /* [previous][next][first][last][top][bottom][index][help]  */
 676 {
 677     in->buffer[0] = '\0';
 678     in->point = 0;
 679     in->first = FALSE;
 680     in->charpoint = 0;
 681 }
 682 
 683 /* --------------------------------------------------------------------------------------------- */
 684 
 685 static cb_ret_t
 686 input_execute_cmd (WInput * in, long command)
     /* [previous][next][first][last][top][bottom][index][help]  */
 687 {
 688     cb_ret_t res = MSG_HANDLED;
 689 
 690     switch (command)
 691     {
 692     case CK_MarkLeft:
 693     case CK_MarkRight:
 694     case CK_MarkToWordBegin:
 695     case CK_MarkToWordEnd:
 696     case CK_MarkToHome:
 697     case CK_MarkToEnd:
 698         /* a highlight command like shift-arrow */
 699         if (in->mark < 0)
 700         {
 701             input_mark_cmd (in, FALSE); /* clear */
 702             input_mark_cmd (in, TRUE);  /* marking on */
 703         }
 704         break;
 705     case CK_WordRight:
 706     case CK_WordLeft:
 707     case CK_Right:
 708     case CK_Left:
 709         if (in->mark >= 0)
 710             input_mark_cmd (in, FALSE);
 711         break;
 712     default:
 713         break;
 714     }
 715 
 716     switch (command)
 717     {
 718     case CK_Home:
 719     case CK_MarkToHome:
 720         beginning_of_line (in);
 721         break;
 722     case CK_End:
 723     case CK_MarkToEnd:
 724         end_of_line (in);
 725         break;
 726     case CK_Left:
 727     case CK_MarkLeft:
 728         backward_char (in);
 729         break;
 730     case CK_WordLeft:
 731     case CK_MarkToWordBegin:
 732         backward_word (in);
 733         break;
 734     case CK_Right:
 735     case CK_MarkRight:
 736         forward_char (in);
 737         break;
 738     case CK_WordRight:
 739     case CK_MarkToWordEnd:
 740         forward_word (in);
 741         break;
 742     case CK_BackSpace:
 743         {
 744             long m1, m2;
 745 
 746             if (input_eval_marks (in, &m1, &m2))
 747                 delete_region (in, m1, m2);
 748             else
 749                 backward_delete (in);
 750         }
 751         break;
 752     case CK_Delete:
 753         if (in->first)
 754             port_region_marked_for_delete (in);
 755         else
 756         {
 757             long m1, m2;
 758 
 759             if (input_eval_marks (in, &m1, &m2))
 760                 delete_region (in, m1, m2);
 761             else
 762                 delete_char (in);
 763         }
 764         break;
 765     case CK_DeleteToWordEnd:
 766         kill_word (in);
 767         break;
 768     case CK_DeleteToWordBegin:
 769         back_kill_word (in);
 770         break;
 771     case CK_Mark:
 772         input_mark_cmd (in, TRUE);
 773         break;
 774     case CK_Remove:
 775         delete_region (in, in->point, MAX (in->mark, 0));
 776         break;
 777     case CK_DeleteToEnd:
 778         kill_line (in);
 779         break;
 780     case CK_Clear:
 781         clear_line (in);
 782         break;
 783     case CK_Store:
 784         copy_region (in, MAX (in->mark, 0), in->point);
 785         break;
 786     case CK_Cut:
 787         {
 788             long m;
 789 
 790             m = MAX (in->mark, 0);
 791             copy_region (in, m, in->point);
 792             delete_region (in, in->point, m);
 793         }
 794         break;
 795     case CK_Yank:
 796         yank (in);
 797         break;
 798     case CK_Paste:
 799         ins_from_clip (in);
 800         break;
 801     case CK_HistoryPrev:
 802         hist_prev (in);
 803         break;
 804     case CK_HistoryNext:
 805         hist_next (in);
 806         break;
 807     case CK_History:
 808         do_show_hist (in);
 809         break;
 810     case CK_Complete:
 811         complete (in);
 812         break;
 813     default:
 814         res = MSG_NOT_HANDLED;
 815     }
 816 
 817     switch (command)
 818     {
 819     case CK_MarkLeft:
 820     case CK_MarkRight:
 821     case CK_MarkToWordBegin:
 822     case CK_MarkToWordEnd:
 823     case CK_MarkToHome:
 824     case CK_MarkToEnd:
 825         /* do nothing */
 826         break;
 827     default:
 828         in->mark = -1;
 829         break;
 830     }
 831 
 832     return res;
 833 }
 834 
 835 /* --------------------------------------------------------------------------------------------- */
 836 
 837 /* "history_load" event handler */
 838 static gboolean
 839 input_load_history (const gchar * event_group_name, const gchar * event_name,
     /* [previous][next][first][last][top][bottom][index][help]  */
 840                     gpointer init_data, gpointer data)
 841 {
 842     WInput *in = INPUT (init_data);
 843     ev_history_load_save_t *ev = (ev_history_load_save_t *) data;
 844 
 845     (void) event_group_name;
 846     (void) event_name;
 847 
 848     in->history.list = mc_config_history_load (ev->cfg, in->history.name);
 849     in->history.current = in->history.list;
 850 
 851     if (in->init_from_history)
 852     {
 853         const char *def_text = "";
 854 
 855         if (in->history.list != NULL && in->history.list->data != NULL)
 856             def_text = (const char *) in->history.list->data;
 857 
 858         input_assign_text (in, def_text);
 859     }
 860 
 861     return TRUE;
 862 }
 863 
 864 /* --------------------------------------------------------------------------------------------- */
 865 
 866 /* "history_save" event handler */
 867 static gboolean
 868 input_save_history (const gchar * event_group_name, const gchar * event_name,
     /* [previous][next][first][last][top][bottom][index][help]  */
 869                     gpointer init_data, gpointer data)
 870 {
 871     WInput *in = INPUT (init_data);
 872 
 873     (void) event_group_name;
 874     (void) event_name;
 875 
 876     if (!in->is_password && (DIALOG (WIDGET (in)->owner)->ret_value != B_CANCEL))
 877     {
 878         ev_history_load_save_t *ev = (ev_history_load_save_t *) data;
 879 
 880         push_history (in, in->buffer);
 881         if (in->history.changed)
 882             mc_config_history_save (ev->cfg, in->history.name, in->history.list);
 883         in->history.changed = FALSE;
 884     }
 885 
 886     return TRUE;
 887 }
 888 
 889 /* --------------------------------------------------------------------------------------------- */
 890 
 891 static void
 892 input_destroy (WInput * in)
     /* [previous][next][first][last][top][bottom][index][help]  */
 893 {
 894     if (in == NULL)
 895     {
 896         fprintf (stderr, "Internal error: null Input *\n");
 897         exit (EXIT_FAILURE);
 898     }
 899 
 900     input_free_completions (in);
 901 
 902     /* clean history */
 903     if (in->history.list != NULL)
 904     {
 905         /* history is already saved before this moment */
 906         in->history.list = g_list_first (in->history.list);
 907         g_list_free_full (in->history.list, g_free);
 908     }
 909     g_free (in->history.name);
 910     g_free (in->buffer);
 911     MC_PTR_FREE (kill_buffer);
 912 }
 913 
 914 /* --------------------------------------------------------------------------------------------- */
 915 
 916 /**
 917  * Calculates the buffer index (aka "point") corresponding to some screen coordinate.
 918  */
 919 static int
 920 input_screen_to_point (const WInput * in, int x)
     /* [previous][next][first][last][top][bottom][index][help]  */
 921 {
 922     x += in->term_first_shown;
 923 
 924     if (x < 0)
 925         return 0;
 926 
 927     if (x < str_term_width1 (in->buffer))
 928         return str_column_to_pos (in->buffer, x);
 929 
 930     return str_length (in->buffer);
 931 }
 932 
 933 /* --------------------------------------------------------------------------------------------- */
 934 
 935 static void
 936 input_mouse_callback (Widget * w, mouse_msg_t msg, mouse_event_t * event)
     /* [previous][next][first][last][top][bottom][index][help]  */
 937 {
 938     /* save point between MSG_MOUSE_DOWN and MSG_MOUSE_DRAG */
 939     static int prev_point = 0;
 940     WInput *in = INPUT (w);
 941 
 942     switch (msg)
 943     {
 944     case MSG_MOUSE_DOWN:
 945         widget_select (w);
 946         in->first = FALSE;
 947 
 948         if (event->x >= w->cols - HISTORY_BUTTON_WIDTH && should_show_history_button (in))
 949             do_show_hist (in);
 950         else
 951         {
 952             input_mark_cmd (in, FALSE);
 953             input_set_point (in, input_screen_to_point (in, event->x));
 954             /* save point for the possible following MSG_MOUSE_DRAG action */
 955             prev_point = in->point;
 956         }
 957         break;
 958 
 959     case MSG_MOUSE_DRAG:
 960         /* start point: set marker using point before first MSG_MOUSE_DRAG action */
 961         if (in->mark < 0)
 962             in->mark = prev_point;
 963 
 964         input_set_point (in, input_screen_to_point (in, event->x));
 965         break;
 966 
 967     default:
 968         /* don't create highlight region of 0 length */
 969         if (in->mark == in->point)
 970             input_mark_cmd (in, FALSE);
 971         break;
 972     }
 973 }
 974 
 975 /* --------------------------------------------------------------------------------------------- */
 976 /*** public functions ****************************************************************************/
 977 /* --------------------------------------------------------------------------------------------- */
 978 
 979 /** Create new instance of WInput object.
 980   * @param y                    Y coordinate
 981   * @param x                    X coordinate
 982   * @param input_colors         Array of used colors
 983   * @param width                Widget width
 984   * @param def_text             Default text filled in widget
 985   * @param histname             Name of history
 986   * @param completion_flags     Flags for specify type of completions
 987   * @return                     WInput object
 988   */
 989 WInput *
 990 input_new (int y, int x, const int *colors, int width, const char *def_text,
     /* [previous][next][first][last][top][bottom][index][help]  */
 991            const char *histname, input_complete_t completion_flags)
 992 {
 993     WInput *in;
 994     Widget *w;
 995 
 996     in = g_new (WInput, 1);
 997     w = WIDGET (in);
 998     widget_init (w, y, x, 1, width, input_callback, input_mouse_callback);
 999     w->options |= WOP_SELECTABLE | WOP_IS_INPUT | WOP_WANT_CURSOR;
1000     w->keymap = input_map;
1001 
1002     in->color = colors;
1003     in->first = TRUE;
1004     in->mark = -1;
1005     in->term_first_shown = 0;
1006     in->disable_update = 0;
1007     in->is_password = FALSE;
1008     in->strip_password = FALSE;
1009 
1010     /* in->buffer will be corrected in "history_load" event handler */
1011     in->current_max_size = width + 1;
1012     in->buffer = g_new0 (char, in->current_max_size);
1013 
1014     /* init completions before input_assign_text() call */
1015     in->completions = NULL;
1016     in->completion_flags = completion_flags;
1017 
1018     in->init_from_history = (def_text == INPUT_LAST_TEXT);
1019 
1020     if (in->init_from_history || def_text == NULL)
1021         def_text = "";
1022 
1023     input_assign_text (in, def_text);
1024 
1025     /* prepare to history setup */
1026     in->history.list = NULL;
1027     in->history.current = NULL;
1028     in->history.changed = FALSE;
1029     in->history.name = NULL;
1030     if ((histname != NULL) && (*histname != '\0'))
1031         in->history.name = g_strdup (histname);
1032     /* history will be loaded later */
1033 
1034     in->label = NULL;
1035 
1036     return in;
1037 }
1038 
1039 /* --------------------------------------------------------------------------------------------- */
1040 
1041 cb_ret_t
1042 input_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)
     /* [previous][next][first][last][top][bottom][index][help]  */
1043 {
1044     WInput *in = INPUT (w);
1045     WDialog *h = DIALOG (w->owner);
1046     cb_ret_t v;
1047 
1048     switch (msg)
1049     {
1050     case MSG_INIT:
1051         /* subscribe to "history_load" event */
1052         mc_event_add (h->event_group, MCEVENT_HISTORY_LOAD, input_load_history, w, NULL);
1053         /* subscribe to "history_save" event */
1054         mc_event_add (h->event_group, MCEVENT_HISTORY_SAVE, input_save_history, w, NULL);
1055         if (in->label != NULL)
1056             widget_set_state (WIDGET (in->label), WST_DISABLED, widget_get_state (w, WST_DISABLED));
1057         return MSG_HANDLED;
1058 
1059     case MSG_KEY:
1060         if (parm == XCTRL ('q'))
1061         {
1062             quote = TRUE;
1063             v = input_handle_char (in, ascii_alpha_to_cntrl (tty_getch ()));
1064             quote = FALSE;
1065             return v;
1066         }
1067 
1068         /* Keys we want others to handle */
1069         if (parm == KEY_UP || parm == KEY_DOWN || parm == ESC_CHAR
1070             || parm == KEY_F (10) || parm == '\n')
1071             return MSG_NOT_HANDLED;
1072 
1073         /* When pasting multiline text, insert literal Enter */
1074         if ((parm & ~KEY_M_MASK) == '\n')
1075         {
1076             quote = TRUE;
1077             v = input_handle_char (in, '\n');
1078             quote = FALSE;
1079             return v;
1080         }
1081 
1082         return input_handle_char (in, parm);
1083 
1084     case MSG_ACTION:
1085         return input_execute_cmd (in, parm);
1086 
1087     case MSG_DRAW:
1088         input_update (in, FALSE);
1089         return MSG_HANDLED;
1090 
1091     case MSG_ENABLE:
1092     case MSG_DISABLE:
1093         if (in->label != NULL)
1094             widget_set_state (WIDGET (in->label), WST_DISABLED, msg == MSG_DISABLE);
1095         return MSG_HANDLED;
1096 
1097     case MSG_CURSOR:
1098         widget_gotoyx (in, 0, str_term_width2 (in->buffer, in->point) - in->term_first_shown);
1099         return MSG_HANDLED;
1100 
1101     case MSG_DESTROY:
1102         /* unsubscribe from "history_load" event */
1103         mc_event_del (h->event_group, MCEVENT_HISTORY_LOAD, input_load_history, w);
1104         /* unsubscribe from "history_save" event */
1105         mc_event_del (h->event_group, MCEVENT_HISTORY_SAVE, input_save_history, w);
1106         input_destroy (in);
1107         return MSG_HANDLED;
1108 
1109     default:
1110         return widget_default_callback (w, sender, msg, parm, data);
1111     }
1112 }
1113 
1114 /* --------------------------------------------------------------------------------------------- */
1115 
1116 void
1117 input_set_default_colors (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1118 {
1119     input_colors[WINPUTC_MAIN] = INPUT_COLOR;
1120     input_colors[WINPUTC_MARK] = INPUT_MARK_COLOR;
1121     input_colors[WINPUTC_UNCHANGED] = INPUT_UNCHANGED_COLOR;
1122     input_colors[WINPUTC_HISTORY] = INPUT_HISTORY_COLOR;
1123 }
1124 
1125 /* --------------------------------------------------------------------------------------------- */
1126 
1127 cb_ret_t
1128 input_handle_char (WInput * in, int key)
     /* [previous][next][first][last][top][bottom][index][help]  */
1129 {
1130     cb_ret_t v;
1131     long command;
1132 
1133     if (quote)
1134     {
1135         input_free_completions (in);
1136         v = insert_char (in, key);
1137         input_update (in, TRUE);
1138         quote = FALSE;
1139         return v;
1140     }
1141 
1142     command = widget_lookup_key (WIDGET (in), key);
1143     if (command == CK_IgnoreKey)
1144     {
1145         if (key > 255)
1146             return MSG_NOT_HANDLED;
1147         if (in->first)
1148             port_region_marked_for_delete (in);
1149         input_free_completions (in);
1150         v = insert_char (in, key);
1151     }
1152     else
1153     {
1154         if (command != CK_Complete)
1155             input_free_completions (in);
1156         input_execute_cmd (in, command);
1157         v = MSG_HANDLED;
1158         if (in->first)
1159             input_update (in, TRUE);    /* needed to clear in->first */
1160     }
1161 
1162     input_update (in, TRUE);
1163     return v;
1164 }
1165 
1166 /* --------------------------------------------------------------------------------------------- */
1167 
1168 /* This function is a test for a special input key used in complete.c */
1169 /* Returns 0 if it is not a special key, 1 if it is a non-complete key
1170    and 2 if it is a complete key */
1171 int
1172 input_key_is_in_map (WInput * in, int key)
     /* [previous][next][first][last][top][bottom][index][help]  */
1173 {
1174     long command;
1175 
1176     command = widget_lookup_key (WIDGET (in), key);
1177     if (command == CK_IgnoreKey)
1178         return 0;
1179 
1180     return (command == CK_Complete) ? 2 : 1;
1181 }
1182 
1183 /* --------------------------------------------------------------------------------------------- */
1184 
1185 void
1186 input_assign_text (WInput * in, const char *text)
     /* [previous][next][first][last][top][bottom][index][help]  */
1187 {
1188     Widget *w = WIDGET (in);
1189     size_t text_len, buffer_len;
1190 
1191     if (text == NULL)
1192         text = "";
1193 
1194     input_free_completions (in);
1195     in->mark = -1;
1196     in->need_push = TRUE;
1197     in->charpoint = 0;
1198 
1199     text_len = strlen (text);
1200     buffer_len = 1 + MAX ((size_t) w->cols, text_len);
1201     in->current_max_size = buffer_len;
1202     if (buffer_len > (size_t) w->cols)
1203         in->buffer = g_realloc (in->buffer, buffer_len);
1204     memmove (in->buffer, text, text_len + 1);
1205     in->point = str_length (in->buffer);
1206     input_update (in, TRUE);
1207 }
1208 
1209 /* --------------------------------------------------------------------------------------------- */
1210 
1211 gboolean
1212 input_is_empty (const WInput * in)
     /* [previous][next][first][last][top][bottom][index][help]  */
1213 {
1214     return (in == NULL || in->buffer == NULL || in->buffer[0] == '\0');
1215 }
1216 
1217 /* --------------------------------------------------------------------------------------------- */
1218 
1219 /* Inserts text in input line */
1220 void
1221 input_insert (WInput * in, const char *text, gboolean insert_extra_space)
     /* [previous][next][first][last][top][bottom][index][help]  */
1222 {
1223     input_disable_update (in);
1224     while (*text != '\0')
1225         input_handle_char (in, (unsigned char) *text++);        /* unsigned extension char->int */
1226     if (insert_extra_space)
1227         input_handle_char (in, ' ');
1228     input_enable_update (in);
1229     input_update (in, TRUE);
1230 }
1231 
1232 /* --------------------------------------------------------------------------------------------- */
1233 
1234 void
1235 input_set_point (WInput * in, int pos)
     /* [previous][next][first][last][top][bottom][index][help]  */
1236 {
1237     int max_pos;
1238 
1239     max_pos = str_length (in->buffer);
1240     pos = MIN (pos, max_pos);
1241     if (pos != in->point)
1242         input_free_completions (in);
1243     in->point = pos;
1244     in->charpoint = 0;
1245     input_update (in, TRUE);
1246 }
1247 
1248 /* --------------------------------------------------------------------------------------------- */
1249 
1250 void
1251 input_update (WInput * in, gboolean clear_first)
     /* [previous][next][first][last][top][bottom][index][help]  */
1252 {
1253     Widget *w = WIDGET (in);
1254     int has_history = 0;
1255     int buf_len;
1256     const char *cp;
1257     int pw;
1258 
1259     if (in->disable_update != 0)
1260         return;
1261 
1262     /* don't draw widget not put into dialog */
1263     if (w->owner == NULL || !widget_get_state (WIDGET (w->owner), WST_ACTIVE))
1264         return;
1265 
1266     if (should_show_history_button (in))
1267         has_history = HISTORY_BUTTON_WIDTH;
1268 
1269     buf_len = str_length (in->buffer);
1270 
1271     /* Adjust the mark */
1272     in->mark = MIN (in->mark, buf_len);
1273 
1274     pw = str_term_width2 (in->buffer, in->point);
1275 
1276     /* Make the point visible */
1277     if ((pw < in->term_first_shown) || (pw >= in->term_first_shown + w->cols - has_history))
1278     {
1279         in->term_first_shown = pw - (w->cols / 3);
1280         if (in->term_first_shown < 0)
1281             in->term_first_shown = 0;
1282     }
1283 
1284     if (has_history != 0)
1285         draw_history_button (in);
1286 
1287     if (widget_get_state (w, WST_DISABLED))
1288         tty_setcolor (DISABLED_COLOR);
1289     else if (in->first)
1290         tty_setcolor (in->color[WINPUTC_UNCHANGED]);
1291     else
1292         tty_setcolor (in->color[WINPUTC_MAIN]);
1293 
1294     widget_gotoyx (in, 0, 0);
1295 
1296     if (!in->is_password)
1297     {
1298         if (in->mark < 0)
1299             tty_print_string (str_term_substring (in->buffer, in->term_first_shown,
1300                                                   w->cols - has_history));
1301         else
1302         {
1303             long m1, m2;
1304 
1305             if (input_eval_marks (in, &m1, &m2))
1306             {
1307                 tty_setcolor (in->color[WINPUTC_MAIN]);
1308                 cp = str_term_substring (in->buffer, in->term_first_shown, w->cols - has_history);
1309                 tty_print_string (cp);
1310                 tty_setcolor (in->color[WINPUTC_MARK]);
1311                 if (m1 < in->term_first_shown)
1312                 {
1313                     widget_gotoyx (in, 0, 0);
1314                     tty_print_string (str_term_substring
1315                                       (in->buffer, in->term_first_shown,
1316                                        m2 - in->term_first_shown));
1317                 }
1318                 else
1319                 {
1320                     int sel_width, buf_width;
1321 
1322                     widget_gotoyx (in, 0, m1 - in->term_first_shown);
1323                     buf_width = str_term_width2 (in->buffer, m1);
1324                     sel_width =
1325                         MIN (m2 - m1, (w->cols - has_history) - (buf_width - in->term_first_shown));
1326                     tty_print_string (str_term_substring (in->buffer, m1, sel_width));
1327                 }
1328             }
1329         }
1330     }
1331     else
1332     {
1333         int i;
1334 
1335         cp = str_term_substring (in->buffer, in->term_first_shown, w->cols - has_history);
1336         tty_setcolor (in->color[WINPUTC_MAIN]);
1337         for (i = 0; i < w->cols - has_history; i++)
1338         {
1339             if (i < (buf_len - in->term_first_shown) && cp[0] != '\0')
1340                 tty_print_char ('*');
1341             else
1342                 tty_print_char (' ');
1343             if (cp[0] != '\0')
1344                 str_cnext_char (&cp);
1345         }
1346     }
1347 
1348     if (clear_first)
1349         in->first = FALSE;
1350 }
1351 
1352 /* --------------------------------------------------------------------------------------------- */
1353 
1354 void
1355 input_enable_update (WInput * in)
     /* [previous][next][first][last][top][bottom][index][help]  */
1356 {
1357     in->disable_update--;
1358     input_update (in, FALSE);
1359 }
1360 
1361 /* --------------------------------------------------------------------------------------------- */
1362 
1363 void
1364 input_disable_update (WInput * in)
     /* [previous][next][first][last][top][bottom][index][help]  */
1365 {
1366     in->disable_update++;
1367 }
1368 
1369 /* --------------------------------------------------------------------------------------------- */
1370 
1371 /**
1372   *  Cleans the input line and adds the current text to the history
1373   *
1374   *  @param in the input line
1375   */
1376 void
1377 input_clean (WInput * in)
     /* [previous][next][first][last][top][bottom][index][help]  */
1378 {
1379     push_history (in, in->buffer);
1380     in->need_push = TRUE;
1381     in->buffer[0] = '\0';
1382     in->point = 0;
1383     in->charpoint = 0;
1384     in->mark = -1;
1385     input_free_completions (in);
1386     input_update (in, FALSE);
1387 }
1388 
1389 /* --------------------------------------------------------------------------------------------- */
1390 
1391 void
1392 input_free_completions (WInput * in)
     /* [previous][next][first][last][top][bottom][index][help]  */
1393 {
1394     g_strfreev (in->completions);
1395     in->completions = NULL;
1396 }
1397 
1398 /* --------------------------------------------------------------------------------------------- */

/* [previous][next][first][last][top][bottom][index][help]  */