Split file src/keybind.[ch] to lib/keybind.[ch] and src/keybind-defaults.[ch].
[midnight-commander.git] / src / viewer / actions_cmd.c
blobf58f352244600d5b01297a28a173ce52514040e9
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" /* is_idle() */
56 #include "lib/lock.h" /* lock_file() */
57 #include "lib/util.h"
58 #include "lib/widget.h"
59 #include "lib/charsets.h"
61 #include "src/panel.h"
62 #include "src/layout.h"
63 #include "src/history.h"
64 #include "src/cmd.h"
65 #include "src/execute.h"
66 #include "src/help.h"
67 #include "src/keybind-defaults.h"
68 #include "src/main.h" /* midnight_shutdown */
70 #include "internal.h"
71 #include "mcviewer.h"
73 /*** global variables ****************************************************************************/
75 /*** file scope macro definitions ****************************************************************/
77 /*** file scope type declarations ****************************************************************/
79 /*** file scope variables ************************************************************************/
81 /*** file scope functions ************************************************************************/
82 /* --------------------------------------------------------------------------------------------- */
84 /* Both views */
85 static void
86 mcview_search (mcview_t * view)
88 if (mcview_dialog_search (view))
89 mcview_do_search (view);
92 /* --------------------------------------------------------------------------------------------- */
94 static void
95 mcview_continue_search_cmd (mcview_t * view)
97 if (view->last_search_string != NULL)
99 mcview_do_search (view);
101 else
103 /* find last search string in history */
104 GList *history;
105 history = history_get (MC_HISTORY_SHARED_SEARCH);
106 if (history != NULL && history->data != NULL)
108 view->last_search_string = (gchar *) g_strdup (history->data);
109 history = g_list_first (history);
110 g_list_foreach (history, (GFunc) g_free, NULL);
111 g_list_free (history);
113 view->search = mc_search_new (view->last_search_string, -1);
114 view->search_nroff_seq = mcview_nroff_seq_new (view);
116 if (!view->search)
118 /* if not... then ask for an expression */
119 g_free (view->last_search_string);
120 view->last_search_string = NULL;
121 mcview_search (view);
123 else
125 view->search->search_type = mcview_search_options.type;
126 view->search->is_all_charsets = mcview_search_options.all_codepages;
127 view->search->is_case_sensitive = mcview_search_options.case_sens;
128 view->search->whole_words = mcview_search_options.whole_words;
129 view->search->search_fn = mcview_search_cmd_callback;
130 view->search->update_fn = mcview_search_update_cmd_callback;
132 mcview_do_search (view);
135 else
137 /* if not... then ask for an expression */
138 g_free (view->last_search_string);
139 view->last_search_string = NULL;
140 mcview_search (view);
145 /* --------------------------------------------------------------------------------------------- */
147 static void
148 mcview_hook (void *v)
150 mcview_t *view = (mcview_t *) v;
151 WPanel *panel;
153 /* If the user is busy typing, wait until he finishes to update the
154 screen */
155 if (!is_idle ())
157 if (!hook_present (idle_hook, mcview_hook))
158 add_hook (&idle_hook, mcview_hook, v);
159 return;
162 delete_hook (&idle_hook, mcview_hook);
164 if (get_current_type () == view_listing)
165 panel = current_panel;
166 else if (get_other_type () == view_listing)
167 panel = other_panel;
168 else
169 return;
171 mcview_done (view);
172 mcview_init (view);
173 mcview_load (view, 0, panel->dir.list[panel->selected].fname, 0);
174 mcview_display (view);
177 /* --------------------------------------------------------------------------------------------- */
179 static cb_ret_t
180 mcview_handle_editkey (mcview_t * view, int key)
182 struct hexedit_change_node *node;
183 int byte_val;
185 /* Has there been a change at this position? */
186 node = view->change_list;
187 while ((node != NULL) && (node->offset != view->hex_cursor))
188 node = node->next;
190 if (!view->hexview_in_text)
192 /* Hex editing */
193 unsigned int hexvalue = 0;
195 if (key >= '0' && key <= '9')
196 hexvalue = 0 + (key - '0');
197 else if (key >= 'A' && key <= 'F')
198 hexvalue = 10 + (key - 'A');
199 else if (key >= 'a' && key <= 'f')
200 hexvalue = 10 + (key - 'a');
201 else
202 return MSG_NOT_HANDLED;
204 if (node != NULL)
205 byte_val = node->value;
206 else
207 mcview_get_byte (view, view->hex_cursor, &byte_val);
209 if (view->hexedit_lownibble)
210 byte_val = (byte_val & 0xf0) | (hexvalue);
211 else
212 byte_val = (byte_val & 0x0f) | (hexvalue << 4);
214 else
216 /* Text editing */
217 if (key < 256 && ((key == '\n') || is_printable (key)))
218 byte_val = key;
219 else
220 return MSG_NOT_HANDLED;
223 if ((view->filename != NULL) && (view->filename[0] != '\0') && (view->change_list == NULL))
224 view->locked = mcview_lock_file (view);
226 if (node == NULL)
228 node = g_new (struct hexedit_change_node, 1);
229 node->offset = view->hex_cursor;
230 node->value = byte_val;
231 mcview_enqueue_change (&view->change_list, node);
233 else
234 node->value = byte_val;
236 view->dirty++;
237 mcview_move_right (view, 1);
239 return MSG_HANDLED;
242 /* --------------------------------------------------------------------------------------------- */
244 static cb_ret_t
245 mcview_execute_cmd (mcview_t * view, unsigned long command)
247 int res = MSG_HANDLED;
249 switch (command)
251 case CK_ViewHelp:
252 interactive_display (NULL, "[Internal File Viewer]");
253 break;
254 case CK_ViewToggleWrapMode:
255 /* Toggle between wrapped and unwrapped view */
256 mcview_toggle_wrap_mode (view);
257 break;
258 case CK_ViewToggleHexEditMode:
259 /* Toggle between hexview and hexedit mode */
260 mcview_toggle_hexedit_mode (view);
261 break;
262 case CK_ViewToggleHexMode:
263 /* Toggle between hex view and text view */
264 mcview_toggle_hex_mode (view);
265 break;
266 case CK_ViewGoto:
268 off_t addr;
270 if (mcview_dialog_goto (view, &addr))
272 if (addr >= 0)
273 mcview_moveto_offset (view, addr);
274 else
276 message (D_ERROR, _("Warning"), _("Invalid value"));
277 view->dirty++;
280 break;
282 case CK_ViewHexEditSave:
283 mcview_hexedit_save_changes (view);
284 break;
285 case CK_ViewSearch:
286 mcview_search (view);
287 break;
288 case CK_ViewToggleMagicMode:
289 mcview_toggle_magic_mode (view);
290 break;
291 case CK_ViewToggleNroffMode:
292 mcview_toggle_nroff_mode (view);
293 break;
294 case CK_ViewToggleHexNavMode:
295 view->hexview_in_text = !view->hexview_in_text;
296 view->dirty++;
297 break;
298 case CK_ViewMoveToBol:
299 mcview_moveto_bol (view);
300 break;
301 case CK_ViewMoveToEol:
302 mcview_moveto_eol (view);
303 break;
304 case CK_ViewMoveLeft:
305 mcview_move_left (view, 1);
306 break;
307 case CK_ViewMoveRight:
308 mcview_move_right (view, 1);
309 break;
310 case CK_ViewMoveLeft10:
311 if (!view->hex_mode)
312 mcview_move_left (view, 10);
313 break;
314 case CK_ViewMoveRight10:
315 if (!view->hex_mode)
316 mcview_move_right (view, 10);
317 break;
318 case CK_ViewContinueSearch:
319 mcview_continue_search_cmd (view);
320 break;
321 case CK_ViewToggleRuler:
322 mcview_display_toggle_ruler (view);
323 break;
324 case CK_ViewMoveUp:
325 mcview_move_up (view, 1);
326 break;
327 case CK_ViewMoveDown:
328 mcview_move_down (view, 1);
329 break;
330 case CK_ViewMoveHalfPgUp:
331 mcview_move_up (view, (view->data_area.height + 1) / 2);
332 break;
333 case CK_ViewMoveHalfPgDn:
334 mcview_move_down (view, (view->data_area.height + 1) / 2);
335 break;
336 case CK_ViewMovePgUp:
337 mcview_move_up (view, view->data_area.height);
338 break;
339 case CK_ViewMovePgDn:
340 mcview_move_down (view, view->data_area.height);
341 break;
342 case CK_ViewMoveTop:
343 mcview_moveto_top (view);
344 break;
345 case CK_ViewMoveBottom:
346 mcview_moveto_bottom (view);
347 break;
348 case CK_ShowCommandLine:
349 view_other_cmd ();
350 break;
352 // Unlike Ctrl-O, run a new shell if the subshell is not running
353 case '!':
354 exec_shell ();
355 return MSG_HANDLED;
357 case CK_ViewGotoBookmark:
358 view->marks[view->marker] = view->dpy_start;
359 break;
360 case CK_ViewNewBookmark:
361 view->dpy_start = view->marks[view->marker];
362 view->dirty++;
363 break;
364 case CK_SelectCodepage:
365 mcview_select_encoding (view);
366 view->dirty++;
367 break;
368 case CK_ViewNextFile:
369 case CK_ViewPrevFile:
370 /* Use to indicate parent that we want to see the next/previous file */
371 /* Does not work in panel mode */
372 if (!mcview_is_in_panel (view))
373 view->move_dir = (command == CK_ViewNextFile) ? 1 : -1;
374 /* fallthrough */
375 case CK_ViewQuit:
376 if (!mcview_is_in_panel (view))
377 dlg_stop (view->widget.owner);
378 break;
379 default:
380 res = MSG_NOT_HANDLED;
382 return res;
385 /* --------------------------------------------------------------------------------------------- */
386 /** Both views */
387 static cb_ret_t
388 mcview_handle_key (mcview_t * view, int key)
390 unsigned long command;
392 key = convert_from_input_c (key);
394 if (view->hex_mode)
396 if (view->hexedit_mode && (mcview_handle_editkey (view, key) == MSG_HANDLED))
397 return MSG_HANDLED;
399 command = keybind_lookup_keymap_command (view->hex_map, key);
400 if ((command != CK_Ignore_Key) && (mcview_execute_cmd (view, command) == MSG_HANDLED))
401 return MSG_HANDLED;
404 command = keybind_lookup_keymap_command (view->plain_map, key);
405 if ((command != CK_Ignore_Key) && (mcview_execute_cmd (view, command) == MSG_HANDLED))
406 return MSG_HANDLED;
408 #ifdef MC_ENABLE_DEBUGGING_CODE
409 if (c == 't')
410 { /* mnemonic: "test" */
411 mcview_ccache_dump (view);
412 return MSG_HANDLED;
414 #endif
415 if (key >= '0' && key <= '9')
416 view->marker = key - '0';
418 /* Key not used */
419 return MSG_NOT_HANDLED;
423 /* --------------------------------------------------------------------------------------------- */
425 static inline void
426 mcview_adjust_size (Dlg_head * h)
428 mcview_t *view;
429 WButtonBar *b;
431 /* Look up the viewer and the buttonbar, we assume only two widgets here */
432 view = (mcview_t *) find_widget_type (h, mcview_callback);
433 b = find_buttonbar (h);
435 widget_set_size (&view->widget, 0, 0, LINES - 1, COLS);
436 widget_set_size (&b->widget, LINES - 1, 0, 1, COLS);
438 mcview_compute_areas (view);
439 mcview_update_bytes_per_line (view);
443 /* --------------------------------------------------------------------------------------------- */
444 /*** public functions ****************************************************************************/
445 /* --------------------------------------------------------------------------------------------- */
447 cb_ret_t
448 mcview_callback (Widget * w, widget_msg_t msg, int parm)
450 mcview_t *view = (mcview_t *) w;
451 cb_ret_t i;
453 mcview_compute_areas (view);
454 mcview_update_bytes_per_line (view);
456 switch (msg)
458 case WIDGET_INIT:
459 if (mcview_is_in_panel (view))
460 add_hook (&select_file_hook, mcview_hook, view);
461 else
462 view->dpy_bbar_dirty = TRUE;
463 return MSG_HANDLED;
465 case WIDGET_DRAW:
466 mcview_display (view);
467 return MSG_HANDLED;
469 case WIDGET_CURSOR:
470 if (view->hex_mode)
471 mcview_place_cursor (view);
472 return MSG_HANDLED;
474 case WIDGET_KEY:
475 i = mcview_handle_key (view, parm);
476 mcview_update (view);
477 return i;
479 case WIDGET_COMMAND:
480 i = mcview_execute_cmd (view, parm);
481 mcview_update (view);
482 return i;
484 case WIDGET_FOCUS:
485 view->dpy_bbar_dirty = TRUE;
486 mcview_update (view);
487 return MSG_HANDLED;
489 case WIDGET_DESTROY:
490 if (mcview_is_in_panel (view))
492 delete_hook (&select_file_hook, mcview_hook);
494 if (midnight_shutdown)
495 mcview_ok_to_quit (view);
497 mcview_done (view);
498 return MSG_HANDLED;
500 default:
501 return default_proc (msg, parm);
505 /* --------------------------------------------------------------------------------------------- */
507 cb_ret_t
508 mcview_dialog_callback (Dlg_head * h, Widget * sender, dlg_msg_t msg, int parm, void *data)
510 mcview_t *view;
512 switch (msg)
514 case DLG_RESIZE:
515 mcview_adjust_size (h);
516 return MSG_HANDLED;
518 case DLG_ACTION:
519 /* command from buttonbar */
520 view = (mcview_t *) data;
521 return send_message ((Widget *) view, WIDGET_COMMAND, parm);
523 case DLG_VALIDATE:
524 view = (mcview_t *) find_widget_type (h, mcview_callback);
525 h->state = DLG_ACTIVE; /* don't stop the dialog before final decision */
526 if (mcview_ok_to_quit (view))
527 h->state = DLG_CLOSED;
528 else
529 mcview_update (view);
530 return MSG_HANDLED;
532 default:
533 return default_dlg_callback (h, sender, msg, parm, data);
537 /* --------------------------------------------------------------------------------------------- */