x86_64: Cleanup of code for master
[midnight-commander.git] / src / viewer / actions_cmd.c
blob52e8ca8be0615b5ee1c2e6db3579eb55c4d37401
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 "lib/global.h"
54 #include "lib/tty/tty.h"
55 #include "lib/tty/key.h"
57 #include "src/dialog.h" /* cb_ret_t */
58 #include "src/panel.h"
59 #include "src/layout.h"
60 #include "src/wtools.h"
61 #include "src/history.h"
62 #include "src/charsets.h"
63 #include "src/cmd.h"
64 #include "src/execute.h"
65 #include "src/help.h"
66 #include "src/keybind.h"
67 #include "src/cmddef.h" /* CK_ cmd name const */
69 #include "internal.h"
70 #include "mcviewer.h"
72 /*** global variables ****************************************************************************/
74 /*** file scope macro definitions ****************************************************************/
76 /*** file scope type declarations ****************************************************************/
78 /*** file scope variables ************************************************************************/
80 /*** file scope functions ************************************************************************/
82 /* Both views */
83 static void
84 mcview_search (mcview_t *view)
86 if (mcview_dialog_search (view))
87 mcview_do_search (view);
90 /* --------------------------------------------------------------------------------------------- */
92 static void
93 mcview_continue_search_cmd (mcview_t * view)
95 if (view->last_search_string != NULL) {
96 mcview_do_search (view);
97 } else {
98 /* find last search string in history */
99 GList *history;
100 history = history_get (MC_HISTORY_SHARED_SEARCH);
101 if (history != NULL && history->data != NULL) {
102 view->last_search_string = (gchar *) g_strdup(history->data);
103 history = g_list_first (history);
104 g_list_foreach (history, (GFunc) g_free, NULL);
105 g_list_free (history);
107 view->search = mc_search_new (view->last_search_string, -1);
108 view->search_nroff_seq = mcview_nroff_seq_new (view);
110 if (!view->search) {
111 /* if not... then ask for an expression */
112 g_free(view->last_search_string);
113 view->last_search_string = NULL;
114 mcview_search (view);
115 } else {
116 view->search->search_type = mcview_search_options.type;
117 view->search->is_all_charsets = mcview_search_options.all_codepages;
118 view->search->is_case_sentitive = mcview_search_options.case_sens;
119 view->search->whole_words = mcview_search_options.whole_words;
120 view->search->search_fn = mcview_search_cmd_callback;
121 view->search->update_fn = mcview_search_update_cmd_callback;
123 mcview_do_search (view);
125 } else {
126 /* if not... then ask for an expression */
127 g_free(view->last_search_string);
128 view->last_search_string = NULL;
129 mcview_search (view);
134 /* --------------------------------------------------------------------------------------------- */
136 /* Check for left and right arrows, possibly with modifiers */
137 static cb_ret_t
138 mcview_check_left_right_keys (mcview_t * view, int c)
140 if (c == KEY_LEFT) {
141 mcview_move_left (view, 1);
142 return MSG_HANDLED;
145 if (c == KEY_RIGHT) {
146 mcview_move_right (view, 1);
147 return MSG_HANDLED;
150 /* Ctrl with arrows moves by 10 postions in the unwrap mode */
151 if (view->hex_mode || view->text_wrap_mode)
152 return MSG_NOT_HANDLED;
154 if (c == (KEY_M_CTRL | KEY_LEFT)) {
155 if (view->dpy_text_column >= 10)
156 view->dpy_text_column -= 10;
157 else
158 view->dpy_text_column = 0;
159 view->dirty++;
160 return MSG_HANDLED;
163 if (c == (KEY_M_CTRL | KEY_RIGHT)) {
164 if (view->dpy_text_column <= OFFSETTYPE_MAX - 10)
165 view->dpy_text_column += 10;
166 else
167 view->dpy_text_column = OFFSETTYPE_MAX;
168 view->dirty++;
169 return MSG_HANDLED;
172 return MSG_NOT_HANDLED;
175 /* --------------------------------------------------------------------------------------------- */
177 static void
178 mcview_cmk_move_up (void *w, int n)
180 mcview_move_up ((mcview_t *) w, n);
183 /* --------------------------------------------------------------------------------------------- */
185 static void
186 mcview_cmk_move_down (void *w, int n)
188 mcview_move_down ((mcview_t *) w, n);
191 /* --------------------------------------------------------------------------------------------- */
193 static void
194 mcview_cmk_moveto_top (void *w, int n)
196 (void) &n;
197 mcview_moveto_top ((mcview_t *) w);
200 /* --------------------------------------------------------------------------------------------- */
202 static void
203 mcview_cmk_moveto_bottom (void *w, int n)
205 (void) &n;
206 mcview_moveto_bottom ((mcview_t *) w);
209 /* --------------------------------------------------------------------------------------------- */
211 static void
212 mcview_hook (void *v)
214 mcview_t *view = (mcview_t *) v;
215 WPanel *panel;
217 /* If the user is busy typing, wait until he finishes to update the
218 screen */
219 if (!is_idle ()) {
220 if (!hook_present (idle_hook, mcview_hook))
221 add_hook (&idle_hook, mcview_hook, v);
222 return;
225 delete_hook (&idle_hook, mcview_hook);
227 if (get_current_type () == view_listing)
228 panel = current_panel;
229 else if (get_other_type () == view_listing)
230 panel = other_panel;
231 else
232 return;
234 mcview_load (view, 0, panel->dir.list[panel->selected].fname, 0);
235 mcview_display (view);
238 /* --------------------------------------------------------------------------------------------- */
240 static cb_ret_t
241 mcview_handle_editkey (mcview_t * view, int key)
243 struct hexedit_change_node *node;
244 int byte_val;
245 /* Has there been a change at this position? */
246 node = view->change_list;
247 while (node && (node->offset != view->hex_cursor))
248 node = node->next;
250 if (!view->hexview_in_text) {
251 /* Hex editing */
252 unsigned int hexvalue = 0;
253 if (key >= '0' && key <= '9') {
254 hexvalue = 0 + (key - '0');
255 } else if (key >= 'A' && key <= 'F')
256 hexvalue = 10 + (key - 'A');
257 else if (key >= 'a' && key <= 'f')
258 hexvalue = 10 + (key - 'a');
259 else
260 return MSG_NOT_HANDLED;
262 if (node)
263 byte_val = node->value;
264 else
265 mcview_get_byte (view, view->hex_cursor, &byte_val);
267 if (view->hexedit_lownibble) {
268 byte_val = (byte_val & 0xf0) | (hexvalue);
269 } else {
270 byte_val = (byte_val & 0x0f) | (hexvalue << 4);
272 } else {
273 /* Text editing */
274 if (key < 256 && ((key == '\n') || is_printable (key)))
275 byte_val = key;
276 else
277 return MSG_NOT_HANDLED;
279 if (!node) {
280 node = g_new (struct hexedit_change_node, 1);
281 node->offset = view->hex_cursor;
282 node->value = byte_val;
283 mcview_enqueue_change (&view->change_list, node);
284 } else {
285 node->value = byte_val;
287 view->dirty++;
288 mcview_move_right (view, 1);
289 return MSG_HANDLED;
292 /* --------------------------------------------------------------------------------------------- */
294 static cb_ret_t
295 mcview_execute_cmd (mcview_t *view, unsigned long command)
297 int res = MSG_HANDLED;
299 switch (command) {
300 case CK_ViewHelp:
301 interactive_display (NULL, "[Internal File Viewer]");
302 break;
303 case CK_ViewToggleWrapMode:
304 /* Toggle between wrapped and unwrapped view */
305 mcview_toggle_wrap_mode (view);
306 view->dirty++;
307 break;
308 case CK_ViewToggleHexEditMode:
309 /* Toggle between hexview and hexedit mode */
310 mcview_toggle_hexedit_mode (view);
311 view->dirty++;
312 break;
313 case CK_ViewToggleHexMode:
314 /* Toggle between hex view and text view */
315 mcview_toggle_hex_mode (view);
316 view->dirty++;
317 break;
318 case CK_ViewGoto:
320 off_t addr;
322 if (mcview_dialog_goto (view, &addr)) {
323 if (addr >= 0)
324 mcview_moveto_offset (view, addr);
325 else {
326 message (D_ERROR, _("Warning"), _("Invalid value"));
327 view->dirty++;
330 break;
332 case CK_ViewHexEditSave:
333 mcview_hexedit_save_changes (view);
334 break;
335 case CK_ViewSearch:
336 mcview_search (view);
337 break;
338 case CK_ViewToggleMagicMode:
339 mcview_toggle_magic_mode (view);
340 view->dirty++;
341 break;
342 case CK_ViewToggleNroffMode:
343 mcview_toggle_nroff_mode (view);
344 view->dirty++;
345 break;
346 case CK_ViewToggleHexNavMode:
347 view->hexview_in_text = !view->hexview_in_text;
348 view->dirty++;
349 break;
350 case CK_ViewMoveToBol:
351 mcview_moveto_bol (view);
352 break;
353 case CK_ViewMoveToEol:
354 mcview_moveto_eol (view);
355 break;
356 case CK_ViewMoveLeft:
357 mcview_move_left (view, 1);
358 break;
359 case CK_ViewMoveRight:
360 mcview_move_right (view, 1);
361 break;
362 /* Continue search */
363 case CK_ViewContinueSearch:
364 mcview_continue_search_cmd (view);
365 break;
366 case CK_ViewToggleRuler:
367 mcview_display_toggle_ruler (view);
368 break;
369 case CK_ViewMoveUp:
370 mcview_move_up (view, 1);
371 break;
372 case CK_ViewMoveDown:
373 mcview_move_down (view, 1);
374 break;
375 case CK_ViewMoveHalfPgUp:
376 mcview_move_up (view, (view->data_area.height + 1) / 2);
377 break;
378 case CK_ViewMoveHalfPgDn:
379 mcview_move_down (view, (view->data_area.height + 1) / 2);
380 break;
381 case CK_ViewMovePgUp:
382 mcview_move_up (view, view->data_area.height);
383 break;
384 case CK_ViewMovePgDn:
385 mcview_move_down (view, view->data_area.height);
386 break;
387 case CK_ShowCommandLine:
388 view_other_cmd ();
389 break;
391 // Unlike Ctrl-O, run a new shell if the subshell is not running
392 case '!':
393 exec_shell ();
394 return MSG_HANDLED;
396 case CK_ViewGotoBookmark:
397 view->marks[view->marker] = view->dpy_start;
398 break;
399 case CK_ViewNewBookmark:
400 view->dpy_start = view->marks[view->marker];
401 view->dirty++;
402 break;
403 case CK_SelectCodepage:
404 mcview_select_encoding (view);
405 view->dirty++;
406 break;
407 case CK_ViewNextFile:
408 case CK_ViewPrevFile:
409 /* Use to indicate parent that we want to see the next/previous file */
410 /* Does not work in panel mode */
411 if (!mcview_is_in_panel (view))
412 view->move_dir = (command == CK_ViewNextFile) ? 1 : -1;
413 /* fallthrough */
414 case CK_ViewQuit:
415 if (mcview_ok_to_quit (view))
416 view->want_to_quit = TRUE;
417 break;
418 default :
419 res = MSG_NOT_HANDLED;
421 return res;
424 /* Both views */
425 static cb_ret_t
426 mcview_handle_key (mcview_t * view, int key)
428 unsigned long command;
430 key = convert_from_input_c (key);
432 if (view->hex_mode) {
433 if (view->hexedit_mode
434 && (mcview_handle_editkey (view, key) == MSG_HANDLED))
435 return MSG_HANDLED;
437 command = lookup_keymap_command (view->hex_map, key);
438 if ((command != CK_Ignore_Key)
439 && (mcview_execute_cmd (view, command) == MSG_HANDLED))
440 return MSG_HANDLED;
443 command = lookup_keymap_command (view->plain_map, key);
444 if ((command != CK_Ignore_Key)
445 && (mcview_execute_cmd (view, command) == MSG_HANDLED))
446 return MSG_HANDLED;
448 if (mcview_check_left_right_keys (view, key))
449 return MSG_HANDLED;
451 if (check_movement_keys (key, view->data_area.height + 1, view,
452 mcview_cmk_move_up, mcview_cmk_move_down,
453 mcview_cmk_moveto_top, mcview_cmk_moveto_bottom))
454 return MSG_HANDLED;
456 #ifdef MC_ENABLE_DEBUGGING_CODE
457 if (c == 't') { /* mnemonic: "test" */
458 mcview_ccache_dump (view);
459 return MSG_HANDLED;
461 #endif
462 if (key >= '0' && key <= '9')
463 view->marker = key - '0';
465 /* Key not used */
466 return MSG_NOT_HANDLED;
470 /* --------------------------------------------------------------------------------------------- */
472 static inline void
473 mcview_adjust_size (Dlg_head *h)
475 mcview_t *view;
476 WButtonBar *b;
478 /* Look up the viewer and the buttonbar, we assume only two widgets here */
479 view = (mcview_t *) find_widget_type (h, mcview_callback);
480 b = find_buttonbar (h);
482 widget_set_size (&view->widget, 0, 0, LINES - 1, COLS);
483 widget_set_size (&b->widget , LINES - 1, 0, 1, COLS);
485 mcview_compute_areas (view);
486 mcview_update_bytes_per_line (view);
490 /* --------------------------------------------------------------------------------------------- */
492 /*** public functions ****************************************************************************/
494 /* --------------------------------------------------------------------------------------------- */
496 cb_ret_t
497 mcview_callback (Widget * w, widget_msg_t msg, int parm)
499 mcview_t *view = (mcview_t *) w;
500 cb_ret_t i;
501 Dlg_head *h = view->widget.parent;
503 mcview_compute_areas (view);
504 mcview_update_bytes_per_line (view);
506 switch (msg) {
507 case WIDGET_INIT:
508 if (mcview_is_in_panel (view))
509 add_hook (&select_file_hook, mcview_hook, view);
510 else
511 view->dpy_bbar_dirty = TRUE;
512 return MSG_HANDLED;
514 case WIDGET_DRAW:
515 mcview_display (view);
516 return MSG_HANDLED;
518 case WIDGET_CURSOR:
519 if (view->hex_mode)
520 mcview_place_cursor (view);
521 return MSG_HANDLED;
523 case WIDGET_KEY:
524 i = mcview_handle_key (view, parm);
525 if (view->want_to_quit && !mcview_is_in_panel (view))
526 dlg_stop (h);
527 else
528 mcview_update (view);
529 return i;
531 case WIDGET_COMMAND:
532 i = mcview_execute_cmd (view, parm);
533 if (view->want_to_quit && !mcview_is_in_panel (view))
534 dlg_stop (h);
535 else
536 mcview_update (view);
537 return i;
539 case WIDGET_FOCUS:
540 view->dpy_bbar_dirty = TRUE;
541 mcview_update (view);
542 return MSG_HANDLED;
544 case WIDGET_DESTROY:
545 mcview_done (view);
546 if (mcview_is_in_panel (view))
547 delete_hook (&select_file_hook, mcview_hook);
548 return MSG_HANDLED;
550 default:
551 return default_proc (msg, parm);
555 /* --------------------------------------------------------------------------------------------- */
557 cb_ret_t
558 mcview_dialog_callback (Dlg_head *h, Widget *sender,
559 dlg_msg_t msg, int parm, void *data)
561 mcview_t *view = data;
563 switch (msg) {
564 case DLG_RESIZE:
565 mcview_adjust_size (h);
566 return MSG_HANDLED;
568 case DLG_ACTION:
569 /* command from buttonbar */
570 return send_message ((Widget *) view, WIDGET_COMMAND, parm);
572 default:
573 return default_dlg_callback (h, sender, msg, parm, data);
577 /* --------------------------------------------------------------------------------------------- */