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, 2013
7 The Free Software Foundation, Inc.
10 Miguel de Icaza, 1994, 1995, 1998
11 Janne Kukonlehto, 1994, 1995
13 Joseph M. Hinkle, 1996
16 Roland Illig <roland.illig@gmx.de>, 2004, 2005
17 Slava Zanko <slavazanko@google.com>, 2009
18 Andrew Borodin <aborodin@vmail.ru>, 2009, 2013
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
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() */
57 #include "lib/widget.h"
59 #include "lib/charsets.h"
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 */
66 #include "src/filemanager/ext.h" /* regex_command_for() */
68 #include "src/history.h"
69 #include "src/execute.h"
70 #include "src/keybind-defaults.h"
74 /*** global variables ****************************************************************************/
76 /*** file scope macro definitions ****************************************************************/
78 /*** file scope type declarations ****************************************************************/
80 /*** file scope variables ************************************************************************/
82 /* --------------------------------------------------------------------------------------------- */
83 /*** file scope functions ************************************************************************/
84 /* --------------------------------------------------------------------------------------------- */
87 mcview_remove_ext_script (mcview_t
* view
)
89 if (view
->ext_script
!= NULL
)
91 mc_unlink (view
->ext_script
);
92 vfs_path_free (view
->ext_script
);
93 view
->ext_script
= NULL
;
97 /* --------------------------------------------------------------------------------------------- */
101 mcview_search (mcview_t
* view
)
103 if (mcview_dialog_search (view
))
104 mcview_do_search (view
);
107 /* --------------------------------------------------------------------------------------------- */
110 mcview_continue_search_cmd (mcview_t
* view
)
112 if (view
->last_search_string
!= NULL
)
114 mcview_do_search (view
);
118 /* find last search string in history */
120 history
= history_get (MC_HISTORY_SHARED_SEARCH
);
121 if (history
!= NULL
&& history
->data
!= NULL
)
123 view
->last_search_string
= (gchar
*) g_strdup (history
->data
);
124 history
= g_list_first (history
);
125 g_list_foreach (history
, (GFunc
) g_free
, NULL
);
126 g_list_free (history
);
128 view
->search
= mc_search_new (view
->last_search_string
, -1);
129 view
->search_nroff_seq
= mcview_nroff_seq_new (view
);
133 /* if not... then ask for an expression */
134 g_free (view
->last_search_string
);
135 view
->last_search_string
= NULL
;
136 mcview_search (view
);
140 view
->search
->search_type
= mcview_search_options
.type
;
141 view
->search
->is_all_charsets
= mcview_search_options
.all_codepages
;
142 view
->search
->is_case_sensitive
= mcview_search_options
.case_sens
;
143 view
->search
->whole_words
= mcview_search_options
.whole_words
;
144 view
->search
->search_fn
= mcview_search_cmd_callback
;
145 view
->search
->update_fn
= mcview_search_update_cmd_callback
;
147 mcview_do_search (view
);
152 /* if not... then ask for an expression */
153 g_free (view
->last_search_string
);
154 view
->last_search_string
= NULL
;
155 mcview_search (view
);
160 /* --------------------------------------------------------------------------------------------- */
163 mcview_hook (void *v
)
165 mcview_t
*view
= (mcview_t
*) v
;
168 /* If the user is busy typing, wait until he finishes to update the
172 if (!hook_present (idle_hook
, mcview_hook
))
173 add_hook (&idle_hook
, mcview_hook
, v
);
177 delete_hook (&idle_hook
, mcview_hook
);
179 if (get_current_type () == view_listing
)
180 panel
= current_panel
;
181 else if (get_other_type () == view_listing
)
188 mcview_load (view
, 0, panel
->dir
.list
[panel
->selected
].fname
, 0);
189 mcview_display (view
);
192 /* --------------------------------------------------------------------------------------------- */
195 mcview_handle_editkey (mcview_t
* view
, int key
)
197 struct hexedit_change_node
*node
;
200 /* Has there been a change at this position? */
201 node
= view
->change_list
;
202 while ((node
!= NULL
) && (node
->offset
!= view
->hex_cursor
))
205 if (!view
->hexview_in_text
)
208 unsigned int hexvalue
= 0;
210 if (key
>= '0' && key
<= '9')
211 hexvalue
= 0 + (key
- '0');
212 else if (key
>= 'A' && key
<= 'F')
213 hexvalue
= 10 + (key
- 'A');
214 else if (key
>= 'a' && key
<= 'f')
215 hexvalue
= 10 + (key
- 'a');
217 return MSG_NOT_HANDLED
;
220 byte_val
= node
->value
;
222 mcview_get_byte (view
, view
->hex_cursor
, &byte_val
);
224 if (view
->hexedit_lownibble
)
225 byte_val
= (byte_val
& 0xf0) | (hexvalue
);
227 byte_val
= (byte_val
& 0x0f) | (hexvalue
<< 4);
232 if (key
< 256 && ((key
== '\n') || is_printable (key
)))
235 return MSG_NOT_HANDLED
;
238 if ((view
->filename_vpath
!= NULL
)
239 && (*(vfs_path_get_last_path_str (view
->filename_vpath
)) != '\0')
240 && (view
->change_list
== NULL
))
241 view
->locked
= lock_file (view
->filename_vpath
);
245 node
= g_new (struct hexedit_change_node
, 1);
246 node
->offset
= view
->hex_cursor
;
247 node
->value
= byte_val
;
248 mcview_enqueue_change (&view
->change_list
, node
);
251 node
->value
= byte_val
;
254 mcview_move_right (view
, 1);
259 /* --------------------------------------------------------------------------------------------- */
262 mcview_load_next_prev_init (mcview_t
* view
)
264 if (mc_global
.mc_run_mode
!= MC_RUN_VIEWER
)
266 /* get file list from current panel. Update it each time */
267 view
->dir
= ¤t_panel
->dir
;
268 view
->dir_count
= ¤t_panel
->count
;
269 view
->dir_idx
= ¤t_panel
->selected
;
271 else if (view
->dir
== NULL
)
273 /* Run from command line */
274 /* Run 1st time. Load/get directory */
276 /* TODO: check mtime of directory to reload it */
283 /* load directory where requested file is */
284 view
->dir
= g_new0 (dir_list
, 1);
285 view
->dir_count
= g_new (int, 1);
286 view
->dir_idx
= g_new (int, 1);
288 *view
->dir_count
= do_load_dir (view
->workdir_vpath
, view
->dir
, (sortfn
*) sort_name
, FALSE
,
291 full_fname
= vfs_path_to_str (view
->filename_vpath
);
292 fname
= x_basename (full_fname
);
293 fname_len
= strlen (fname
);
295 /* search current file in the list */
296 for (i
= 0; i
!= *view
->dir_count
; i
++)
298 const file_entry
*fe
= &view
->dir
->list
[i
];
300 if (fname_len
== fe
->fnamelen
&& strncmp (fname
, fe
->fname
, fname_len
) == 0)
310 /* --------------------------------------------------------------------------------------------- */
313 mcview_scan_for_file (mcview_t
* view
, int direction
)
317 for (i
= *view
->dir_idx
+ direction
; i
!= *view
->dir_idx
; i
+= direction
)
320 i
= *view
->dir_count
- 1;
321 if (i
== *view
->dir_count
)
323 if (!S_ISDIR (view
->dir
->list
[i
].st
.st_mode
))
330 /* --------------------------------------------------------------------------------------------- */
333 mcview_load_next_prev (mcview_t
* view
, int direction
)
336 int *dir_count
, *dir_idx
;
338 vfs_path_t
*ext_script
= NULL
;
340 mcview_load_next_prev_init (view
);
341 mcview_scan_for_file (view
, direction
);
345 dir_count
= view
->dir_count
;
346 dir_idx
= view
->dir_idx
;
348 view
->dir_count
= NULL
;
349 view
->dir_idx
= NULL
;
350 vfile
= vfs_path_append_new (view
->workdir_vpath
, dir
->list
[*dir_idx
].fname
, (char *) NULL
);
352 mcview_remove_ext_script (view
);
354 if (regex_command_for (view
, vfile
, "View", &ext_script
) == 0)
358 file
= vfs_path_to_str (vfile
);
359 mcview_load (view
, NULL
, file
, 0);
362 vfs_path_free (vfile
);
364 view
->dir_count
= dir_count
;
365 view
->dir_idx
= dir_idx
;
366 view
->ext_script
= ext_script
;
368 view
->dpy_bbar_dirty
= FALSE
; /* FIXME */
372 /* --------------------------------------------------------------------------------------------- */
375 mcview_execute_cmd (mcview_t
* view
, unsigned long command
)
377 int res
= MSG_HANDLED
;
383 ev_help_t event_data
= { NULL
, "[Internal File Viewer]" };
384 mc_event_raise (MCEVENT_GROUP_CORE
, "help", &event_data
);
388 /* Toggle between wrapped and unwrapped view */
389 mcview_toggle_wrap_mode (view
);
392 /* Toggle between hexview and hexedit mode */
393 mcview_toggle_hexedit_mode (view
);
396 /* Toggle between hex view and text view */
397 mcview_toggle_hex_mode (view
);
403 if (mcview_dialog_goto (view
, &addr
))
406 mcview_moveto_offset (view
, addr
);
409 message (D_ERROR
, _("Warning"), _("Invalid value"));
416 mcview_hexedit_save_changes (view
);
419 mcview_search (view
);
421 case CK_SearchForward
:
422 mcview_search_options
.backwards
= FALSE
;
423 mcview_search (view
);
425 case CK_SearchBackward
:
426 mcview_search_options
.backwards
= TRUE
;
427 mcview_search (view
);
430 mcview_toggle_magic_mode (view
);
433 mcview_toggle_nroff_mode (view
);
435 case CK_ToggleNavigation
:
436 view
->hexview_in_text
= !view
->hexview_in_text
;
440 mcview_moveto_bol (view
);
443 mcview_moveto_eol (view
);
446 mcview_move_left (view
, 1);
449 mcview_move_right (view
, 1);
453 mcview_move_left (view
, 10);
457 mcview_move_right (view
, 10);
459 case CK_SearchContinue
:
460 mcview_continue_search_cmd (view
);
462 case CK_SearchForwardContinue
:
463 mcview_search_options
.backwards
= FALSE
;
464 mcview_continue_search_cmd (view
);
466 case CK_SearchBackwardContinue
:
467 mcview_search_options
.backwards
= TRUE
;
468 mcview_continue_search_cmd (view
);
471 mcview_display_toggle_ruler (view
);
474 mcview_move_up (view
, 1);
477 mcview_move_down (view
, 1);
480 mcview_move_up (view
, (view
->data_area
.height
+ 1) / 2);
482 case CK_HalfPageDown
:
483 mcview_move_down (view
, (view
->data_area
.height
+ 1) / 2);
486 mcview_move_up (view
, view
->data_area
.height
);
489 mcview_move_down (view
, view
->data_area
.height
);
492 mcview_moveto_top (view
);
495 mcview_moveto_bottom (view
);
500 case CK_BookmarkGoto
:
501 view
->marks
[view
->marker
] = view
->dpy_start
;
504 view
->dpy_start
= view
->marks
[view
->marker
];
508 case CK_SelectCodepage
:
509 mcview_select_encoding (view
);
515 /* Does not work in panel mode */
516 if (!mcview_is_in_panel (view
))
517 mcview_load_next_prev (view
, command
== CK_FileNext
? 1 : -1);
520 if (!mcview_is_in_panel (view
))
521 dlg_stop (WIDGET (view
)->owner
);
524 /* don't close viewer due to SIGINT */
527 res
= MSG_NOT_HANDLED
;
532 /* --------------------------------------------------------------------------------------------- */
535 mcview_handle_key (mcview_t
* view
, int key
)
537 unsigned long command
;
540 key
= convert_from_input_c (key
);
545 if (view
->hexedit_mode
&& (mcview_handle_editkey (view
, key
) == MSG_HANDLED
))
548 command
= keybind_lookup_keymap_command (viewer_hex_map
, key
);
549 if ((command
!= CK_IgnoreKey
) && (mcview_execute_cmd (view
, command
) == MSG_HANDLED
))
553 command
= keybind_lookup_keymap_command (viewer_map
, key
);
554 if ((command
!= CK_IgnoreKey
) && (mcview_execute_cmd (view
, command
) == MSG_HANDLED
))
557 #ifdef MC_ENABLE_DEBUGGING_CODE
559 { /* mnemonic: "test" */
560 mcview_ccache_dump (view
);
564 if (key
>= '0' && key
<= '9')
565 view
->marker
= key
- '0';
568 return MSG_NOT_HANDLED
;
572 /* --------------------------------------------------------------------------------------------- */
575 mcview_adjust_size (WDialog
* h
)
580 /* Look up the viewer and the buttonbar, we assume only two widgets here */
581 view
= (mcview_t
*) find_widget_type (h
, mcview_callback
);
582 b
= find_buttonbar (h
);
584 widget_set_size (WIDGET (view
), 0, 0, LINES
- 1, COLS
);
585 widget_set_size (WIDGET (b
), LINES
- 1, 0, 1, COLS
);
587 mcview_compute_areas (view
);
588 mcview_update_bytes_per_line (view
);
592 /* --------------------------------------------------------------------------------------------- */
593 /*** public functions ****************************************************************************/
594 /* --------------------------------------------------------------------------------------------- */
597 mcview_callback (Widget
* w
, Widget
* sender
, widget_msg_t msg
, int parm
, void *data
)
599 mcview_t
*view
= (mcview_t
*) w
;
602 mcview_compute_areas (view
);
603 mcview_update_bytes_per_line (view
);
608 if (mcview_is_in_panel (view
))
609 add_hook (&select_file_hook
, mcview_hook
, view
);
611 view
->dpy_bbar_dirty
= TRUE
;
615 mcview_display (view
);
620 mcview_place_cursor (view
);
624 i
= mcview_handle_key (view
, parm
);
625 mcview_update (view
);
629 i
= mcview_execute_cmd (view
, parm
);
630 mcview_update (view
);
634 view
->dpy_bbar_dirty
= TRUE
;
635 mcview_update (view
);
639 if (mcview_is_in_panel (view
))
641 delete_hook (&select_file_hook
, mcview_hook
);
643 if (mc_global
.midnight_shutdown
)
644 mcview_ok_to_quit (view
);
647 mcview_remove_ext_script (view
);
651 return widget_default_callback (w
, sender
, msg
, parm
, data
);
655 /* --------------------------------------------------------------------------------------------- */
658 mcview_dialog_callback (Widget
* w
, Widget
* sender
, widget_msg_t msg
, int parm
, void *data
)
660 WDialog
*h
= DIALOG (w
);
666 mcview_adjust_size (h
);
672 return mcview_execute_cmd (NULL
, parm
);
673 /* message from buttonbar */
674 if (sender
== WIDGET (find_buttonbar (h
)))
677 return send_message (data
, NULL
, MSG_ACTION
, parm
, NULL
);
679 view
= (mcview_t
*) find_widget_type (h
, mcview_callback
);
680 return mcview_execute_cmd (view
, parm
);
682 return MSG_NOT_HANDLED
;
685 view
= (mcview_t
*) find_widget_type (h
, mcview_callback
);
686 h
->state
= DLG_ACTIVE
; /* don't stop the dialog before final decision */
687 if (mcview_ok_to_quit (view
))
688 h
->state
= DLG_CLOSED
;
690 mcview_update (view
);
694 return dlg_default_callback (w
, sender
, msg
, parm
, data
);
698 /* --------------------------------------------------------------------------------------------- */