input.c 36 KB

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