input.c 35 KB

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