input.c 36 KB

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