Add edit_add_window() function.
[midnight-commander.git] / src / viewer / actions_cmd.c
blob5401a9c1830d63b42b8125f76a6092b415fcfab0
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 #include "lib/charsets.h"
59 #include "lib/event.h" /* mc_event_raise() */
61 #include "src/filemanager/layout.h"
62 #include "src/filemanager/cmd.h"
63 #include "src/filemanager/midnight.h" /* current_panel */
65 #include "src/history.h"
66 #include "src/execute.h"
67 #include "src/keybind-defaults.h"
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 ************************************************************************/
81 /* --------------------------------------------------------------------------------------------- */
83 /* Both views */
84 static void
85 mcview_search (mcview_t * view)
87 if (mcview_dialog_search (view))
88 mcview_do_search (view);
91 /* --------------------------------------------------------------------------------------------- */
93 static void
94 mcview_continue_search_cmd (mcview_t * view)
96 if (view->last_search_string != NULL)
98 mcview_do_search (view);
100 else
102 /* find last search string in history */
103 GList *history;
104 history = history_get (MC_HISTORY_SHARED_SEARCH);
105 if (history != NULL && history->data != NULL)
107 view->last_search_string = (gchar *) g_strdup (history->data);
108 history = g_list_first (history);
109 g_list_foreach (history, (GFunc) g_free, NULL);
110 g_list_free (history);
112 view->search = mc_search_new (view->last_search_string, -1);
113 view->search_nroff_seq = mcview_nroff_seq_new (view);
115 if (!view->search)
117 /* if not... then ask for an expression */
118 g_free (view->last_search_string);
119 view->last_search_string = NULL;
120 mcview_search (view);
122 else
124 view->search->search_type = mcview_search_options.type;
125 view->search->is_all_charsets = mcview_search_options.all_codepages;
126 view->search->is_case_sensitive = mcview_search_options.case_sens;
127 view->search->whole_words = mcview_search_options.whole_words;
128 view->search->search_fn = mcview_search_cmd_callback;
129 view->search->update_fn = mcview_search_update_cmd_callback;
131 mcview_do_search (view);
134 else
136 /* if not... then ask for an expression */
137 g_free (view->last_search_string);
138 view->last_search_string = NULL;
139 mcview_search (view);
144 /* --------------------------------------------------------------------------------------------- */
146 static void
147 mcview_hook (void *v)
149 mcview_t *view = (mcview_t *) v;
150 WPanel *panel;
152 /* If the user is busy typing, wait until he finishes to update the
153 screen */
154 if (!is_idle ())
156 if (!hook_present (idle_hook, mcview_hook))
157 add_hook (&idle_hook, mcview_hook, v);
158 return;
161 delete_hook (&idle_hook, mcview_hook);
163 if (get_current_type () == view_listing)
164 panel = current_panel;
165 else if (get_other_type () == view_listing)
166 panel = other_panel;
167 else
168 return;
170 mcview_done (view);
171 mcview_init (view);
172 mcview_load (view, 0, panel->dir.list[panel->selected].fname, 0);
173 mcview_display (view);
176 /* --------------------------------------------------------------------------------------------- */
178 static cb_ret_t
179 mcview_handle_editkey (mcview_t * view, int key)
181 struct hexedit_change_node *node;
182 int byte_val;
184 /* Has there been a change at this position? */
185 node = view->change_list;
186 while ((node != NULL) && (node->offset != view->hex_cursor))
187 node = node->next;
189 if (!view->hexview_in_text)
191 /* Hex editing */
192 unsigned int hexvalue = 0;
194 if (key >= '0' && key <= '9')
195 hexvalue = 0 + (key - '0');
196 else if (key >= 'A' && key <= 'F')
197 hexvalue = 10 + (key - 'A');
198 else if (key >= 'a' && key <= 'f')
199 hexvalue = 10 + (key - 'a');
200 else
201 return MSG_NOT_HANDLED;
203 if (node != NULL)
204 byte_val = node->value;
205 else
206 mcview_get_byte (view, view->hex_cursor, &byte_val);
208 if (view->hexedit_lownibble)
209 byte_val = (byte_val & 0xf0) | (hexvalue);
210 else
211 byte_val = (byte_val & 0x0f) | (hexvalue << 4);
213 else
215 /* Text editing */
216 if (key < 256 && ((key == '\n') || is_printable (key)))
217 byte_val = key;
218 else
219 return MSG_NOT_HANDLED;
222 if ((view->filename_vpath != NULL)
223 && (*(vfs_path_get_last_path_str (view->filename_vpath)) != '\0')
224 && (view->change_list == NULL))
225 view->locked = lock_file (view->filename_vpath);
227 if (node == NULL)
229 node = g_new (struct hexedit_change_node, 1);
230 node->offset = view->hex_cursor;
231 node->value = byte_val;
232 mcview_enqueue_change (&view->change_list, node);
234 else
235 node->value = byte_val;
237 view->dirty++;
238 mcview_move_right (view, 1);
240 return MSG_HANDLED;
243 /* --------------------------------------------------------------------------------------------- */
245 static void
246 mcview_load_next_prev_init (mcview_t * view)
248 if (mc_global.mc_run_mode != MC_RUN_VIEWER)
250 /* get file list from current panel. Update it each time */
251 view->dir = &current_panel->dir;
252 view->dir_count = &current_panel->count;
253 view->dir_idx = &current_panel->selected;
255 else if (view->dir == NULL)
257 /* Run from command line */
258 /* Run 1st time. Load/get directory */
260 /* TODO: check mtime of directory to reload it */
262 char *full_fname;
263 const char *fname;
264 size_t fname_len;
265 int i;
267 /* load directory where requested file is */
268 view->dir = g_new0 (dir_list, 1);
269 view->dir_count = g_new (int, 1);
270 view->dir_idx = g_new (int, 1);
272 *view->dir_count = do_load_dir (view->workdir_vpath, view->dir, (sortfn *) sort_name, FALSE,
273 TRUE, FALSE, NULL);
275 full_fname = vfs_path_to_str (view->filename_vpath);
276 fname = x_basename (full_fname);
277 fname_len = strlen (fname);
279 /* search current file in the list */
280 for (i = 0; i != *view->dir_count; i++)
282 const file_entry *fe = &view->dir->list[i];
284 if (fname_len == fe->fnamelen && strncmp (fname, fe->fname, fname_len) == 0)
285 break;
288 g_free (full_fname);
290 *view->dir_idx = i;
294 /* --------------------------------------------------------------------------------------------- */
296 static void
297 mcview_scan_for_file (mcview_t * view, int direction)
299 int i;
301 for (i = *view->dir_idx + direction; i != *view->dir_idx; i += direction)
303 if (i < 0)
304 i = *view->dir_count - 1;
305 if (i == *view->dir_count)
306 i = 0;
307 if (!S_ISDIR (view->dir->list[i].st.st_mode))
308 break;
311 *view->dir_idx = i;
314 /* --------------------------------------------------------------------------------------------- */
316 static void
317 mcview_load_next_prev (mcview_t * view, int direction)
319 dir_list *dir;
320 int *dir_count, *dir_idx;
321 vfs_path_t *vfile;
322 char *file;
324 mcview_load_next_prev_init (view);
325 mcview_scan_for_file (view, direction);
327 /* reinit view */
328 dir = view->dir;
329 dir_count = view->dir_count;
330 dir_idx = view->dir_idx;
331 view->dir = NULL;
332 view->dir_count = NULL;
333 view->dir_idx = NULL;
334 vfile = vfs_path_append_new (view->workdir_vpath, dir->list[*dir_idx].fname, (char *) NULL);
335 file = vfs_path_to_str (vfile);
336 vfs_path_free (vfile);
337 mcview_done (view);
338 mcview_init (view);
339 mcview_load (view, NULL, file, 0);
340 g_free (file);
341 view->dir = dir;
342 view->dir_count = dir_count;
343 view->dir_idx = dir_idx;
345 view->dpy_bbar_dirty = FALSE; /* FIXME */
346 view->dirty++;
349 /* --------------------------------------------------------------------------------------------- */
351 static cb_ret_t
352 mcview_execute_cmd (mcview_t * view, unsigned long command)
354 int res = MSG_HANDLED;
356 switch (command)
358 case CK_Help:
360 ev_help_t event_data = { NULL, "[Internal File Viewer]" };
361 mc_event_raise (MCEVENT_GROUP_CORE, "help", &event_data);
363 break;
364 case CK_WrapMode:
365 /* Toggle between wrapped and unwrapped view */
366 mcview_toggle_wrap_mode (view);
367 break;
368 case CK_HexEditMode:
369 /* Toggle between hexview and hexedit mode */
370 mcview_toggle_hexedit_mode (view);
371 break;
372 case CK_HexMode:
373 /* Toggle between hex view and text view */
374 mcview_toggle_hex_mode (view);
375 break;
376 case CK_Goto:
378 off_t addr;
380 if (mcview_dialog_goto (view, &addr))
382 if (addr >= 0)
383 mcview_moveto_offset (view, addr);
384 else
386 message (D_ERROR, _("Warning"), _("Invalid value"));
387 view->dirty++;
390 break;
392 case CK_Save:
393 mcview_hexedit_save_changes (view);
394 break;
395 case CK_Search:
396 mcview_search (view);
397 break;
398 case CK_SearchForward:
399 mcview_search_options.backwards = FALSE;
400 mcview_search (view);
401 break;
402 case CK_SearchBackward:
403 mcview_search_options.backwards = TRUE;
404 mcview_search (view);
405 break;
406 case CK_MagicMode:
407 mcview_toggle_magic_mode (view);
408 break;
409 case CK_NroffMode:
410 mcview_toggle_nroff_mode (view);
411 break;
412 case CK_ToggleNavigation:
413 view->hexview_in_text = !view->hexview_in_text;
414 view->dirty++;
415 break;
416 case CK_Home:
417 mcview_moveto_bol (view);
418 break;
419 case CK_End:
420 mcview_moveto_eol (view);
421 break;
422 case CK_Left:
423 mcview_move_left (view, 1);
424 break;
425 case CK_Right:
426 mcview_move_right (view, 1);
427 break;
428 case CK_LeftQuick:
429 if (!view->hex_mode)
430 mcview_move_left (view, 10);
431 break;
432 case CK_RightQuick:
433 if (!view->hex_mode)
434 mcview_move_right (view, 10);
435 break;
436 case CK_SearchContinue:
437 mcview_continue_search_cmd (view);
438 break;
439 case CK_SearchForwardContinue:
440 mcview_search_options.backwards = FALSE;
441 mcview_continue_search_cmd (view);
442 break;
443 case CK_SearchBackwardContinue:
444 mcview_search_options.backwards = TRUE;
445 mcview_continue_search_cmd (view);
446 break;
447 case CK_Ruler:
448 mcview_display_toggle_ruler (view);
449 break;
450 case CK_Up:
451 mcview_move_up (view, 1);
452 break;
453 case CK_Down:
454 mcview_move_down (view, 1);
455 break;
456 case CK_HalfPageUp:
457 mcview_move_up (view, (view->data_area.height + 1) / 2);
458 break;
459 case CK_HalfPageDown:
460 mcview_move_down (view, (view->data_area.height + 1) / 2);
461 break;
462 case CK_PageUp:
463 mcview_move_up (view, view->data_area.height);
464 break;
465 case CK_PageDown:
466 mcview_move_down (view, view->data_area.height);
467 break;
468 case CK_Top:
469 mcview_moveto_top (view);
470 break;
471 case CK_Bottom:
472 mcview_moveto_bottom (view);
473 break;
474 case CK_Shell:
475 view_other_cmd ();
476 break;
477 case CK_BookmarkGoto:
478 view->marks[view->marker] = view->dpy_start;
479 break;
480 case CK_Bookmark:
481 view->dpy_start = view->marks[view->marker];
482 view->dirty++;
483 break;
484 #ifdef HAVE_CHARSET
485 case CK_SelectCodepage:
486 mcview_select_encoding (view);
487 view->dirty++;
488 break;
489 #endif
490 case CK_FileNext:
491 case CK_FilePrev:
492 /* Does not work in panel mode */
493 if (!mcview_is_in_panel (view))
494 mcview_load_next_prev (view, command == CK_FileNext ? 1 : -1);
495 break;
496 case CK_Quit:
497 if (!mcview_is_in_panel (view))
498 dlg_stop (view->widget.owner);
499 break;
500 case CK_Cancel:
501 /* don't close viewer due to SIGINT */
502 break;
503 default:
504 res = MSG_NOT_HANDLED;
506 return res;
509 /* --------------------------------------------------------------------------------------------- */
510 /** Both views */
511 static cb_ret_t
512 mcview_handle_key (mcview_t * view, int key)
514 unsigned long command;
516 key = convert_from_input_c (key);
518 if (view->hex_mode)
520 if (view->hexedit_mode && (mcview_handle_editkey (view, key) == MSG_HANDLED))
521 return MSG_HANDLED;
523 command = keybind_lookup_keymap_command (viewer_hex_map, key);
524 if ((command != CK_IgnoreKey) && (mcview_execute_cmd (view, command) == MSG_HANDLED))
525 return MSG_HANDLED;
528 command = keybind_lookup_keymap_command (viewer_map, key);
529 if ((command != CK_IgnoreKey) && (mcview_execute_cmd (view, command) == MSG_HANDLED))
530 return MSG_HANDLED;
532 #ifdef MC_ENABLE_DEBUGGING_CODE
533 if (c == 't')
534 { /* mnemonic: "test" */
535 mcview_ccache_dump (view);
536 return MSG_HANDLED;
538 #endif
539 if (key >= '0' && key <= '9')
540 view->marker = key - '0';
542 /* Key not used */
543 return MSG_NOT_HANDLED;
547 /* --------------------------------------------------------------------------------------------- */
549 static inline void
550 mcview_adjust_size (Dlg_head * h)
552 mcview_t *view;
553 WButtonBar *b;
555 /* Look up the viewer and the buttonbar, we assume only two widgets here */
556 view = (mcview_t *) find_widget_type (h, mcview_callback);
557 b = find_buttonbar (h);
559 widget_set_size (&view->widget, 0, 0, LINES - 1, COLS);
560 widget_set_size (&b->widget, LINES - 1, 0, 1, COLS);
562 mcview_compute_areas (view);
563 mcview_update_bytes_per_line (view);
567 /* --------------------------------------------------------------------------------------------- */
568 /*** public functions ****************************************************************************/
569 /* --------------------------------------------------------------------------------------------- */
571 cb_ret_t
572 mcview_callback (Widget * w, widget_msg_t msg, int parm)
574 mcview_t *view = (mcview_t *) w;
575 cb_ret_t i;
577 mcview_compute_areas (view);
578 mcview_update_bytes_per_line (view);
580 switch (msg)
582 case WIDGET_INIT:
583 if (mcview_is_in_panel (view))
584 add_hook (&select_file_hook, mcview_hook, view);
585 else
586 view->dpy_bbar_dirty = TRUE;
587 return MSG_HANDLED;
589 case WIDGET_DRAW:
590 mcview_display (view);
591 return MSG_HANDLED;
593 case WIDGET_CURSOR:
594 if (view->hex_mode)
595 mcview_place_cursor (view);
596 return MSG_HANDLED;
598 case WIDGET_KEY:
599 i = mcview_handle_key (view, parm);
600 mcview_update (view);
601 return i;
603 case WIDGET_COMMAND:
604 i = mcview_execute_cmd (view, parm);
605 mcview_update (view);
606 return i;
608 case WIDGET_FOCUS:
609 view->dpy_bbar_dirty = TRUE;
610 mcview_update (view);
611 return MSG_HANDLED;
613 case WIDGET_DESTROY:
614 if (mcview_is_in_panel (view))
616 delete_hook (&select_file_hook, mcview_hook);
618 if (mc_global.midnight_shutdown)
619 mcview_ok_to_quit (view);
621 mcview_done (view);
622 return MSG_HANDLED;
624 default:
625 return default_proc (msg, parm);
629 /* --------------------------------------------------------------------------------------------- */
631 cb_ret_t
632 mcview_dialog_callback (Dlg_head * h, Widget * sender, dlg_msg_t msg, int parm, void *data)
634 mcview_t *view;
636 switch (msg)
638 case DLG_RESIZE:
639 mcview_adjust_size (h);
640 return MSG_HANDLED;
642 case DLG_ACTION:
643 /* shortcut */
644 if (sender == NULL)
645 return mcview_execute_cmd (NULL, parm);
646 /* message from buttonbar */
647 if (sender == (Widget *) find_buttonbar (h))
649 if (data != NULL)
650 return send_message ((Widget *) data, WIDGET_COMMAND, parm);
652 view = (mcview_t *) find_widget_type (h, mcview_callback);
653 return mcview_execute_cmd (view, parm);
655 return MSG_NOT_HANDLED;
657 case DLG_VALIDATE:
658 view = (mcview_t *) find_widget_type (h, mcview_callback);
659 h->state = DLG_ACTIVE; /* don't stop the dialog before final decision */
660 if (mcview_ok_to_quit (view))
661 h->state = DLG_CLOSED;
662 else
663 mcview_update (view);
664 return MSG_HANDLED;
666 default:
667 return default_dlg_callback (h, sender, msg, parm, data);
671 /* --------------------------------------------------------------------------------------------- */