Rename keymap files to be with .keymap suffix.
[midnight-commander.git] / src / viewer / actions_cmd.c
blobe6baa020d704f3cf5546b5690affcdc67ce11434
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, 2011
7 The Free Software Foundation, Inc.
9 Written by:
10 Miguel de Icaza, 1994, 1995, 1998
11 Janne Kukonlehto, 1994, 1995
12 Jakub Jelinek, 1995
13 Joseph M. Hinkle, 1996
14 Norbert Warmuth, 1997
15 Pavel Machek, 1998
16 Roland Illig <roland.illig@gmx.de>, 2004, 2005
17 Slava Zanko <slavazanko@google.com>, 2009
18 Andrew Borodin <aborodin@vmail.ru>, 2009
19 Ilia Maslakov <il.smind@gmail.com>, 2009
21 This file is part of the Midnight Commander.
23 The Midnight Commander is free software: you can redistribute it
24 and/or modify it under the terms of the GNU General Public License as
25 published by the Free Software Foundation, either version 3 of the License,
26 or (at your option) any later version.
28 The Midnight Commander is distributed in the hope that it will be useful,
29 but WITHOUT ANY WARRANTY; without even the implied warranty of
30 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
31 GNU General Public License for more details.
33 You should have received a copy of the GNU General Public License
34 along with this program. If not, see <http://www.gnu.org/licenses/>.
38 The functions in this section can be bound to hotkeys. They are all
39 of the same type (taking a pointer to mcview_t as parameter and
40 returning void). TODO: In the not-too-distant future, these commands
41 will become fully configurable, like they already are in the
42 internal editor. By convention, all the function names end in
43 "_cmd".
46 #include <config.h>
48 #include <errno.h>
49 #include <stdlib.h>
51 #include "lib/global.h"
53 #include "lib/tty/tty.h"
54 #include "lib/tty/key.h" /* is_idle() */
55 #include "lib/lock.h" /* lock_file() */
56 #include "lib/util.h"
57 #include "lib/widget.h"
58 #ifdef HAVE_CHARSET
59 #include "lib/charsets.h"
60 #endif
61 #include "lib/event.h" /* mc_event_raise() */
63 #include "src/filemanager/layout.h"
64 #include "src/filemanager/cmd.h"
65 #include "src/filemanager/midnight.h" /* current_panel */
67 #include "src/history.h"
68 #include "src/execute.h"
69 #include "src/keybind-defaults.h"
71 #include "internal.h"
72 #include "mcviewer.h"
74 /*** global variables ****************************************************************************/
76 /*** file scope macro definitions ****************************************************************/
78 /*** file scope type declarations ****************************************************************/
80 /*** file scope variables ************************************************************************/
82 /*** file scope functions ************************************************************************/
83 /* --------------------------------------------------------------------------------------------- */
85 /* Both views */
86 static void
87 mcview_search (mcview_t * view)
89 if (mcview_dialog_search (view))
90 mcview_do_search (view);
93 /* --------------------------------------------------------------------------------------------- */
95 static void
96 mcview_continue_search_cmd (mcview_t * view)
98 if (view->last_search_string != NULL)
100 mcview_do_search (view);
102 else
104 /* find last search string in history */
105 GList *history;
106 history = history_get (MC_HISTORY_SHARED_SEARCH);
107 if (history != NULL && history->data != NULL)
109 view->last_search_string = (gchar *) g_strdup (history->data);
110 history = g_list_first (history);
111 g_list_foreach (history, (GFunc) g_free, NULL);
112 g_list_free (history);
114 view->search = mc_search_new (view->last_search_string, -1);
115 view->search_nroff_seq = mcview_nroff_seq_new (view);
117 if (!view->search)
119 /* if not... then ask for an expression */
120 g_free (view->last_search_string);
121 view->last_search_string = NULL;
122 mcview_search (view);
124 else
126 view->search->search_type = mcview_search_options.type;
127 view->search->is_all_charsets = mcview_search_options.all_codepages;
128 view->search->is_case_sensitive = mcview_search_options.case_sens;
129 view->search->whole_words = mcview_search_options.whole_words;
130 view->search->search_fn = mcview_search_cmd_callback;
131 view->search->update_fn = mcview_search_update_cmd_callback;
133 mcview_do_search (view);
136 else
138 /* if not... then ask for an expression */
139 g_free (view->last_search_string);
140 view->last_search_string = NULL;
141 mcview_search (view);
146 /* --------------------------------------------------------------------------------------------- */
148 static void
149 mcview_hook (void *v)
151 mcview_t *view = (mcview_t *) v;
152 WPanel *panel;
154 /* If the user is busy typing, wait until he finishes to update the
155 screen */
156 if (!is_idle ())
158 if (!hook_present (idle_hook, mcview_hook))
159 add_hook (&idle_hook, mcview_hook, v);
160 return;
163 delete_hook (&idle_hook, mcview_hook);
165 if (get_current_type () == view_listing)
166 panel = current_panel;
167 else if (get_other_type () == view_listing)
168 panel = other_panel;
169 else
170 return;
172 mcview_done (view);
173 mcview_init (view);
174 mcview_load (view, 0, panel->dir.list[panel->selected].fname, 0);
175 mcview_display (view);
178 /* --------------------------------------------------------------------------------------------- */
180 static cb_ret_t
181 mcview_handle_editkey (mcview_t * view, int key)
183 struct hexedit_change_node *node;
184 int byte_val;
186 /* Has there been a change at this position? */
187 node = view->change_list;
188 while ((node != NULL) && (node->offset != view->hex_cursor))
189 node = node->next;
191 if (!view->hexview_in_text)
193 /* Hex editing */
194 unsigned int hexvalue = 0;
196 if (key >= '0' && key <= '9')
197 hexvalue = 0 + (key - '0');
198 else if (key >= 'A' && key <= 'F')
199 hexvalue = 10 + (key - 'A');
200 else if (key >= 'a' && key <= 'f')
201 hexvalue = 10 + (key - 'a');
202 else
203 return MSG_NOT_HANDLED;
205 if (node != NULL)
206 byte_val = node->value;
207 else
208 mcview_get_byte (view, view->hex_cursor, &byte_val);
210 if (view->hexedit_lownibble)
211 byte_val = (byte_val & 0xf0) | (hexvalue);
212 else
213 byte_val = (byte_val & 0x0f) | (hexvalue << 4);
215 else
217 /* Text editing */
218 if (key < 256 && ((key == '\n') || is_printable (key)))
219 byte_val = key;
220 else
221 return MSG_NOT_HANDLED;
224 if ((view->filename_vpath != NULL)
225 && (*(vfs_path_get_last_path_str (view->filename_vpath)) != '\0')
226 && (view->change_list == NULL))
227 view->locked = lock_file (view->filename_vpath);
229 if (node == NULL)
231 node = g_new (struct hexedit_change_node, 1);
232 node->offset = view->hex_cursor;
233 node->value = byte_val;
234 mcview_enqueue_change (&view->change_list, node);
236 else
237 node->value = byte_val;
239 view->dirty++;
240 mcview_move_right (view, 1);
242 return MSG_HANDLED;
245 /* --------------------------------------------------------------------------------------------- */
247 static void
248 mcview_load_next_prev_init (mcview_t * view)
250 if (mc_global.mc_run_mode != MC_RUN_VIEWER)
252 /* get file list from current panel. Update it each time */
253 view->dir = &current_panel->dir;
254 view->dir_count = &current_panel->count;
255 view->dir_idx = &current_panel->selected;
257 else if (view->dir == NULL)
259 /* Run from command line */
260 /* Run 1st time. Load/get directory */
262 /* TODO: check mtime of directory to reload it */
264 char *full_fname;
265 const char *fname;
266 size_t fname_len;
267 int i;
269 /* load directory where requested file is */
270 view->dir = g_new0 (dir_list, 1);
271 view->dir_count = g_new (int, 1);
272 view->dir_idx = g_new (int, 1);
274 *view->dir_count = do_load_dir (view->workdir_vpath, view->dir, (sortfn *) sort_name, FALSE,
275 TRUE, FALSE, NULL);
277 full_fname = vfs_path_to_str (view->filename_vpath);
278 fname = x_basename (full_fname);
279 fname_len = strlen (fname);
281 /* search current file in the list */
282 for (i = 0; i != *view->dir_count; i++)
284 const file_entry *fe = &view->dir->list[i];
286 if (fname_len == fe->fnamelen && strncmp (fname, fe->fname, fname_len) == 0)
287 break;
290 g_free (full_fname);
292 *view->dir_idx = i;
296 /* --------------------------------------------------------------------------------------------- */
298 static void
299 mcview_scan_for_file (mcview_t * view, int direction)
301 int i;
303 for (i = *view->dir_idx + direction; i != *view->dir_idx; i += direction)
305 if (i < 0)
306 i = *view->dir_count - 1;
307 if (i == *view->dir_count)
308 i = 0;
309 if (!S_ISDIR (view->dir->list[i].st.st_mode))
310 break;
313 *view->dir_idx = i;
316 /* --------------------------------------------------------------------------------------------- */
318 static void
319 mcview_load_next_prev (mcview_t * view, int direction)
321 dir_list *dir;
322 int *dir_count, *dir_idx;
323 vfs_path_t *vfile;
324 char *file;
326 mcview_load_next_prev_init (view);
327 mcview_scan_for_file (view, direction);
329 /* reinit view */
330 dir = view->dir;
331 dir_count = view->dir_count;
332 dir_idx = view->dir_idx;
333 view->dir = NULL;
334 view->dir_count = NULL;
335 view->dir_idx = NULL;
336 vfile = vfs_path_append_new (view->workdir_vpath, dir->list[*dir_idx].fname, (char *) NULL);
337 file = vfs_path_to_str (vfile);
338 vfs_path_free (vfile);
339 mcview_done (view);
340 mcview_init (view);
341 mcview_load (view, NULL, file, 0);
342 g_free (file);
343 view->dir = dir;
344 view->dir_count = dir_count;
345 view->dir_idx = dir_idx;
347 view->dpy_bbar_dirty = FALSE; /* FIXME */
348 view->dirty++;
351 /* --------------------------------------------------------------------------------------------- */
353 static cb_ret_t
354 mcview_execute_cmd (mcview_t * view, unsigned long command)
356 int res = MSG_HANDLED;
358 switch (command)
360 case CK_Help:
362 ev_help_t event_data = { NULL, "[Internal File Viewer]" };
363 mc_event_raise (MCEVENT_GROUP_CORE, "help", &event_data);
365 break;
366 case CK_WrapMode:
367 /* Toggle between wrapped and unwrapped view */
368 mcview_toggle_wrap_mode (view);
369 break;
370 case CK_HexEditMode:
371 /* Toggle between hexview and hexedit mode */
372 mcview_toggle_hexedit_mode (view);
373 break;
374 case CK_HexMode:
375 /* Toggle between hex view and text view */
376 mcview_toggle_hex_mode (view);
377 break;
378 case CK_Goto:
380 off_t addr;
382 if (mcview_dialog_goto (view, &addr))
384 if (addr >= 0)
385 mcview_moveto_offset (view, addr);
386 else
388 message (D_ERROR, _("Warning"), _("Invalid value"));
389 view->dirty++;
392 break;
394 case CK_Save:
395 mcview_hexedit_save_changes (view);
396 break;
397 case CK_Search:
398 mcview_search (view);
399 break;
400 case CK_SearchForward:
401 mcview_search_options.backwards = FALSE;
402 mcview_search (view);
403 break;
404 case CK_SearchBackward:
405 mcview_search_options.backwards = TRUE;
406 mcview_search (view);
407 break;
408 case CK_MagicMode:
409 mcview_toggle_magic_mode (view);
410 break;
411 case CK_NroffMode:
412 mcview_toggle_nroff_mode (view);
413 break;
414 case CK_ToggleNavigation:
415 view->hexview_in_text = !view->hexview_in_text;
416 view->dirty++;
417 break;
418 case CK_Home:
419 mcview_moveto_bol (view);
420 break;
421 case CK_End:
422 mcview_moveto_eol (view);
423 break;
424 case CK_Left:
425 mcview_move_left (view, 1);
426 break;
427 case CK_Right:
428 mcview_move_right (view, 1);
429 break;
430 case CK_LeftQuick:
431 if (!view->hex_mode)
432 mcview_move_left (view, 10);
433 break;
434 case CK_RightQuick:
435 if (!view->hex_mode)
436 mcview_move_right (view, 10);
437 break;
438 case CK_SearchContinue:
439 mcview_continue_search_cmd (view);
440 break;
441 case CK_SearchForwardContinue:
442 mcview_search_options.backwards = FALSE;
443 mcview_continue_search_cmd (view);
444 break;
445 case CK_SearchBackwardContinue:
446 mcview_search_options.backwards = TRUE;
447 mcview_continue_search_cmd (view);
448 break;
449 case CK_Ruler:
450 mcview_display_toggle_ruler (view);
451 break;
452 case CK_Up:
453 mcview_move_up (view, 1);
454 break;
455 case CK_Down:
456 mcview_move_down (view, 1);
457 break;
458 case CK_HalfPageUp:
459 mcview_move_up (view, (view->data_area.height + 1) / 2);
460 break;
461 case CK_HalfPageDown:
462 mcview_move_down (view, (view->data_area.height + 1) / 2);
463 break;
464 case CK_PageUp:
465 mcview_move_up (view, view->data_area.height);
466 break;
467 case CK_PageDown:
468 mcview_move_down (view, view->data_area.height);
469 break;
470 case CK_Top:
471 mcview_moveto_top (view);
472 break;
473 case CK_Bottom:
474 mcview_moveto_bottom (view);
475 break;
476 case CK_Shell:
477 view_other_cmd ();
478 break;
479 case CK_BookmarkGoto:
480 view->marks[view->marker] = view->dpy_start;
481 break;
482 case CK_Bookmark:
483 view->dpy_start = view->marks[view->marker];
484 view->dirty++;
485 break;
486 #ifdef HAVE_CHARSET
487 case CK_SelectCodepage:
488 mcview_select_encoding (view);
489 view->dirty++;
490 break;
491 #endif
492 case CK_FileNext:
493 case CK_FilePrev:
494 /* Does not work in panel mode */
495 if (!mcview_is_in_panel (view))
496 mcview_load_next_prev (view, command == CK_FileNext ? 1 : -1);
497 break;
498 case CK_Quit:
499 if (!mcview_is_in_panel (view))
500 dlg_stop (WIDGET (view)->owner);
501 break;
502 case CK_Cancel:
503 /* don't close viewer due to SIGINT */
504 break;
505 default:
506 res = MSG_NOT_HANDLED;
508 return res;
511 /* --------------------------------------------------------------------------------------------- */
512 /** Both views */
513 static cb_ret_t
514 mcview_handle_key (mcview_t * view, int key)
516 unsigned long command;
518 #ifdef HAVE_CHARSET
519 key = convert_from_input_c (key);
520 #endif
522 if (view->hex_mode)
524 if (view->hexedit_mode && (mcview_handle_editkey (view, key) == MSG_HANDLED))
525 return MSG_HANDLED;
527 command = keybind_lookup_keymap_command (viewer_hex_map, key);
528 if ((command != CK_IgnoreKey) && (mcview_execute_cmd (view, command) == MSG_HANDLED))
529 return MSG_HANDLED;
532 command = keybind_lookup_keymap_command (viewer_map, key);
533 if ((command != CK_IgnoreKey) && (mcview_execute_cmd (view, command) == MSG_HANDLED))
534 return MSG_HANDLED;
536 #ifdef MC_ENABLE_DEBUGGING_CODE
537 if (c == 't')
538 { /* mnemonic: "test" */
539 mcview_ccache_dump (view);
540 return MSG_HANDLED;
542 #endif
543 if (key >= '0' && key <= '9')
544 view->marker = key - '0';
546 /* Key not used */
547 return MSG_NOT_HANDLED;
551 /* --------------------------------------------------------------------------------------------- */
553 static inline void
554 mcview_adjust_size (WDialog * h)
556 mcview_t *view;
557 WButtonBar *b;
559 /* Look up the viewer and the buttonbar, we assume only two widgets here */
560 view = (mcview_t *) find_widget_type (h, mcview_callback);
561 b = find_buttonbar (h);
563 widget_set_size (WIDGET (view), 0, 0, LINES - 1, COLS);
564 widget_set_size (WIDGET (b), LINES - 1, 0, 1, COLS);
566 mcview_compute_areas (view);
567 mcview_update_bytes_per_line (view);
571 /* --------------------------------------------------------------------------------------------- */
572 /*** public functions ****************************************************************************/
573 /* --------------------------------------------------------------------------------------------- */
575 cb_ret_t
576 mcview_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)
578 mcview_t *view = (mcview_t *) w;
579 cb_ret_t i;
581 mcview_compute_areas (view);
582 mcview_update_bytes_per_line (view);
584 switch (msg)
586 case MSG_INIT:
587 if (mcview_is_in_panel (view))
588 add_hook (&select_file_hook, mcview_hook, view);
589 else
590 view->dpy_bbar_dirty = TRUE;
591 return MSG_HANDLED;
593 case MSG_DRAW:
594 mcview_display (view);
595 return MSG_HANDLED;
597 case MSG_CURSOR:
598 if (view->hex_mode)
599 mcview_place_cursor (view);
600 return MSG_HANDLED;
602 case MSG_KEY:
603 i = mcview_handle_key (view, parm);
604 mcview_update (view);
605 return i;
607 case MSG_ACTION:
608 i = mcview_execute_cmd (view, parm);
609 mcview_update (view);
610 return i;
612 case MSG_FOCUS:
613 view->dpy_bbar_dirty = TRUE;
614 mcview_update (view);
615 return MSG_HANDLED;
617 case MSG_DESTROY:
618 if (mcview_is_in_panel (view))
620 delete_hook (&select_file_hook, mcview_hook);
622 if (mc_global.midnight_shutdown)
623 mcview_ok_to_quit (view);
625 mcview_done (view);
626 return MSG_HANDLED;
628 default:
629 return widget_default_callback (w, sender, msg, parm, data);
633 /* --------------------------------------------------------------------------------------------- */
635 cb_ret_t
636 mcview_dialog_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)
638 WDialog *h = DIALOG (w);
639 mcview_t *view;
641 switch (msg)
643 case MSG_RESIZE:
644 mcview_adjust_size (h);
645 return MSG_HANDLED;
647 case MSG_ACTION:
648 /* shortcut */
649 if (sender == NULL)
650 return mcview_execute_cmd (NULL, parm);
651 /* message from buttonbar */
652 if (sender == WIDGET (find_buttonbar (h)))
654 if (data != NULL)
655 return send_message (data, NULL, MSG_ACTION, parm, NULL);
657 view = (mcview_t *) find_widget_type (h, mcview_callback);
658 return mcview_execute_cmd (view, parm);
660 return MSG_NOT_HANDLED;
662 case MSG_VALIDATE:
663 view = (mcview_t *) find_widget_type (h, mcview_callback);
664 h->state = DLG_ACTIVE; /* don't stop the dialog before final decision */
665 if (mcview_ok_to_quit (view))
666 h->state = DLG_CLOSED;
667 else
668 mcview_update (view);
669 return MSG_HANDLED;
671 default:
672 return dlg_default_callback (w, sender, msg, parm, data);
676 /* --------------------------------------------------------------------------------------------- */