2 Internal file viewer for the Midnight Commander
3 Callback function for some actions (hotkeys, menu)
5 Copyright (C) 1994-2016
6 Free Software Foundation, Inc.
9 Miguel de Icaza, 1994, 1995, 1998
10 Janne Kukonlehto, 1994, 1995
12 Joseph M. Hinkle, 1996
15 Roland Illig <roland.illig@gmx.de>, 2004, 2005
16 Slava Zanko <slavazanko@google.com>, 2009, 2013
17 Andrew Borodin <aborodin@vmail.ru>, 2009, 2013
18 Ilia Maslakov <il.smind@gmail.com>, 2009
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 3 of the License,
25 or (at your option) any later version.
27 The Midnight Commander is distributed in the hope that it will be useful,
28 but WITHOUT ANY WARRANTY; without even the implied warranty of
29 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
30 GNU 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, see <http://www.gnu.org/licenses/>.
37 The functions in this section can be bound to hotkeys. They are all
38 of the same type (taking a pointer to WView as parameter and
39 returning void). TODO: In the not-too-distant future, these commands
40 will become fully configurable, like they already are in the
41 internal editor. By convention, all the function names end in
50 #include "lib/global.h"
52 #include "lib/tty/tty.h"
53 #include "lib/tty/key.h" /* is_idle() */
54 #include "lib/lock.h" /* lock_file() */
56 #include "lib/widget.h"
58 #include "lib/charsets.h"
60 #include "lib/event.h" /* mc_event_raise() */
62 #include "src/filemanager/layout.h"
63 #include "src/filemanager/cmd.h"
64 #include "src/filemanager/midnight.h" /* current_panel */
65 #include "src/filemanager/ext.h" /* regex_command_for() */
67 #include "src/history.h"
68 #include "src/execute.h"
69 #include "src/keybind-defaults.h"
73 /*** global variables ****************************************************************************/
75 /*** file scope macro definitions ****************************************************************/
77 /*** file scope type declarations ****************************************************************/
79 /*** file scope variables ************************************************************************/
81 /* --------------------------------------------------------------------------------------------- */
82 /*** file scope functions ************************************************************************/
83 /* --------------------------------------------------------------------------------------------- */
86 mcview_remove_ext_script (WView
* view
)
88 if (view
->ext_script
!= NULL
)
90 mc_unlink (view
->ext_script
);
91 vfs_path_free (view
->ext_script
);
92 view
->ext_script
= NULL
;
96 /* --------------------------------------------------------------------------------------------- */
100 mcview_search (WView
* view
, gboolean start_search
)
102 off_t want_search_start
= view
->search_start
;
106 if (mcview_dialog_search (view
))
109 want_search_start
= view
->hex_cursor
;
111 mcview_do_search (view
, want_search_start
);
118 if (!mcview_search_options
.backwards
)
119 want_search_start
= view
->hex_cursor
+ 1;
120 else if (view
->hex_cursor
> 0)
121 want_search_start
= view
->hex_cursor
- 1;
123 want_search_start
= 0;
126 mcview_do_search (view
, want_search_start
);
130 /* --------------------------------------------------------------------------------------------- */
133 mcview_continue_search_cmd (WView
* view
)
135 if (view
->last_search_string
!= NULL
)
136 mcview_search (view
, FALSE
);
139 /* find last search string in history */
142 history
= history_get (MC_HISTORY_SHARED_SEARCH
);
143 if (history
!= NULL
&& history
->data
!= NULL
)
145 view
->last_search_string
= (gchar
*) g_strdup (history
->data
);
146 history
= g_list_first (history
);
147 g_list_free_full (history
, g_free
);
150 view
->search
= mc_search_new (view
->last_search_string
, cp_source
);
152 view
->search
= mc_search_new (view
->last_search_string
, NULL
);
154 view
->search_nroff_seq
= mcview_nroff_seq_new (view
);
156 if (view
->search
== NULL
)
158 /* if not... then ask for an expression */
159 MC_PTR_FREE (view
->last_search_string
);
160 mcview_search (view
, TRUE
);
164 view
->search
->search_type
= mcview_search_options
.type
;
166 view
->search
->is_all_charsets
= mcview_search_options
.all_codepages
;
168 view
->search
->is_case_sensitive
= mcview_search_options
.case_sens
;
169 view
->search
->whole_words
= mcview_search_options
.whole_words
;
170 view
->search
->search_fn
= mcview_search_cmd_callback
;
171 view
->search
->update_fn
= mcview_search_update_cmd_callback
;
173 mcview_search (view
, FALSE
);
178 /* if not... then ask for an expression */
179 MC_PTR_FREE (view
->last_search_string
);
180 mcview_search (view
, TRUE
);
185 /* --------------------------------------------------------------------------------------------- */
188 mcview_hook (void *v
)
190 WView
*view
= (WView
*) v
;
193 /* If the user is busy typing, wait until he finishes to update the
197 if (!hook_present (idle_hook
, mcview_hook
))
198 add_hook (&idle_hook
, mcview_hook
, v
);
202 delete_hook (&idle_hook
, mcview_hook
);
204 if (get_current_type () == view_listing
)
205 panel
= current_panel
;
206 else if (get_other_type () == view_listing
)
213 mcview_load (view
, 0, panel
->dir
.list
[panel
->selected
].fname
, 0, 0, 0);
214 mcview_display (view
);
217 /* --------------------------------------------------------------------------------------------- */
220 mcview_handle_editkey (WView
* view
, int key
)
222 struct hexedit_change_node
*node
;
225 /* Has there been a change at this position? */
226 node
= view
->change_list
;
227 while ((node
!= NULL
) && (node
->offset
!= view
->hex_cursor
))
230 if (!view
->hexview_in_text
)
233 unsigned int hexvalue
= 0;
235 if (key
>= '0' && key
<= '9')
236 hexvalue
= 0 + (key
- '0');
237 else if (key
>= 'A' && key
<= 'F')
238 hexvalue
= 10 + (key
- 'A');
239 else if (key
>= 'a' && key
<= 'f')
240 hexvalue
= 10 + (key
- 'a');
242 return MSG_NOT_HANDLED
;
245 byte_val
= node
->value
;
247 mcview_get_byte (view
, view
->hex_cursor
, &byte_val
);
249 if (view
->hexedit_lownibble
)
250 byte_val
= (byte_val
& 0xf0) | (hexvalue
);
252 byte_val
= (byte_val
& 0x0f) | (hexvalue
<< 4);
257 if (key
< 256 && key
!= '\t')
260 return MSG_NOT_HANDLED
;
263 if ((view
->filename_vpath
!= NULL
)
264 && (*(vfs_path_get_last_path_str (view
->filename_vpath
)) != '\0')
265 && (view
->change_list
== NULL
))
266 view
->locked
= lock_file (view
->filename_vpath
);
270 node
= g_new (struct hexedit_change_node
, 1);
271 node
->offset
= view
->hex_cursor
;
272 node
->value
= byte_val
;
273 mcview_enqueue_change (&view
->change_list
, node
);
276 node
->value
= byte_val
;
279 mcview_move_right (view
, 1);
284 /* --------------------------------------------------------------------------------------------- */
287 mcview_load_next_prev_init (WView
* view
)
289 if (mc_global
.mc_run_mode
!= MC_RUN_VIEWER
)
291 /* get file list from current panel. Update it each time */
292 view
->dir
= ¤t_panel
->dir
;
293 view
->dir_idx
= ¤t_panel
->selected
;
295 else if (view
->dir
== NULL
)
297 /* Run from command line */
298 /* Run 1st time. Load/get directory */
300 /* TODO: check mtime of directory to reload it */
305 dir_sort_options_t sort_op
= { FALSE
, TRUE
, FALSE
};
307 /* load directory where requested file is */
308 view
->dir
= g_new0 (dir_list
, 1);
309 view
->dir_idx
= g_new (int, 1);
311 dir_list_load (view
->dir
, view
->workdir_vpath
, (GCompareFunc
) sort_name
, &sort_op
, NULL
);
313 fname
= x_basename (vfs_path_as_str (view
->filename_vpath
));
314 fname_len
= strlen (fname
);
316 /* search current file in the list */
317 for (i
= 0; i
!= view
->dir
->len
; i
++)
319 const file_entry_t
*fe
= &view
->dir
->list
[i
];
321 if (fname_len
== fe
->fnamelen
&& strncmp (fname
, fe
->fname
, fname_len
) == 0)
329 /* --------------------------------------------------------------------------------------------- */
332 mcview_scan_for_file (WView
* view
, int direction
)
336 for (i
= *view
->dir_idx
+ direction
; i
!= *view
->dir_idx
; i
+= direction
)
339 i
= view
->dir
->len
- 1;
340 if (i
== view
->dir
->len
)
342 if (!S_ISDIR (view
->dir
->list
[i
].st
.st_mode
))
349 /* --------------------------------------------------------------------------------------------- */
352 mcview_load_next_prev (WView
* view
, int direction
)
357 vfs_path_t
*ext_script
= NULL
;
359 mcview_load_next_prev_init (view
);
360 mcview_scan_for_file (view
, direction
);
364 dir_idx
= view
->dir_idx
;
366 view
->dir_idx
= NULL
;
367 vfile
= vfs_path_append_new (view
->workdir_vpath
, dir
->list
[*dir_idx
].fname
, (char *) NULL
);
369 mcview_remove_ext_script (view
);
371 if (regex_command_for (view
, vfile
, "View", &ext_script
) == 0)
372 mcview_load (view
, NULL
, vfs_path_as_str (vfile
), 0, 0, 0);
373 vfs_path_free (vfile
);
375 view
->dir_idx
= dir_idx
;
376 view
->ext_script
= ext_script
;
378 view
->dpy_bbar_dirty
= FALSE
; /* FIXME */
382 /* --------------------------------------------------------------------------------------------- */
385 mcview_execute_cmd (WView
* view
, long command
)
387 int res
= MSG_HANDLED
;
393 ev_help_t event_data
= { NULL
, "[Internal File Viewer]" };
394 mc_event_raise (MCEVENT_GROUP_CORE
, "help", &event_data
);
398 /* Toggle between wrapped and unwrapped view */
399 mcview_toggle_wrap_mode (view
);
402 /* Toggle between hexview and hexedit mode */
403 mcview_toggle_hexedit_mode (view
);
406 /* Toggle between hex view and text view */
407 mcview_toggle_hex_mode (view
);
413 if (mcview_dialog_goto (view
, &addr
))
416 mcview_moveto_offset (view
, addr
);
419 message (D_ERROR
, _("Warning"), _("Invalid value"));
426 mcview_hexedit_save_changes (view
);
429 mcview_search (view
, TRUE
);
431 case CK_SearchForward
:
432 mcview_search_options
.backwards
= FALSE
;
433 mcview_search (view
, TRUE
);
435 case CK_SearchBackward
:
436 mcview_search_options
.backwards
= TRUE
;
437 mcview_search (view
, TRUE
);
440 mcview_toggle_magic_mode (view
);
443 mcview_toggle_nroff_mode (view
);
445 case CK_ToggleNavigation
:
446 view
->hexview_in_text
= !view
->hexview_in_text
;
450 mcview_moveto_bol (view
);
453 mcview_moveto_eol (view
);
456 mcview_move_left (view
, 1);
459 mcview_move_right (view
, 1);
463 mcview_move_left (view
, 10);
467 mcview_move_right (view
, 10);
469 case CK_SearchContinue
:
470 mcview_continue_search_cmd (view
);
472 case CK_SearchForwardContinue
:
473 mcview_search_options
.backwards
= FALSE
;
474 mcview_continue_search_cmd (view
);
476 case CK_SearchBackwardContinue
:
477 mcview_search_options
.backwards
= TRUE
;
478 mcview_continue_search_cmd (view
);
481 mcview_display_toggle_ruler (view
);
484 mcview_move_up (view
, 1);
487 mcview_move_down (view
, 1);
490 mcview_move_up (view
, (view
->data_area
.height
+ 1) / 2);
492 case CK_HalfPageDown
:
493 mcview_move_down (view
, (view
->data_area
.height
+ 1) / 2);
496 mcview_move_up (view
, view
->data_area
.height
);
499 mcview_move_down (view
, view
->data_area
.height
);
502 mcview_moveto_top (view
);
505 mcview_moveto_bottom (view
);
510 case CK_BookmarkGoto
:
511 view
->marks
[view
->marker
] = view
->dpy_start
;
514 view
->dpy_start
= view
->marks
[view
->marker
];
515 view
->dpy_paragraph_skip_lines
= 0; /* TODO: remember this value in the marker? */
516 view
->dpy_wrap_dirty
= TRUE
;
520 case CK_SelectCodepage
:
521 mcview_select_encoding (view
);
527 /* Does not work in panel mode */
528 if (!mcview_is_in_panel (view
))
529 mcview_load_next_prev (view
, command
== CK_FileNext
? 1 : -1);
532 if (!mcview_is_in_panel (view
))
533 dlg_stop (WIDGET (view
)->owner
);
536 /* don't close viewer due to SIGINT */
539 res
= MSG_NOT_HANDLED
;
544 /* --------------------------------------------------------------------------------------------- */
547 mcview_handle_key (WView
* view
, int key
)
552 key
= convert_from_input_c (key
);
557 if (view
->hexedit_mode
&& (mcview_handle_editkey (view
, key
) == MSG_HANDLED
))
560 command
= keybind_lookup_keymap_command (viewer_hex_map
, key
);
561 if ((command
!= CK_IgnoreKey
) && (mcview_execute_cmd (view
, command
) == MSG_HANDLED
))
565 command
= keybind_lookup_keymap_command (viewer_map
, key
);
566 if ((command
!= CK_IgnoreKey
) && (mcview_execute_cmd (view
, command
) == MSG_HANDLED
))
569 #ifdef MC_ENABLE_DEBUGGING_CODE
571 { /* mnemonic: "test" */
572 mcview_ccache_dump (view
);
576 if (key
>= '0' && key
<= '9')
577 view
->marker
= key
- '0';
580 return MSG_NOT_HANDLED
;
584 /* --------------------------------------------------------------------------------------------- */
587 mcview_adjust_size (WDialog
* h
)
592 /* Look up the viewer and the buttonbar, we assume only two widgets here */
593 view
= (WView
*) find_widget_type (h
, mcview_callback
);
594 b
= find_buttonbar (h
);
596 widget_set_size (WIDGET (view
), 0, 0, LINES
- 1, COLS
);
597 widget_set_size (WIDGET (b
), LINES
- 1, 0, 1, COLS
);
599 view
->dpy_wrap_dirty
= TRUE
;
600 mcview_compute_areas (view
);
601 mcview_update_bytes_per_line (view
);
604 /* --------------------------------------------------------------------------------------------- */
607 mcview_ok_to_quit (WView
* view
)
611 if (view
->change_list
== NULL
)
614 if (!mc_global
.midnight_shutdown
)
617 r
= query_dialog (_("Quit"),
618 _("File was modified. Save with exit?"), D_NORMAL
, 3,
619 _("&Yes"), _("&No"), _("&Cancel quit"));
623 r
= query_dialog (_("Quit"),
624 _("Midnight Commander is being shut down.\nSave modified file?"),
625 D_NORMAL
, 2, _("&Yes"), _("&No"));
634 return mcview_hexedit_save_changes (view
) || mc_global
.midnight_shutdown
;
636 mcview_hexedit_free_change_list (view
);
643 /* --------------------------------------------------------------------------------------------- */
644 /*** public functions ****************************************************************************/
645 /* --------------------------------------------------------------------------------------------- */
648 mcview_callback (Widget
* w
, Widget
* sender
, widget_msg_t msg
, int parm
, void *data
)
650 WView
*view
= (WView
*) w
;
653 mcview_compute_areas (view
);
654 mcview_update_bytes_per_line (view
);
659 if (mcview_is_in_panel (view
))
660 add_hook (&select_file_hook
, mcview_hook
, view
);
662 view
->dpy_bbar_dirty
= TRUE
;
666 mcview_display (view
);
671 mcview_place_cursor (view
);
675 i
= mcview_handle_key (view
, parm
);
676 mcview_update (view
);
677 /* don't pass any chars to command line in QuickView mode */
678 return mcview_is_in_panel (view
) ? MSG_HANDLED
: i
;
681 i
= mcview_execute_cmd (view
, parm
);
682 mcview_update (view
);
686 view
->dpy_bbar_dirty
= TRUE
;
687 /* TODO: get rid of draw here before MSG_DRAW */
688 mcview_update (view
);
692 if (mcview_is_in_panel (view
))
694 delete_hook (&select_file_hook
, mcview_hook
);
696 if (mc_global
.midnight_shutdown
)
697 mcview_ok_to_quit (view
);
700 mcview_remove_ext_script (view
);
704 return widget_default_callback (w
, sender
, msg
, parm
, data
);
708 /* --------------------------------------------------------------------------------------------- */
711 mcview_dialog_callback (Widget
* w
, Widget
* sender
, widget_msg_t msg
, int parm
, void *data
)
713 WDialog
*h
= DIALOG (w
);
719 mcview_adjust_size (h
);
723 /* Handle shortcuts. */
725 /* Note: the buttonbar sends messages directly to the the WView, not to
726 * here, which is why we can pass NULL in the following call. */
727 return mcview_execute_cmd (NULL
, parm
);
730 view
= (WView
*) find_widget_type (h
, mcview_callback
);
731 /* don't stop the dialog before final decision */
732 widget_set_state (WIDGET (h
), WST_ACTIVE
, TRUE
);
733 if (mcview_ok_to_quit (view
))
736 mcview_update (view
);
740 return dlg_default_callback (w
, sender
, msg
, parm
, data
);
744 /* --------------------------------------------------------------------------------------------- */