Core, mceditor, mcviewer and mcdiffviewer code tweak and cleanup
[midnight-commander.git] / src / viewer / actions_cmd.c
blobb6f6440fa7b24c6cc7e6f60db721e6c10b5103b4
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 cb_ret_t
248 mcview_execute_cmd (mcview_t * view, unsigned long command)
250 int res = MSG_HANDLED;
252 switch (command)
254 case CK_Help:
256 ev_help_t event_data = { NULL, "[Internal File Viewer]" };
257 mc_event_raise (MCEVENT_GROUP_CORE, "help", &event_data);
259 break;
260 case CK_WrapMode:
261 /* Toggle between wrapped and unwrapped view */
262 mcview_toggle_wrap_mode (view);
263 break;
264 case CK_HexEditMode:
265 /* Toggle between hexview and hexedit mode */
266 mcview_toggle_hexedit_mode (view);
267 break;
268 case CK_HexMode:
269 /* Toggle between hex view and text view */
270 mcview_toggle_hex_mode (view);
271 break;
272 case CK_Goto:
274 off_t addr;
276 if (mcview_dialog_goto (view, &addr))
278 if (addr >= 0)
279 mcview_moveto_offset (view, addr);
280 else
282 message (D_ERROR, _("Warning"), _("Invalid value"));
283 view->dirty++;
286 break;
288 case CK_Save:
289 mcview_hexedit_save_changes (view);
290 break;
291 case CK_Search:
292 mcview_search (view);
293 break;
294 case CK_SearchForward:
295 mcview_search_options.backwards = FALSE;
296 mcview_search (view);
297 break;
298 case CK_SearchBackward:
299 mcview_search_options.backwards = TRUE;
300 mcview_search (view);
301 break;
302 case CK_MagicMode:
303 mcview_toggle_magic_mode (view);
304 break;
305 case CK_NroffMode:
306 mcview_toggle_nroff_mode (view);
307 break;
308 case CK_ToggleNavigation:
309 view->hexview_in_text = !view->hexview_in_text;
310 view->dirty++;
311 break;
312 case CK_Home:
313 mcview_moveto_bol (view);
314 break;
315 case CK_End:
316 mcview_moveto_eol (view);
317 break;
318 case CK_Left:
319 mcview_move_left (view, 1);
320 break;
321 case CK_Right:
322 mcview_move_right (view, 1);
323 break;
324 case CK_LeftQuick:
325 if (!view->hex_mode)
326 mcview_move_left (view, 10);
327 break;
328 case CK_RightQuick:
329 if (!view->hex_mode)
330 mcview_move_right (view, 10);
331 break;
332 case CK_SearchContinue:
333 mcview_continue_search_cmd (view);
334 break;
335 case CK_SearchForwardContinue:
336 mcview_search_options.backwards = FALSE;
337 mcview_continue_search_cmd (view);
338 break;
339 case CK_SearchBackwardContinue:
340 mcview_search_options.backwards = TRUE;
341 mcview_continue_search_cmd (view);
342 break;
343 case CK_Ruler:
344 mcview_display_toggle_ruler (view);
345 break;
346 case CK_Up:
347 mcview_move_up (view, 1);
348 break;
349 case CK_Down:
350 mcview_move_down (view, 1);
351 break;
352 case CK_HalfPageUp:
353 mcview_move_up (view, (view->data_area.height + 1) / 2);
354 break;
355 case CK_HalfPageDown:
356 mcview_move_down (view, (view->data_area.height + 1) / 2);
357 break;
358 case CK_PageUp:
359 mcview_move_up (view, view->data_area.height);
360 break;
361 case CK_PageDown:
362 mcview_move_down (view, view->data_area.height);
363 break;
364 case CK_Top:
365 mcview_moveto_top (view);
366 break;
367 case CK_Bottom:
368 mcview_moveto_bottom (view);
369 break;
370 case CK_Shell:
371 view_other_cmd ();
372 break;
373 case CK_BookmarkGoto:
374 view->marks[view->marker] = view->dpy_start;
375 break;
376 case CK_Bookmark:
377 view->dpy_start = view->marks[view->marker];
378 view->dirty++;
379 break;
380 #ifdef HAVE_CHARSET
381 case CK_SelectCodepage:
382 mcview_select_encoding (view);
383 view->dirty++;
384 break;
385 #endif
386 case CK_FileNext:
387 case CK_FilePrev:
388 /* Use to indicate parent that we want to see the next/previous file */
389 /* Does not work in panel mode */
390 if (!mcview_is_in_panel (view))
391 view->move_dir = (command == CK_FileNext) ? 1 : -1;
392 /* fallthrough */
393 case CK_Quit:
394 if (!mcview_is_in_panel (view))
395 dlg_stop (view->widget.owner);
396 break;
397 case CK_Cancel:
398 /* don't close viewer due to SIGINT */
399 break;
400 default:
401 res = MSG_NOT_HANDLED;
403 return res;
406 /* --------------------------------------------------------------------------------------------- */
407 /** Both views */
408 static cb_ret_t
409 mcview_handle_key (mcview_t * view, int key)
411 unsigned long command;
413 #ifdef HAVE_CHARSET
414 key = convert_from_input_c (key);
415 #endif
417 if (view->hex_mode)
419 if (view->hexedit_mode && (mcview_handle_editkey (view, key) == MSG_HANDLED))
420 return MSG_HANDLED;
422 command = keybind_lookup_keymap_command (viewer_hex_map, key);
423 if ((command != CK_IgnoreKey) && (mcview_execute_cmd (view, command) == MSG_HANDLED))
424 return MSG_HANDLED;
427 command = keybind_lookup_keymap_command (viewer_map, key);
428 if ((command != CK_IgnoreKey) && (mcview_execute_cmd (view, command) == MSG_HANDLED))
429 return MSG_HANDLED;
431 #ifdef MC_ENABLE_DEBUGGING_CODE
432 if (c == 't')
433 { /* mnemonic: "test" */
434 mcview_ccache_dump (view);
435 return MSG_HANDLED;
437 #endif
438 if (key >= '0' && key <= '9')
439 view->marker = key - '0';
441 /* Key not used */
442 return MSG_NOT_HANDLED;
446 /* --------------------------------------------------------------------------------------------- */
448 static inline void
449 mcview_adjust_size (Dlg_head * h)
451 mcview_t *view;
452 WButtonBar *b;
454 /* Look up the viewer and the buttonbar, we assume only two widgets here */
455 view = (mcview_t *) find_widget_type (h, mcview_callback);
456 b = find_buttonbar (h);
458 widget_set_size (&view->widget, 0, 0, LINES - 1, COLS);
459 widget_set_size (&b->widget, LINES - 1, 0, 1, COLS);
461 mcview_compute_areas (view);
462 mcview_update_bytes_per_line (view);
466 /* --------------------------------------------------------------------------------------------- */
467 /*** public functions ****************************************************************************/
468 /* --------------------------------------------------------------------------------------------- */
470 cb_ret_t
471 mcview_callback (Widget * w, widget_msg_t msg, int parm)
473 mcview_t *view = (mcview_t *) w;
474 cb_ret_t i;
476 mcview_compute_areas (view);
477 mcview_update_bytes_per_line (view);
479 switch (msg)
481 case WIDGET_INIT:
482 if (mcview_is_in_panel (view))
483 add_hook (&select_file_hook, mcview_hook, view);
484 else
485 view->dpy_bbar_dirty = TRUE;
486 return MSG_HANDLED;
488 case WIDGET_DRAW:
489 mcview_display (view);
490 return MSG_HANDLED;
492 case WIDGET_CURSOR:
493 if (view->hex_mode)
494 mcview_place_cursor (view);
495 return MSG_HANDLED;
497 case WIDGET_KEY:
498 i = mcview_handle_key (view, parm);
499 mcview_update (view);
500 return i;
502 case WIDGET_COMMAND:
503 i = mcview_execute_cmd (view, parm);
504 mcview_update (view);
505 return i;
507 case WIDGET_FOCUS:
508 view->dpy_bbar_dirty = TRUE;
509 mcview_update (view);
510 return MSG_HANDLED;
512 case WIDGET_DESTROY:
513 if (mcview_is_in_panel (view))
515 delete_hook (&select_file_hook, mcview_hook);
517 if (mc_global.midnight_shutdown)
518 mcview_ok_to_quit (view);
520 mcview_done (view);
521 return MSG_HANDLED;
523 default:
524 return default_proc (msg, parm);
528 /* --------------------------------------------------------------------------------------------- */
530 cb_ret_t
531 mcview_dialog_callback (Dlg_head * h, Widget * sender, dlg_msg_t msg, int parm, void *data)
533 mcview_t *view;
535 switch (msg)
537 case DLG_RESIZE:
538 mcview_adjust_size (h);
539 return MSG_HANDLED;
541 case DLG_ACTION:
542 /* shortcut */
543 if (sender == NULL)
544 return mcview_execute_cmd (NULL, parm);
545 /* message from buttonbar */
546 if (sender == (Widget *) find_buttonbar (h))
548 if (data != NULL)
549 return send_message ((Widget *) data, WIDGET_COMMAND, parm);
551 view = (mcview_t *) find_widget_type (h, mcview_callback);
552 return mcview_execute_cmd (view, parm);
554 return MSG_NOT_HANDLED;
556 case DLG_VALIDATE:
557 view = (mcview_t *) find_widget_type (h, mcview_callback);
558 h->state = DLG_ACTIVE; /* don't stop the dialog before final decision */
559 if (mcview_ok_to_quit (view))
560 h->state = DLG_CLOSED;
561 else
562 mcview_update (view);
563 return MSG_HANDLED;
565 default:
566 return default_dlg_callback (h, sender, msg, parm, data);
570 /* --------------------------------------------------------------------------------------------- */