add redefine keys support in viewer
[midnight-commander.git] / src / viewer / actions_cmd.c
blob6120ddc183c792b99edb7a782951b7dd3295f8f6
1 /*
2 Internal file viewer for the Midnight Commander
3 Callback function for some actions (hotkeys, menu)
5 Copyright (C) 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003,
6 2004, 2005, 2006, 2007, 2009 Free Software Foundation, Inc.
8 Written by: 1994, 1995, 1998 Miguel de Icaza
9 1994, 1995 Janne Kukonlehto
10 1995 Jakub Jelinek
11 1996 Joseph M. Hinkle
12 1997 Norbert Warmuth
13 1998 Pavel Machek
14 2004 Roland Illig <roland.illig@gmx.de>
15 2005 Roland Illig <roland.illig@gmx.de>
16 2009 Slava Zanko <slavazanko@google.com>
17 2009 Andrew Borodin <aborodin@vmail.ru>
18 2009 Ilia Maslakov <il.smind@gmail.com>
20 This file is part of the Midnight Commander.
22 The Midnight Commander is free software; you can redistribute it
23 and/or modify it under the terms of the GNU General Public License as
24 published by the Free Software Foundation; either version 2 of the
25 License, or (at your option) any later version.
27 The Midnight Commander is distributed in the hope that it will be
28 useful, but WITHOUT ANY WARRANTY; without even the implied warranty
29 of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
30 General Public License for more details.
32 You should have received a copy of the GNU General Public License
33 along with this program; if not, write to the Free Software
34 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
35 MA 02110-1301, USA.
39 The functions in this section can be bound to hotkeys. They are all
40 of the same type (taking a pointer to mcview_t as parameter and
41 returning void). TODO: In the not-too-distant future, these commands
42 will become fully configurable, like they already are in the
43 internal editor. By convention, all the function names end in
44 "_cmd".
47 #include <config.h>
49 #include <errno.h>
50 #include <stdlib.h>
52 #include "../src/global.h"
53 #include "../src/panel.h"
54 #include "../src/layout.h"
55 #include "../src/wtools.h"
56 #include "../src/history.h"
57 #include "../src/charsets.h"
58 #include "../src/tty/tty.h"
59 #include "../src/tty/key.h"
60 #include "../src/cmd.h"
61 #include "../src/execute.h"
62 #include "../src/help.h"
63 #include "../src/keybind.h" /* global_key_map_t */
64 #include "../src/cmddef.h" /* CK_ cmd name const */
67 #include "internal.h"
68 #include "mcviewer.h"
70 /*** global variables ****************************************************************************/
72 const global_key_map_t default_keymap[] = {
74 { '?', CK_ViewSearch },
75 { '/', CK_ViewSearch },
76 { XCTRL('r'), CK_ViewContinueSearch },
77 { XCTRL('s'), CK_ViewContinueSearch },
78 { KEY_F (17), CK_ViewContinueSearch },
79 { ALT('r'), CK_ViewToggleRuler },
81 { KEY_HOME, CK_ViewMoveToBol },
82 { KEY_END, CK_ViewMoveToEol },
84 { 'h', CK_ViewMoveLeft },
85 { KEY_LEFT, CK_ViewMoveLeft },
87 { 'l', CK_ViewMoveRight },
88 { KEY_RIGHT, CK_ViewMoveRight },
90 { 'k', CK_ViewMoveUp },
91 { 'y', CK_ViewMoveUp },
92 { KEY_IC, CK_ViewMoveUp },
93 { KEY_UP, CK_ViewMoveUp },
95 { 'j', CK_ViewMoveDown },
96 { 'e', CK_ViewMoveDown },
97 { KEY_DOWN, CK_ViewMoveDown },
98 { KEY_DC, CK_ViewMoveDown },
101 { ' ', CK_ViewMovePgDn },
102 { 'f', CK_ViewMovePgDn },
103 { KEY_NPAGE, CK_ViewMovePgDn },
105 { 'b', CK_ViewMovePgUp },
106 { KEY_PPAGE, CK_ViewMovePgUp },
108 { 'd', CK_ViewMoveHalfPgDn },
110 { 'u', CK_ViewMoveHalfPgUp },
112 { XCTRL('o'), CK_ViewOtherCmd },
114 { 'm', CK_ViewGotoBookmark },
115 { 'r', CK_ViewNewBookmark },
117 { XCTRL ('f'), CK_ViewNextFile },
118 { XCTRL ('b'), CK_ViewPrevFile },
120 { 'q', CK_ViewQuit },
121 { XCTRL ('g'), CK_ViewQuit },
122 { ESC_CHAR, CK_ViewQuit },
124 { XCTRL ('t'), CK_Select_Codepage },
125 { 0, 0 }
128 const global_key_map_t default_hex_keymap[] = {
130 { '\t', CK_HexViewToggleNavigationMode },
131 { XCTRL ('a'), CK_ViewMoveToBol },
132 { XCTRL ('e'), CK_ViewMoveToEol },
134 { 'b', CK_ViewMoveLeft },
135 { KEY_LEFT, CK_ViewMoveLeft },
137 { 'f', CK_ViewMoveRight },
138 { KEY_RIGHT, CK_ViewMoveRight },
140 { 'k', CK_ViewMoveUp },
141 { 'y', CK_ViewMoveUp },
142 { KEY_UP, CK_ViewMoveUp },
144 { 'j', CK_ViewMoveDown },
145 { KEY_DOWN, CK_ViewMoveDown },
146 { KEY_DC, CK_ViewMoveDown },
148 { 0, 0 }
151 /*** file scope macro definitions ****************************************************************/
153 /*** file scope type declarations ****************************************************************/
155 /*** file scope variables ************************************************************************/
157 /*** file scope functions ************************************************************************/
159 /* --------------------------------------------------------------------------------------------- */
161 static void
162 mcview_continue_search_cmd (mcview_t * view)
165 if (view->last_search_string != NULL) {
166 mcview_do_search (view);
167 } else {
168 /* find last search string in history */
169 GList *history;
170 history = history_get (MC_HISTORY_SHARED_SEARCH);
171 if (history != NULL && history->data != NULL) {
172 view->last_search_string = (gchar *) g_strdup(history->data);
173 history = g_list_first (history);
174 g_list_foreach (history, (GFunc) g_free, NULL);
175 g_list_free (history);
177 view->search = mc_search_new (view->last_search_string, -1);
178 view->search_nroff_seq = mcview_nroff_seq_new (view);
180 if (!view->search) {
181 /* if not... then ask for an expression */
182 g_free(view->last_search_string);
183 mcview_search_cmd (view);
184 } else {
185 view->search->search_type = view->search_type;
186 view->search->is_all_charsets = view->search_all_codepages;
187 view->search->is_case_sentitive = view->search_case;
188 view->search->search_fn = mcview_search_cmd_callback;
189 view->search->update_fn = mcview_search_update_cmd_callback;
190 view->search->whole_words = view->whole_words;
192 mcview_do_search (view);
194 } else {
195 /* if not... then ask for an expression */
196 g_free(view->last_search_string);
197 mcview_search_cmd (view);
202 /* --------------------------------------------------------------------------------------------- */
203 static cb_ret_t
204 mcview_handle_editkey (mcview_t * view, int key)
206 struct hexedit_change_node *node;
207 int byte_val;
209 /* Has there been a change at this position? */
210 node = view->change_list;
211 while (node && (node->offset != view->hex_cursor))
212 node = node->next;
214 if (!view->hexview_in_text) {
215 /* Hex editing */
216 unsigned int hexvalue = 0;
218 if (key >= '0' && key <= '9')
219 hexvalue = 0 + (key - '0');
220 else if (key >= 'A' && key <= 'F')
221 hexvalue = 10 + (key - 'A');
222 else if (key >= 'a' && key <= 'f')
223 hexvalue = 10 + (key - 'a');
224 else
225 return MSG_NOT_HANDLED;
227 if (node)
228 byte_val = node->value;
229 else
230 mcview_get_byte (view, view->hex_cursor, &byte_val);
232 if (view->hexedit_lownibble) {
233 byte_val = (byte_val & 0xf0) | (hexvalue);
234 } else {
235 byte_val = (byte_val & 0x0f) | (hexvalue << 4);
237 } else {
238 /* Text editing */
239 if (key < 256 && ((key == '\n') || is_printable (key)))
240 byte_val = key;
241 else
242 return MSG_NOT_HANDLED;
244 if (!node) {
245 node = g_new (struct hexedit_change_node, 1);
246 node->offset = view->hex_cursor;
247 node->value = byte_val;
248 mcview_enqueue_change (&view->change_list, node);
249 } else {
250 node->value = byte_val;
252 view->dirty++;
253 mcview_update (view);
254 mcview_move_right (view, 1);
255 return MSG_HANDLED;
258 /* --------------------------------------------------------------------------------------------- */
260 /* Check for left and right arrows, possibly with modifiers */
261 static cb_ret_t
262 mcview_check_left_right_keys (mcview_t * view, int c)
264 if (c == KEY_LEFT) {
265 mcview_move_left (view, 1);
266 return MSG_HANDLED;
269 if (c == KEY_RIGHT) {
270 mcview_move_right (view, 1);
271 return MSG_HANDLED;
274 /* Ctrl with arrows moves by 10 postions in the unwrap mode */
275 if (view->hex_mode || view->text_wrap_mode)
276 return MSG_NOT_HANDLED;
278 if (c == (KEY_M_CTRL | KEY_LEFT)) {
279 if (view->dpy_text_column >= 10)
280 view->dpy_text_column -= 10;
281 else
282 view->dpy_text_column = 0;
283 view->dirty++;
284 return MSG_HANDLED;
287 if (c == (KEY_M_CTRL | KEY_RIGHT)) {
288 if (view->dpy_text_column <= OFFSETTYPE_MAX - 10)
289 view->dpy_text_column += 10;
290 else
291 view->dpy_text_column = OFFSETTYPE_MAX;
292 view->dirty++;
293 return MSG_HANDLED;
296 return MSG_NOT_HANDLED;
299 /* --------------------------------------------------------------------------------------------- */
301 static void
302 mcview_cmk_move_up (void *w, int n)
304 mcview_move_up ((mcview_t *) w, n);
307 /* --------------------------------------------------------------------------------------------- */
309 static void
310 mcview_cmk_move_down (void *w, int n)
312 mcview_move_down ((mcview_t *) w, n);
315 /* --------------------------------------------------------------------------------------------- */
317 static void
318 mcview_cmk_moveto_top (void *w, int n)
320 (void) &n;
321 mcview_moveto_top ((mcview_t *) w);
324 /* --------------------------------------------------------------------------------------------- */
326 static void
327 mcview_cmk_moveto_bottom (void *w, int n)
329 (void) &n;
330 mcview_moveto_bottom ((mcview_t *) w);
333 /* --------------------------------------------------------------------------------------------- */
335 static void
336 mcview_hook (void *v)
338 mcview_t *view = (mcview_t *) v;
339 WPanel *panel;
341 /* If the user is busy typing, wait until he finishes to update the
342 screen */
343 if (!is_idle ()) {
344 if (!hook_present (idle_hook, mcview_hook))
345 add_hook (&idle_hook, mcview_hook, v);
346 return;
349 delete_hook (&idle_hook, mcview_hook);
351 if (get_current_type () == view_listing)
352 panel = current_panel;
353 else if (get_other_type () == view_listing)
354 panel = other_panel;
355 else
356 return;
358 mcview_load (view, 0, panel->dir.list[panel->selected].fname, 0);
359 mcview_display (view);
362 /* --------------------------------------------------------------------------------------------- */
364 static int
365 mcview_execute_cmd (mcview_t * view, int command, int key)
367 int res = MSG_HANDLED;
369 if (view->hex_mode) {
370 switch (command) {
371 case CK_HexViewToggleNavigationMode:
372 view->hexview_in_text = !view->hexview_in_text;
373 view->dirty++;
374 break;
375 case CK_ViewMoveToBol:
376 mcview_moveto_bol (view);
377 view->dirty++;
378 break;
379 case CK_ViewMoveToEol:
380 mcview_moveto_eol (view);
381 break;
382 case CK_ViewMoveLeft:
383 mcview_move_left (view, 1);
384 break;
385 case CK_ViewMoveRight:
386 mcview_move_right (view, 1);
387 break;
388 default :
389 res = MSG_NOT_HANDLED;
390 break;
392 } else {
393 switch (command) {
394 case CK_ViewSearch:
395 view->search_type = MC_SEARCH_T_REGEX;
396 mcview_search_cmd (view);
397 break;
398 /* Continue search */
399 case CK_ViewContinueSearch:
400 mcview_continue_search_cmd (view);
401 break;
402 /* toggle ruler */
403 case CK_ViewToggleRuler:
404 mcview_toggle_ruler_cmd (view);
405 break;
406 case CK_ViewMoveLeft:
407 mcview_move_left (view, 1);
408 break;
409 case CK_ViewMoveRight:
410 mcview_move_right (view, 1);
411 break;
412 case CK_ViewMoveUp:
413 mcview_move_up (view, 1);
414 break;
415 case CK_ViewMoveDown:
416 mcview_move_down (view, 1);
417 break;
418 case CK_ViewMoveHalfPgUp:
419 mcview_move_up (view, (view->data_area.height + 1) / 2);
420 break;
421 case CK_ViewMoveHalfPgDn:
422 mcview_move_down (view, (view->data_area.height + 1) / 2);
423 break;
424 case CK_ViewMovePgUp:
425 mcview_move_up (view, view->data_area.height);
426 break;
427 case CK_ViewMovePgDn:
428 mcview_move_down (view, view->data_area.height);
429 break;
430 case CK_ViewOtherCmd:
431 view_other_cmd ();
432 break;
434 // Unlike Ctrl-O, run a new shell if the subshell is not running
435 case '!':
436 exec_shell ();
437 return MSG_HANDLED;
439 case CK_ViewGotoBookmark:
440 view->marks[view->marker] = view->dpy_start;
441 break;
442 case CK_ViewNewBookmark:
443 view->dpy_start = view->marks[view->marker];
444 view->dirty++;
445 break;
446 case CK_ViewNextFile:
447 /* Use to indicate parent that we want to see the next/previous file */
448 /* Does not work in panel mode */
449 if (!mcview_is_in_panel (view))
450 view->move_dir = 1;
451 break;
452 case CK_ViewPrevFile:
453 /* Use to indicate parent that we want to see the next/previous file */
454 /* Does not work in panel mode */
455 if (!mcview_is_in_panel (view))
456 view->move_dir = -1;
457 break;
458 case CK_ViewQuit:
459 if (mcview_ok_to_quit (view))
460 view->want_to_quit = TRUE;
461 break;
462 case CK_Select_Codepage:
463 mcview_select_encoding (view);
464 view->dirty++;
465 mcview_update (view);
466 break;
467 default :
468 res = MSG_NOT_HANDLED;
471 return res;
475 /* Both views */
476 static cb_ret_t
477 mcview_handle_key (mcview_t * view, int key)
479 key = convert_from_input_c (key);
481 int res = 0;
482 int i;
484 if (view->hex_mode) {
485 for (i = 0; view->hex_map[i].key; i++) {
486 if (key == view->hex_map[i].key) {
487 res = mcview_execute_cmd (view, view->hex_map[i].command, key);
488 if (res == MSG_HANDLED) {
489 return MSG_HANDLED;
493 } else {
494 for (i = 0; view->plain_map[i].key; i++) {
495 if (key == view->plain_map[i].key) {
496 res = mcview_execute_cmd (view, view->plain_map[i].command, key);
497 mc_log ("key: %i, command: %i, res: %i\n", key, view->plain_map[i].command, res);
498 if (res == MSG_HANDLED) {
499 return MSG_HANDLED;
504 if (mcview_check_left_right_keys (view, key))
505 return MSG_HANDLED;
507 if (check_movement_keys (key, view->data_area.height + 1, view,
508 mcview_cmk_move_up, mcview_cmk_move_down,
509 mcview_cmk_moveto_top, mcview_cmk_moveto_bottom))
510 return MSG_HANDLED;
512 #ifdef MC_ENABLE_DEBUGGING_CODE
513 if (c == 't') { /* mnemonic: "test" */
514 mcview_ccache_dump (view);
515 return MSG_HANDLED;
517 #endif
518 if (key >= '0' && key <= '9')
519 view->marker = key - '0';
521 /* Key not used */
522 return MSG_NOT_HANDLED;
525 /* --------------------------------------------------------------------------------------------- */
527 /*** public functions ****************************************************************************/
529 /* --------------------------------------------------------------------------------------------- */
531 cb_ret_t
532 mcview_callback (Widget * w, widget_msg_t msg, int parm)
534 mcview_t *view = (mcview_t *) w;
535 cb_ret_t i;
536 Dlg_head *h = view->widget.parent;
538 mcview_compute_areas (view);
539 mcview_update_bytes_per_line (view);
541 switch (msg) {
542 case WIDGET_INIT:
543 if (mcview_is_in_panel (view))
544 add_hook (&select_file_hook, mcview_hook, view);
545 else
546 view->dpy_bbar_dirty = TRUE;
547 return MSG_HANDLED;
549 case WIDGET_DRAW:
550 mcview_display (view);
551 return MSG_HANDLED;
553 case WIDGET_CURSOR:
554 if (view->hex_mode)
555 mcview_place_cursor (view);
556 return MSG_HANDLED;
558 case WIDGET_KEY:
559 i = mcview_handle_key ((mcview_t *) view, parm);
560 if (view->want_to_quit && !mcview_is_in_panel (view))
561 dlg_stop (h);
562 else {
563 mcview_update (view);
565 return i;
567 case WIDGET_FOCUS:
568 view->dpy_bbar_dirty = TRUE;
569 mcview_update (view);
570 return MSG_HANDLED;
572 case WIDGET_DESTROY:
573 mcview_done (view);
574 if (mcview_is_in_panel (view))
575 delete_hook (&select_file_hook, mcview_hook);
576 return MSG_HANDLED;
578 default:
579 return default_proc (msg, parm);
583 /* --------------------------------------------------------------------------------------------- */
585 cb_ret_t
586 mcview_dialog_callback (Dlg_head * h, dlg_msg_t msg, int parm)
588 switch (msg) {
589 case DLG_RESIZE:
590 mcview_adjust_size (h);
591 return MSG_HANDLED;
593 default:
594 return default_dlg_callback (h, msg, parm);
598 /* --------------------------------------------------------------------------------------------- */
600 void
601 mcview_help_cmd (void)
603 interactive_display (NULL, "[Internal File Viewer]");
606 /* --------------------------------------------------------------------------------------------- */
608 void
609 mcview_quit_cmd (mcview_t * view)
611 if (mcview_ok_to_quit (view))
612 dlg_stop (view->widget.parent);
615 /* --------------------------------------------------------------------------------------------- */
617 /* Toggle between hex view and text view */
618 void
619 mcview_toggle_hex_mode_cmd (mcview_t * view)
621 mcview_toggle_hex_mode (view);
622 mcview_update (view);
625 /* --------------------------------------------------------------------------------------------- */
627 void
628 mcview_moveto_line_cmd (mcview_t * view)
630 char *answer, *answer_end, prompt[BUF_SMALL];
631 off_t line, col;
633 mcview_offset_to_coord (view, &line, &col, view->dpy_start);
635 g_snprintf (prompt, sizeof (prompt),
636 _(" The current line number is %lld.\n"
637 " Enter the new line number:"), (line + 1));
638 answer = input_dialog (_(" Goto line "), prompt, MC_HISTORY_VIEW_GOTO_LINE, "");
639 if (answer != NULL && answer[0] != '\0') {
640 errno = 0;
641 line = strtoul (answer, &answer_end, 10);
642 if (*answer_end == '\0' && errno == 0 && line >= 1)
643 mcview_moveto (view, line - 1, 0);
645 g_free (answer);
646 view->dirty++;
647 mcview_update (view);
650 /* --------------------------------------------------------------------------------------------- */
652 void
653 mcview_moveto_addr_cmd (mcview_t * view)
655 char *line, *error, prompt[BUF_SMALL], prompt_format[BUF_SMALL];
656 off_t addr;
658 g_snprintf (prompt_format, sizeof (prompt_format),
659 _(" The current address is %s.\n"
660 " Enter the new address:"), "0x%08" OFFSETTYPE_PRIX "");
661 g_snprintf (prompt, sizeof (prompt), prompt_format, view->hex_cursor);
662 line = input_dialog (_(" Goto Address "), prompt, MC_HISTORY_VIEW_GOTO_ADDR, "");
663 if (line != NULL) {
664 if (*line != '\0') {
665 addr = strtoul (line, &error, 0);
666 if ((*error == '\0') && mcview_get_byte (view, addr, NULL) == TRUE) {
667 mcview_moveto_offset (view, addr);
668 } else {
669 message (D_ERROR, _("Warning"), _(" Invalid address "));
672 g_free (line);
674 view->dirty++;
675 mcview_update (view);
678 /* --------------------------------------------------------------------------------------------- */
680 /* Toggle between hexview and hexedit mode */
681 void
682 mcview_toggle_hexedit_mode_cmd (mcview_t * view)
684 mcview_toggle_hexedit_mode (view);
685 mcview_update (view);
688 /* --------------------------------------------------------------------------------------------- */
690 void
691 mcview_hexedit_save_changes_cmd (mcview_t * view)
693 (void) mcview_hexedit_save_changes (view);
696 /* --------------------------------------------------------------------------------------------- */
698 /* Toggle between wrapped and unwrapped view */
699 void
700 mcview_toggle_wrap_mode_cmd (mcview_t * view)
702 mcview_toggle_wrap_mode (view);
703 mcview_update (view);
706 /* --------------------------------------------------------------------------------------------- */
708 /* Both views */
709 void
710 mcview_search_cmd (mcview_t * view)
712 if (mcview_dialog_search (view))
713 mcview_do_search (view);
716 /* --------------------------------------------------------------------------------------------- */
718 void
719 mcview_toggle_magic_mode_cmd (mcview_t * view)
721 mcview_toggle_magic_mode (view);
722 mcview_update (view);
725 /* --------------------------------------------------------------------------------------------- */
727 void
728 mcview_toggle_nroff_mode_cmd (mcview_t * view)
730 mcview_toggle_nroff_mode (view);
731 mcview_update (view);
734 /* --------------------------------------------------------------------------------------------- */
736 void
737 mcview_toggle_ruler_cmd (mcview_t * view)
739 mcview_display_toggle_ruler (view);
742 /* --------------------------------------------------------------------------------------------- */