2 Internal file viewer for the Midnight Commander
4 Copyright (C) 1994, 1995, 1996 The Free Software Foundation
6 Written by: 1994, 1995, 1998 Miguel de Icaza
7 1994, 1995 Janne Kukonlehto
12 2004 Roland Illig <roland.illig@gmx.de>
13 2005 Roland Illig <roland.illig@gmx.de>
15 This program is free software; you can redistribute it and/or modify
16 it under the terms of the GNU General Public License as published by
17 the Free Software Foundation; either version 2 of the License, or
18 (at your option) any later version.
20 This program is distributed in the hope that it will be useful,
21 but WITHOUT ANY WARRANTY; without even the implied warranty of
22 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 GNU General Public License for more details.
25 You should have received a copy of the GNU General Public License
26 along with this program; if not, write to the Free Software
27 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
31 * \brief Source: internal file viewer
46 #include <sys/types.h>
52 #include "cmd.h" /* For view_other_cmd */
53 #include "dialog.h" /* Needed by widget.h */
54 #include "widget.h" /* Needed for buttonbar_new */
58 #include "key.h" /* For mi_getch() */
61 #include "wtools.h" /* For query_set_sel() */
63 #include "panel.h" /* Needed for current_panel and other_panel */
66 #include "main.h" /* slow_terminal */
70 #include "selcodepage.h"
72 #include "../src/search/search.h"
74 /* Block size for reading files in parts */
75 #define VIEW_PAGE_SIZE ((size_t) 8192)
77 typedef unsigned char byte
;
79 /* Offset in bytes into a file */
80 typedef unsigned long offset_type
;
81 #define INVALID_OFFSET ((offset_type) -1)
82 #define OFFSETTYPE_MAX (~((offset_type) 0))
83 #define OFFSETTYPE_PRIX "lX"
84 #define OFFSETTYPE_PRId "lu"
86 /* A width or height on the screen */
87 typedef unsigned int screen_dimen
;
89 /* A node for building a change list on change_list */
90 struct hexedit_change_node
{
91 struct hexedit_change_node
*next
;
96 /* data sources of the view */
98 DS_NONE
, /* No data available */
99 DS_STDIO_PIPE
, /* Data comes from a pipe using popen/pclose */
100 DS_VFS_PIPE
, /* Data comes from a piped-in VFS file */
101 DS_FILE
, /* Data comes from a VFS file */
102 DS_STRING
/* Data comes from a string in memory */
106 screen_dimen top
, left
;
107 screen_dimen height
, width
;
110 #define VLF_DISCARD 1
112 #define VLF_COMPLETE 3
114 /* basic structure for caching text, it correspond to one line in wrapped text.
115 * That makes easier to move in wrap mode.
116 * cache_lines are stored in two linked lists, one for normal mode
117 * and one for nroff mode
118 * cache_line is valid of end is not INVALID_OFFSET
119 * last line has set width to (screen_dimen) (-1)*/
120 /* never access next and previous in cache_line directly, use appropriately function
123 /* number of line in text, so two cache_line have same number
124 * if they are on same text line (text line is wider than screen) */
125 unsigned long number
;
126 /* previous cache_line in list*/
127 struct cache_line
*prev
;
128 /* next cache_line in list*/
129 struct cache_line
*next
;
130 /* offset when cache_line start in text,
131 * cache_line.start = cache_line->prev.end */
133 /* offset when cache_line ends
134 * cache_line.end = cache_line->next.start */
136 /* how many column take on screen */
138 /* correct ident, if prevoius line ends by tabulator */
145 char *filename
; /* Name of the file */
146 char *command
; /* Command used to pipe data in */
148 enum view_ds datasource
; /* Where the displayed data comes from */
150 /* stdio pipe data source */
151 FILE *ds_stdio_pipe
; /* Output of a shell command */
153 /* vfs pipe data source */
154 int ds_vfs_pipe
; /* Non-seekable vfs file descriptor */
156 /* vfs file data source */
157 int ds_file_fd
; /* File with random access */
158 off_t ds_file_filesize
; /* Size of the file */
159 off_t ds_file_offset
; /* Offset of the currently loaded data */
160 byte
*ds_file_data
; /* Currently loaded data */
161 size_t ds_file_datalen
; /* Number of valid bytes in file_data */
162 size_t ds_file_datasize
; /* Number of allocated bytes in file_data */
164 /* string data source */
165 byte
*ds_string_data
; /* The characters of the string */
166 size_t ds_string_len
; /* The length of the string */
168 /* Growing buffers information */
169 gboolean growbuf_in_use
; /* Use the growing buffers? */
170 byte
**growbuf_blockptr
; /* Pointer to the block pointers */
171 size_t growbuf_blocks
; /* The number of blocks in *block_ptr */
172 size_t growbuf_lastindex
; /* Number of bytes in the last page of the
174 gboolean growbuf_finished
; /* TRUE when all data has been read. */
177 gboolean hex_mode
; /* Hexview or Hexedit */
178 gboolean hexedit_mode
; /* Hexedit */
179 gboolean hexview_in_text
; /* Is the hexview cursor in the text area? */
180 gboolean text_nroff_mode
; /* Nroff-style highlighting */
181 gboolean text_wrap_mode
; /* Wrap text lines to fit them on the screen */
182 gboolean magic_mode
; /* Preprocess the file using external programs */
184 /* Additional editor state */
185 gboolean hexedit_lownibble
; /* Are we editing the last significant nibble? */
187 /* Display information */
188 screen_dimen dpy_frame_size
;/* Size of the frame surrounding the real viewer */
189 offset_type dpy_start
; /* Offset of the displayed data */
190 offset_type dpy_end
; /* Offset after the displayed data */
191 offset_type dpy_text_column
;/* Number of skipped columns in non-wrap
193 offset_type hex_cursor
; /* Hexview cursor position in file */
194 screen_dimen cursor_col
; /* Cursor column */
195 screen_dimen cursor_row
; /* Cursor row */
196 struct hexedit_change_node
*change_list
; /* Linked list of changes */
197 struct area status_area
; /* Where the status line is displayed */
198 struct area ruler_area
; /* Where the ruler is displayed */
199 struct area data_area
; /* Where the data is displayed */
201 int dirty
; /* Number of skipped updates */
202 gboolean dpy_bbar_dirty
; /* Does the button bar need to be updated? */
205 int bytes_per_line
; /* Number of bytes per line in hex mode */
207 /* Search variables */
208 offset_type search_start
; /* First character to start searching from */
209 offset_type search_end
; /* Length of found string or 0 if none was found */
211 /* Pointer to the last search command */
212 gboolean want_to_quit
; /* Prepare for cleanup ... */
215 int marker
; /* mark to use */
216 offset_type marks
[10]; /* 10 marks: 0..9 */
218 int move_dir
; /* return value from widget:
220 * -1 view previous file
224 offset_type update_steps
; /* The number of bytes between percent
226 offset_type update_activate
;/* Last point where we updated the status */
228 /* never access cache_line in view directly, use appropriately function
230 /* first cache_line for normal mode */
231 struct cache_line
*lines
;
232 /* last cache_line for normal mode, is set when text is read to the end */
233 struct cache_line
*lines_end
;
234 /* first cache_line for nroff mode */
235 struct cache_line
*nroff_lines
;
236 /* last cache_line for nroff mode, is set when text is read to the end */
237 struct cache_line
*nroff_lines_end
;
238 /* cache_line, that is first showed on display (something like cursor)
239 * used for both normal adn nroff mode */
240 struct cache_line
*first_showed_line
;
241 /* converter for translation of text */
244 /* handle of search engine */
246 gchar
*last_search_string
;
247 mc_search_type_t search_type
;
248 gboolean search_all_codepages
;
249 gboolean search_case
;
250 gboolean search_backwards
;
254 /* {{{ Global Variables }}} */
256 /* Maxlimit for skipping updates */
257 int max_dirt_limit
= 10;
259 /* If set, show a ruler */
260 static enum ruler_type
{
264 } ruler
= RULER_NONE
;
266 /* Scrolling is done in pages or line increments */
267 int mouse_move_pages_viewer
= 1;
269 /* wrap mode default */
270 int global_wrap_mode
= 1;
272 int default_hex_mode
= 0;
273 int default_magic_flag
= 1;
274 int default_nroff_flag
= 1;
275 int altered_hex_mode
= 0;
276 int altered_magic_flag
= 0;
277 int altered_nroff_flag
= 0;
279 static const char hex_char
[] = "0123456789ABCDEF";
281 int mcview_remember_file_position
= FALSE
;
283 /* {{{ Function Prototypes }}} */
285 /* Our widget callback */
286 static cb_ret_t
view_callback (Widget
*, widget_msg_t
, int);
288 static void view_labels (WView
* view
);
290 static void view_init_growbuf (WView
*);
291 static void view_place_cursor (WView
*view
);
292 static void display (WView
*);
293 static void view_done (WView
*);
295 /* {{{ Helper Functions }}} */
297 /* difference or zero */
298 static inline screen_dimen
299 dimen_doz (screen_dimen a
, screen_dimen b
)
301 return (a
>= b
) ? a
- b
: 0;
304 static inline screen_dimen
305 dimen_min (screen_dimen a
, screen_dimen b
)
307 return (a
< b
) ? a
: b
;
310 static inline offset_type
311 offset_doz (offset_type a
, offset_type b
)
313 return (a
>= b
) ? a
- b
: 0;
316 static inline offset_type
317 offset_rounddown (offset_type a
, offset_type b
)
323 /* {{{ Simple Primitive Functions for WView }}} */
325 static inline gboolean
326 view_is_in_panel (WView
*view
)
328 return (view
->dpy_frame_size
!= 0);
331 static inline int get_byte (WView
*view
, offset_type offset
);
333 /* read on character from text, character is translated into terminal encoding
334 * writes result in ch, return how many bytes was read or -1 if end of text */
336 view_get_char (WView
*view
, offset_type from
, char *ch
, int size
)
338 static char buffer
[MB_LEN_MAX
+ 1];
340 static size_t result
;
344 while (result
< sizeof (buffer
) - 1) {
345 c
= get_byte (view
, from
+ result
);
347 buffer
[result
] = (unsigned char) c
;
349 buffer
[result
] = '\0';
350 switch (str_translate_char (view
->converter
, buffer
, result
, ch
, size
)) {
364 /* this structure and view_read_* functions make reading text simpler
365 * view need remeber 4 following charsets: actual, next and two previous */
367 char ch
[4][MB_LEN_MAX
+ 1];
377 /* set read_info into initial state and read first character to cnxt
378 * return how many bytes was read or -1 if end of text */
380 view_read_start (WView
*view
, struct read_info
*info
, offset_type from
)
382 info
->ch
[0][0] = '\0';
383 info
->ch
[1][0] = '\0';
384 info
->ch
[2][0] = '\0';
385 info
->ch
[3][0] = '\0';
387 info
->cnxt
= info
->ch
[0];
388 info
->cact
= info
->ch
[1];
389 info
->chi1
= info
->ch
[2];
390 info
->chi2
= info
->ch
[3];
394 info
->result
= view_get_char (view
, info
->next
, info
->cnxt
, MB_LEN_MAX
+ 1);
397 /* move characters in read_info one forward (chi2 = last previous is forgotten)
398 * read additional charsets into cnxt
399 * return how many bytes was read or -1 if end of text */
401 view_read_continue (WView
*view
, struct read_info
*info
)
406 info
->chi2
= info
->chi1
;
407 info
->chi1
= info
->cact
;
408 info
->cact
= info
->cnxt
;
411 info
->actual
= info
->next
;
412 info
->next
+= info
->result
;
414 info
->result
= view_get_char (view
, info
->next
, info
->cnxt
, MB_LEN_MAX
+ 1);
419 #define VRT_NW_CONTINUE 2
421 /* test if cact of read_info is newline
422 * return VRT_NW_NO, VRT_NW_YES
423 * or VRT_NW_CONTINUE follow after cact ("\r\n", ...) */
425 view_read_test_new_line (WView
*view
, struct read_info
*info
)
427 #define cmp(t1,t2) (strcmp((t1),(t2)) == 0)
429 if (cmp (info
->cact
, "\n")) return VRT_NW_YES
;
431 if (cmp (info
->cact
, "\r")) {
432 if (info
->result
!= -1 && (cmp (info
->cnxt
, "\r") || cmp (info
->cnxt
, "\n")))
433 return VRT_NW_CONTINUE
;
440 /* test if cact of read_info is tabulator
441 * return VRT_NW_NO or VRT_NW_YES */
443 view_read_test_tabulator (WView
*view
, struct read_info
*info
)
445 #define cmp(t1,t2) (strcmp((t1),(t2)) == 0)
447 return cmp (info
->cact
, "\t");
450 /* test if read_info.cact is '\b' and charsets in read_info are correct
451 * nroff sequence, return VRT_NW_NO or VRT_NW_YES */
453 view_read_test_nroff_back (WView
*view
, struct read_info
*info
)
455 #define cmp(t1,t2) (strcmp((t1),(t2)) == 0)
456 return view
->text_nroff_mode
&& cmp (info
->cact
, "\b") &&
457 (info
->result
!= -1) && str_isprint (info
->cnxt
) &&
458 !cmp (info
->chi1
, "") && str_isprint (info
->chi1
) &&
459 (cmp (info
->chi1
, info
->cnxt
) || cmp (info
->chi1
, "_")
460 || (cmp (info
->chi1
, "+") && cmp (info
->cnxt
, "o")));
464 /* rutine for view_load_cache_line, line is ended and following cache_line is
466 * used when end of line has been read in text */
467 static struct cache_line
*
468 view_lc_create_next_line (struct cache_line
*line
, offset_type start
)
470 struct cache_line
*result
= NULL
;
474 if (line
->next
== NULL
) {
475 result
= g_new0 (struct cache_line
, 1);
478 } else result
= line
->next
;
480 result
->start
= start
;
483 result
->number
= line
->number
+ 1;
484 result
->end
= INVALID_OFFSET
;
489 /* rutine for view_load_cache_line, line is ended and following cache_line is
491 * used when line has maximum width */
492 static struct cache_line
*
493 view_lc_create_wrap_line (struct cache_line
*line
, offset_type start
,
496 struct cache_line
*result
= NULL
;
500 if (line
->next
== NULL
) {
501 result
= g_new0 (struct cache_line
, 1);
504 } else result
= line
->next
;
506 result
->start
= start
;
509 result
->number
= line
->number
;
510 result
->end
= INVALID_OFFSET
;
515 /* read charsets fro text and set up correct width, end and start of next line
516 * view->data_area.width must be greater than 0 */
517 static struct cache_line
*
518 view_load_cache_line (WView
*view
, struct cache_line
*line
)
520 const screen_dimen width
= view
->data_area
.width
;
521 struct read_info info
;
522 offset_type nroff_start
= 0;
528 view_read_start (view
, &info
, line
->start
);
529 while (info
.result
!= -1) {
530 view_read_continue (view
, &info
);
532 switch (view_read_test_new_line (view
, &info
)) {
534 line
= view_lc_create_next_line (line
, info
.next
);
536 case VRT_NW_CONTINUE
:
540 if (view_read_test_tabulator (view
, &info
)) {
541 line
->width
+= 8 - (line
->left
+ line
->width
) % 8;
542 if ((width
!= 0) && (line
->left
+ line
->width
>= width
)) {
543 w
= line
->left
+ line
->width
- width
;
544 /* if width of screen is very small, tabulator is cut to line
545 * left of next line is 0 */
546 w
= (w
< width
) ? w
: 0;
547 line
->width
= width
- line
->left
;
548 line
= view_lc_create_wrap_line (line
, info
.next
, w
);
553 if (view_read_test_nroff_back (view
, &info
)) {
554 w
= str_term_width1 (info
.chi1
);
559 /* assure, that nroff sequence never start in previous cache_line */
563 nroff_start
= info
.actual
;
565 w
= str_isprint (info
.cact
) ? str_term_width1 (info
.cact
) : 1;
567 if (line
->left
+ line
->width
+ w
> width
) {
568 line
= view_lc_create_wrap_line (line
, nroff_start
, 0);
571 while (info
.result
!= -1 && str_iscombiningmark (info
.cnxt
)) {
572 view_read_continue (view
, &info
);
579 /* text read to the end, seting lines_end*/
580 if (view
->text_nroff_mode
)
581 view
->nroff_lines_end
= line
;
583 view
->lines_end
= line
;
585 line
= view_lc_create_next_line (line
, info
.next
);
586 line
->width
= (screen_dimen
) (-1);
587 line
->end
= info
.next
;
593 view_lc_set_param (offset_type
*param
, offset_type value
)
595 if ((*param
) > value
) (*param
) = value
;
598 /* offset to column or column to offset, value that will be maped must be
599 * set to INVALID_OFFSET, it is very analogous to view_load_cache_line
600 * and it is possible to integrate them in one function */
602 view_map_offset_and_column (WView
*view
, struct cache_line
*line
,
603 offset_type
*column
, offset_type
*offset
)
605 const screen_dimen width
= view
->data_area
.width
;
606 struct read_info info
;
607 offset_type nroff_start
= 0;
612 /* HACK: valgrind screams here.
613 * TODO: to figure out WHY valgrind detects uninitialized
614 * variables. Maybe, info isn't fully initialized?
616 memset (&info
, 0, sizeof info
);
619 view_read_start (view
, &info
, line
->start
);
620 while (info
.result
!= -1) {
621 view_read_continue (view
, &info
);
623 if (*column
== INVALID_OFFSET
) {
624 if (*offset
< info
.next
) {
625 (*column
) = col
+ line
->left
;
630 switch (view_read_test_new_line (view
, &info
)) {
632 view_lc_set_param (offset
, info
.actual
);
633 view_lc_set_param (column
, line
->left
+ col
);
635 case VRT_NW_CONTINUE
:
639 if (view_read_test_tabulator (view
, &info
)) {
640 col
+= 8 - (line
->left
+ col
) % 8;
641 if ((width
!= 0) && (line
->left
+ col
>= width
)) {
642 w
= line
->left
+ col
- width
;
643 w
= (w
< width
) ? w
: 0;
644 col
= width
- line
->left
;
645 view_lc_set_param (offset
, info
.actual
);
646 view_lc_set_param (column
, line
->left
+ col
);
651 if (view_read_test_nroff_back (view
, &info
)) {
652 w
= str_term_width1 (info
.chi1
);
660 nroff_start
= info
.actual
;
662 w
= str_isprint (info
.cact
) ? str_term_width1 (info
.cact
) : 1;
664 if (line
->left
+ col
+ w
> width
) {
665 view_lc_set_param (offset
, nroff_start
);
666 view_lc_set_param (column
, line
->left
+ col
);
669 while (info
.result
!= -1 && str_iscombiningmark (info
.cnxt
)) {
670 view_read_continue (view
, &info
);
676 if (*offset
== INVALID_OFFSET
) {
677 if (*column
< col
+ line
->left
) {
678 (*offset
) = nroff_start
;
684 view_lc_set_param (offset
, info
.actual
);
685 view_lc_set_param (column
, line
->left
+ col
);
688 /* macro, that iterate cache_line until stop holds,
689 * nf is function to iterate cache_line
690 * l is line to iterate and t is temporary line only */
691 #define view_move_to_stop(l,t,stop,nf) \
692 while (((t = nf (view, l)) != NULL) && (stop)) l = t;
694 /* make all cache_lines invalidet */
696 view_reset_cache_lines (WView
*view
)
698 if (view
->lines
!= NULL
) {
699 view
->lines
->end
= INVALID_OFFSET
;
701 view
->lines_end
= NULL
;
703 if (view
->nroff_lines
!= NULL
) {
704 view
->nroff_lines
->end
= INVALID_OFFSET
;
706 view
->nroff_lines_end
= NULL
;
708 view
->first_showed_line
= NULL
;
711 #define MAX_UNLOADED_CACHE_LINE 100
713 /* free some cache_lines, if count of cache_lines is bigger
714 * than MAX_UNLOADED_CACHE_LINE */
716 view_reduce_cache_lines (WView
*view
)
718 struct cache_line
*line
;
719 struct cache_line
*next
;
724 while (line
!= NULL
&& li
< MAX_UNLOADED_CACHE_LINE
) {
728 if (line
!= NULL
) line
->prev
->next
= NULL
;
729 while (line
!= NULL
) {
734 view
->lines_end
= NULL
;
736 line
= view
->nroff_lines
;
737 while (line
!= NULL
&& li
< MAX_UNLOADED_CACHE_LINE
) {
741 if (line
!= NULL
) line
->prev
->next
= NULL
;
742 while (line
!= NULL
) {
747 view
->nroff_lines_end
= NULL
;
749 view
->first_showed_line
= NULL
;
752 /* return first cache_line for actual mode (normal / nroff) */
753 static struct cache_line
*
754 view_get_first_line (WView
*view
)
756 struct cache_line
**first_line
;
758 first_line
= (view
->text_nroff_mode
) ?
759 &(view
->nroff_lines
) : &(view
->lines
);
761 if (*first_line
== NULL
) {
762 (*first_line
) = g_new0 (struct cache_line
, 1);
763 (*first_line
)->end
= INVALID_OFFSET
;
766 if ((*first_line
)->end
== INVALID_OFFSET
)
767 view_load_cache_line (view
, *first_line
);
769 return (*first_line
);
772 /* return following chahe_line or NULL */
773 static struct cache_line
*
774 view_get_next_line (WView
*view
, struct cache_line
*line
)
776 struct cache_line
*result
;
778 if (line
->next
!= NULL
) {
781 if (result
->end
== INVALID_OFFSET
)
782 view_load_cache_line (view
, result
);
784 return (result
->width
!= (screen_dimen
) (-1)) ? result
: NULL
;
789 /* return last cache_line, it could take same time, because whole read text must
790 * be read (only once) */
791 static struct cache_line
*
792 view_get_last_line (WView
*view
)
794 struct cache_line
*result
;
795 struct cache_line
*next
;
797 result
= (view
->text_nroff_mode
) ?
798 view
->nroff_lines_end
: view
->nroff_lines_end
;
800 if (result
!= NULL
) return result
;
802 if (view
->first_showed_line
== NULL
)
803 view
->first_showed_line
= view_get_first_line (view
);
805 result
= view
->first_showed_line
;
806 next
= view_get_next_line (view
, result
);
807 while (next
!= NULL
) {
809 next
= view_get_next_line (view
, result
);
814 /* return previous cache_line or NULL */
815 static struct cache_line
*
816 view_get_previous_line (WView
*view
, struct cache_line
*line
)
822 /* return first displayed cache_line */
823 static struct cache_line
*
824 view_get_first_showed_line (WView
*view
)
826 struct cache_line
*result
;
828 if (view
->first_showed_line
== NULL
)
829 view
->first_showed_line
= view_get_first_line (view
);
831 result
= view
->first_showed_line
;
835 /* return first cache_line with same number as line */
836 static struct cache_line
*
837 view_get_start_of_whole_line (WView
*view
, struct cache_line
*line
)
839 struct cache_line
*t
;
842 view_move_to_stop (line
, t
, t
->number
== line
->number
, view_get_previous_line
)
848 /* return last cache_line with same number as line */
849 static struct cache_line
*
850 view_get_end_of_whole_line (WView
*view
, struct cache_line
*line
)
852 struct cache_line
*t
;
855 view_move_to_stop (line
, t
, t
->number
== line
->number
, view_get_next_line
)
861 /* return last cache_line, that has number lesser than line
863 static struct cache_line
*
864 view_get_previous_whole_line (WView
*view
, struct cache_line
*line
)
866 line
= view_get_start_of_whole_line (view
, line
);
867 return view_get_previous_line (view
, line
);
870 /* return first cache_line, that has number greater than line
872 static struct cache_line
*
873 view_get_next_whole_line (WView
*view
, struct cache_line
*line
)
875 line
= view_get_end_of_whole_line (view
, line
);
876 return view_get_next_line (view
, line
);
879 /* return sum of widths of all cache_lines that has same number as line */
881 view_width_of_whole_line (WView
*view
, struct cache_line
*line
)
883 struct cache_line
*next
;
884 screen_dimen result
= 0;
886 line
= view_get_start_of_whole_line (view
, line
);
887 next
= view_get_next_line (view
, line
);
888 while ((next
!= NULL
) && (next
->number
== line
->number
)) {
889 result
+= line
->left
+ line
->width
;
891 next
= view_get_next_line (view
, line
);
893 result
+= line
->left
+ line
->width
;
897 /* return sum of widths of cache_lines before line, that has same number as line */
899 view_width_of_whole_line_before (WView
*view
, struct cache_line
*line
)
901 struct cache_line
*next
;
902 screen_dimen result
= 0;
904 next
= view_get_start_of_whole_line (view
, line
);
905 while (next
!= line
) {
906 result
+= next
->left
+ next
->width
;
907 next
= view_get_next_line (view
, next
);
912 /* map column to offset and cache_line */
914 view_column_to_offset (WView
*view
, struct cache_line
**line
, offset_type column
)
916 struct cache_line
*next
;
919 *line
= view_get_start_of_whole_line (view
, *line
);
921 while (column
>= (*line
)->left
+ (*line
)->width
) {
922 column
-= (*line
)->left
+ (*line
)->width
;
923 result
= (*line
)->end
;
924 next
= view_get_next_line (view
, *line
);
925 if ((next
== NULL
) || (next
->number
!= (*line
)->number
)) break;
928 /* if (column < (*line)->left + (*line)->width) { */
929 result
= INVALID_OFFSET
,
930 view_map_offset_and_column (view
, *line
, &column
, &result
);
935 /* map offset to cache_line */
936 static struct cache_line
*
937 view_offset_to_line (WView
*view
, offset_type from
)
939 struct cache_line
*result
;
940 struct cache_line
*t
;
942 result
= view_get_first_line (view
);
944 view_move_to_stop (result
, t
, result
->end
<= from
, view_get_next_line
)
948 /* map offset to cache_line, searching starts from line */
949 static struct cache_line
*
950 view_offset_to_line_from (WView
*view
, offset_type from
, struct cache_line
*line
)
952 struct cache_line
*result
;
953 struct cache_line
*t
;
957 view_move_to_stop (result
, t
, result
->start
> from
, view_get_previous_line
)
958 view_move_to_stop (result
, t
, result
->end
<= from
, view_get_next_line
)
963 /* mam offset to column */
965 view_offset_to_column (WView
*view
, struct cache_line
*line
, offset_type from
)
967 offset_type result
= INVALID_OFFSET
;
969 view_map_offset_and_column (view
, line
, &result
, &from
);
971 result
+= view_width_of_whole_line_before (view
, line
);
977 view_compute_areas (WView
*view
)
979 struct area view_area
;
980 struct cache_line
*next
;
981 screen_dimen height
, rest
, y
;
982 screen_dimen old_width
;
984 old_width
= view
->data_area
.width
;
986 /* The viewer is surrounded by a frame of size view->dpy_frame_size.
987 * Inside that frame, there are: The status line (at the top),
988 * the data area and an optional ruler, which is shown above or
989 * below the data area. */
991 view_area
.top
= view
->dpy_frame_size
;
992 view_area
.left
= view
->dpy_frame_size
;
993 view_area
.height
= dimen_doz(view
->widget
.lines
, 2 * view
->dpy_frame_size
);
994 view_area
.width
= dimen_doz(view
->widget
.cols
, 2 * view
->dpy_frame_size
);
996 /* Most coordinates of the areas equal those of the whole viewer */
997 view
->status_area
= view_area
;
998 view
->ruler_area
= view_area
;
999 view
->data_area
= view_area
;
1001 /* Compute the heights of the areas */
1002 rest
= view_area
.height
;
1004 height
= dimen_min(rest
, 1);
1005 view
->status_area
.height
= height
;
1008 height
= dimen_min(rest
, (ruler
== RULER_NONE
|| view
->hex_mode
) ? 0 : 2);
1009 view
->ruler_area
.height
= height
;
1012 view
->data_area
.height
= rest
;
1014 /* Compute the position of the areas */
1017 view
->status_area
.top
= y
;
1018 y
+= view
->status_area
.height
;
1020 if (ruler
== RULER_TOP
) {
1021 view
->ruler_area
.top
= y
;
1022 y
+= view
->ruler_area
.height
;
1025 view
->data_area
.top
= y
;
1026 y
+= view
->data_area
.height
;
1028 if (ruler
== RULER_BOTTOM
) {
1029 view
->ruler_area
.top
= y
;
1030 y
+= view
->ruler_area
.height
;
1033 if (old_width
!= view
->data_area
.width
) {
1034 view_reset_cache_lines (view
);
1035 view
->first_showed_line
= view_get_first_line (view
);
1036 next
= view_get_next_line (view
, view
->first_showed_line
);
1037 while ((next
!= NULL
) && (view
->first_showed_line
->end
<= view
->dpy_start
)) {
1038 view
->first_showed_line
= next
;
1039 next
= view_get_next_line (view
, view
->first_showed_line
);
1041 view
->dpy_start
= view
->first_showed_line
->start
;
1046 view_hexedit_free_change_list (WView
*view
)
1048 struct hexedit_change_node
*curr
, *next
;
1050 for (curr
= view
->change_list
; curr
!= NULL
; curr
= next
) {
1054 view
->change_list
= NULL
;
1058 /* {{{ Growing buffer }}} */
1061 view_init_growbuf (WView
*view
)
1063 view
->growbuf_in_use
= TRUE
;
1064 view
->growbuf_blockptr
= NULL
;
1065 view
->growbuf_blocks
= 0;
1066 view
->growbuf_lastindex
= VIEW_PAGE_SIZE
;
1067 view
->growbuf_finished
= FALSE
;
1071 view_growbuf_free (WView
*view
)
1075 assert (view
->growbuf_in_use
);
1077 for (i
= 0; i
< view
->growbuf_blocks
; i
++)
1078 g_free (view
->growbuf_blockptr
[i
]);
1079 g_free (view
->growbuf_blockptr
);
1080 view
->growbuf_blockptr
= NULL
;
1081 view
->growbuf_in_use
= FALSE
;
1085 view_growbuf_filesize (WView
*view
)
1087 assert(view
->growbuf_in_use
);
1089 if (view
->growbuf_blocks
== 0)
1092 return ((offset_type
) view
->growbuf_blocks
- 1) * VIEW_PAGE_SIZE
1093 + view
->growbuf_lastindex
;
1096 /* Copies the output from the pipe to the growing buffer, until either
1097 * the end-of-pipe is reached or the interval [0..ofs) of the growing
1098 * buffer is completely filled. */
1100 view_growbuf_read_until (WView
*view
, offset_type ofs
)
1105 gboolean short_read
;
1107 assert (view
->growbuf_in_use
);
1109 if (view
->growbuf_finished
)
1113 while (view_growbuf_filesize (view
) < ofs
|| short_read
) {
1114 if (view
->growbuf_lastindex
== VIEW_PAGE_SIZE
) {
1115 /* Append a new block to the growing buffer */
1116 byte
*newblock
= g_try_malloc (VIEW_PAGE_SIZE
);
1117 byte
**newblocks
= g_try_malloc (sizeof (*newblocks
) * (view
->growbuf_blocks
+ 1));
1118 if (!newblock
|| !newblocks
) {
1123 memcpy (newblocks
, view
->growbuf_blockptr
, sizeof (*newblocks
) * view
->growbuf_blocks
);
1124 g_free (view
->growbuf_blockptr
);
1125 view
->growbuf_blockptr
= newblocks
;
1126 view
->growbuf_blockptr
[view
->growbuf_blocks
++] = newblock
;
1127 view
->growbuf_lastindex
= 0;
1129 p
= view
->growbuf_blockptr
[view
->growbuf_blocks
- 1] + view
->growbuf_lastindex
;
1130 bytesfree
= VIEW_PAGE_SIZE
- view
->growbuf_lastindex
;
1132 if (view
->datasource
== DS_STDIO_PIPE
) {
1133 nread
= fread (p
, 1, bytesfree
, view
->ds_stdio_pipe
);
1135 view
->growbuf_finished
= TRUE
;
1136 (void) pclose (view
->ds_stdio_pipe
);
1138 close_error_pipe (D_NORMAL
, NULL
);
1139 view
->ds_stdio_pipe
= NULL
;
1143 assert (view
->datasource
== DS_VFS_PIPE
);
1145 nread
= mc_read (view
->ds_vfs_pipe
, p
, bytesfree
);
1146 } while (nread
== -1 && errno
== EINTR
);
1147 if (nread
== -1 || nread
== 0) {
1148 view
->growbuf_finished
= TRUE
;
1149 (void) mc_close (view
->ds_vfs_pipe
);
1150 view
->ds_vfs_pipe
= -1;
1154 short_read
= ((size_t)nread
< bytesfree
);
1155 view
->growbuf_lastindex
+= nread
;
1160 get_byte_growing_buffer (WView
*view
, offset_type byte_index
)
1162 offset_type pageno
= byte_index
/ VIEW_PAGE_SIZE
;
1163 offset_type pageindex
= byte_index
% VIEW_PAGE_SIZE
;
1165 assert (view
->growbuf_in_use
);
1167 if ((size_t) pageno
!= pageno
)
1170 view_growbuf_read_until (view
, byte_index
+ 1);
1171 if (view
->growbuf_blocks
== 0)
1173 if (pageno
< view
->growbuf_blocks
- 1)
1174 return view
->growbuf_blockptr
[pageno
][pageindex
];
1175 if (pageno
== view
->growbuf_blocks
- 1 && pageindex
< view
->growbuf_lastindex
)
1176 return view
->growbuf_blockptr
[pageno
][pageindex
];
1180 /* {{{ Data sources }}} */
1183 The data source provides the viewer with data from either a file, a
1184 string or the output of a command. The get_byte() function can be
1185 used to get the value of a byte at a specific offset. If the offset
1186 is out of range, -1 is returned. The function get_byte_indexed(a,b)
1187 returns the byte at the offset a+b, or -1 if a+b is out of range.
1189 The view_set_byte() function has the effect that later calls to
1190 get_byte() will return the specified byte for this offset. This
1191 function is designed only for use by the hexedit component after
1192 saving its changes. Inspect the source before you want to use it for
1195 The view_get_filesize() function returns the current size of the
1196 data source. If the growing buffer is used, this size may increase
1197 later on. Use the view_may_still_grow() function when you want to
1198 know if the size can change later.
1202 view_get_filesize (WView
*view
)
1204 switch (view
->datasource
) {
1209 return view_growbuf_filesize (view
);
1211 return view
->ds_file_filesize
;
1213 return view
->ds_string_len
;
1215 assert(!"Unknown datasource type");
1220 static inline gboolean
1221 view_may_still_grow (WView
*view
)
1223 return (view
->growbuf_in_use
&& !view
->growbuf_finished
);
1226 /* returns TRUE if the idx lies in the half-open interval
1227 * [offset; offset + size), FALSE otherwise.
1229 static inline gboolean
1230 already_loaded (offset_type offset
, offset_type idx
, size_t size
)
1232 return (offset
<= idx
&& idx
- offset
< size
);
1236 view_file_load_data (WView
*view
, offset_type byte_index
)
1238 offset_type blockoffset
;
1242 assert (view
->datasource
== DS_FILE
);
1244 if (already_loaded (view
->ds_file_offset
, byte_index
, view
->ds_file_datalen
))
1247 if (byte_index
>= view
->ds_file_filesize
)
1250 blockoffset
= offset_rounddown (byte_index
, view
->ds_file_datasize
);
1251 if (mc_lseek (view
->ds_file_fd
, blockoffset
, SEEK_SET
) == -1)
1255 while (bytes_read
< view
->ds_file_datasize
) {
1256 res
= mc_read (view
->ds_file_fd
, view
->ds_file_data
+ bytes_read
, view
->ds_file_datasize
- bytes_read
);
1261 bytes_read
+= (size_t) res
;
1263 view
->ds_file_offset
= blockoffset
;
1264 if (bytes_read
> view
->ds_file_filesize
- view
->ds_file_offset
) {
1265 /* the file has grown in the meantime -- stick to the old size */
1266 view
->ds_file_datalen
= view
->ds_file_filesize
- view
->ds_file_offset
;
1268 view
->ds_file_datalen
= bytes_read
;
1273 view
->ds_file_datalen
= 0;
1277 get_byte_none (WView
*view
, offset_type byte_index
)
1279 assert (view
->datasource
== DS_NONE
);
1286 get_byte_file (WView
*view
, offset_type byte_index
)
1288 assert (view
->datasource
== DS_FILE
);
1290 view_file_load_data (view
, byte_index
);
1291 if (already_loaded(view
->ds_file_offset
, byte_index
, view
->ds_file_datalen
))
1292 return view
->ds_file_data
[byte_index
- view
->ds_file_offset
];
1297 get_byte_string (WView
*view
, offset_type byte_index
)
1299 assert (view
->datasource
== DS_STRING
);
1300 if (byte_index
< view
->ds_string_len
)
1301 return view
->ds_string_data
[byte_index
];
1306 get_byte (WView
*view
, offset_type offset
)
1308 switch (view
->datasource
) {
1311 return get_byte_growing_buffer (view
, offset
);
1313 return get_byte_file (view
, offset
);
1315 return get_byte_string (view
, offset
);
1317 return get_byte_none (view
, offset
);
1319 assert(!"Unknown datasource type");
1324 get_byte_indexed (WView
*view
, offset_type base
, offset_type ofs
)
1326 if (base
<= OFFSETTYPE_MAX
- ofs
)
1327 return get_byte (view
, base
+ ofs
);
1332 view_set_byte (WView
*view
, offset_type offset
, byte b
)
1335 assert (offset
< view_get_filesize (view
));
1336 assert (view
->datasource
== DS_FILE
);
1337 view
->ds_file_datalen
= 0; /* just force reloading */
1341 view_set_datasource_none (WView
*view
)
1343 view
->datasource
= DS_NONE
;
1347 view_set_datasource_vfs_pipe (WView
*view
, int fd
)
1350 view
->datasource
= DS_VFS_PIPE
;
1351 view
->ds_vfs_pipe
= fd
;
1353 view_init_growbuf (view
);
1357 view_set_datasource_stdio_pipe (WView
*view
, FILE *fp
)
1359 assert (fp
!= NULL
);
1360 view
->datasource
= DS_STDIO_PIPE
;
1361 view
->ds_stdio_pipe
= fp
;
1363 view_init_growbuf (view
);
1367 view_set_datasource_string (WView
*view
, const char *s
)
1369 view
->datasource
= DS_STRING
;
1370 view
->ds_string_data
= (byte
*) g_strdup (s
);
1371 view
->ds_string_len
= strlen (s
);
1375 view_set_datasource_file (WView
*view
, int fd
, const struct stat
*st
)
1377 view
->datasource
= DS_FILE
;
1378 view
->ds_file_fd
= fd
;
1379 view
->ds_file_filesize
= st
->st_size
;
1380 view
->ds_file_offset
= 0;
1381 view
->ds_file_data
= g_malloc (4096);
1382 view
->ds_file_datalen
= 0;
1383 view
->ds_file_datasize
= 4096;
1387 view_close_datasource (WView
*view
)
1389 switch (view
->datasource
) {
1393 if (view
->ds_stdio_pipe
!= NULL
) {
1394 (void) pclose (view
->ds_stdio_pipe
);
1396 close_error_pipe (D_NORMAL
, NULL
);
1397 view
->ds_stdio_pipe
= NULL
;
1399 view_growbuf_free (view
);
1402 if (view
->ds_vfs_pipe
!= -1) {
1403 (void) mc_close (view
->ds_vfs_pipe
);
1404 view
->ds_vfs_pipe
= -1;
1406 view_growbuf_free (view
);
1409 (void) mc_close (view
->ds_file_fd
);
1410 view
->ds_file_fd
= -1;
1411 g_free (view
->ds_file_data
);
1412 view
->ds_file_data
= NULL
;
1415 g_free (view
->ds_string_data
);
1416 view
->ds_string_data
= NULL
;
1419 assert (!"Unknown datasource type");
1421 view
->datasource
= DS_NONE
;
1424 /* {{{ Cursor Movement }}} */
1427 The following variables have to do with the current position and are
1428 updated by the cursor movement functions.
1430 In hex view and wrapped text view mode, dpy_start marks the offset of
1431 the top-left corner on the screen, in non-wrapping text mode it is
1432 the beginning of the current line. In hex mode, hex_cursor is the
1433 offset of the cursor. In non-wrapping text mode, dpy_text_column is
1434 the number of columns that are hidden on the left side on the screen.
1436 In hex mode, dpy_start is updated by the view_fix_cursor_position()
1437 function in order to keep the other functions simple. In
1438 non-wrapping text mode dpy_start and dpy_text_column are normalized
1439 such that dpy_text_column < view_get_datacolumns().
1442 /* prototypes for functions used by view_moveto_bottom() */
1443 static void view_move_up (WView
*, offset_type
);
1444 static void view_moveto_bol (WView
*);
1446 /* set view->first_showed_line and view->dpy_start
1447 * use view->dpy_text_column in nowrap mode */
1449 view_set_first_showed (WView
*view
, struct cache_line
*line
)
1451 if (view
->text_wrap_mode
) {
1452 view
->dpy_start
= line
->start
;
1453 view
->first_showed_line
= line
;
1455 view
->dpy_start
= view_column_to_offset (view
, &line
, view
->dpy_text_column
);
1456 view
->first_showed_line
= line
;
1458 if (view
->search_start
== view
->search_end
) {
1459 view
->search_start
= view
->dpy_start
;
1460 view
->search_end
= view
->dpy_start
;
1465 view_scroll_to_cursor (WView
*view
)
1467 if (view
->hex_mode
) {
1468 const offset_type bytes
= view
->bytes_per_line
;
1469 const offset_type displaysize
= view
->data_area
.height
* bytes
;
1470 const offset_type cursor
= view
->hex_cursor
;
1471 offset_type topleft
= view
->dpy_start
;
1473 if (topleft
+ displaysize
<= cursor
)
1474 topleft
= offset_rounddown (cursor
, bytes
)
1475 - (displaysize
- bytes
);
1476 if (cursor
< topleft
)
1477 topleft
= offset_rounddown (cursor
, bytes
);
1478 view
->dpy_start
= topleft
;
1479 } else if (view
->text_wrap_mode
) {
1480 view
->dpy_text_column
= 0;
1486 view_movement_fixups (WView
*view
, gboolean reset_search
)
1488 view_scroll_to_cursor (view
);
1490 view
->search_start
= view
->dpy_start
;
1491 view
->search_end
= view
->dpy_start
;
1497 view_moveto_top (WView
*view
)
1499 view
->dpy_start
= 0;
1500 view
->hex_cursor
= 0;
1501 view
->first_showed_line
= view_get_first_line (view
);
1502 view
->dpy_text_column
= 0;
1503 view_movement_fixups (view
, TRUE
);
1507 view_moveto_bottom (WView
*view
)
1509 offset_type datalines
, lines_up
, filesize
, last_offset
;
1510 struct cache_line
*line
;
1512 if (view
->growbuf_in_use
)
1513 view_growbuf_read_until (view
, OFFSETTYPE_MAX
);
1515 filesize
= view_get_filesize (view
);
1516 last_offset
= offset_doz(filesize
, 1);
1517 datalines
= view
->data_area
.height
;
1518 lines_up
= offset_doz(datalines
, 1);
1520 if (view
->hex_mode
) {
1521 view
->hex_cursor
= filesize
;
1522 view_move_up (view
, lines_up
);
1523 view
->hex_cursor
= last_offset
;
1525 line
= view_get_last_line (view
);
1526 if (!view
->text_wrap_mode
)
1527 line
= view_get_start_of_whole_line (view
, line
);
1528 view_set_first_showed (view
, line
);
1529 view
->dpy_text_column
= 0;
1530 view_move_up (view
, lines_up
);
1532 view_movement_fixups (view
, TRUE
);
1536 view_moveto_bol (WView
*view
)
1538 struct cache_line
*line
;
1540 if (view
->hex_mode
) {
1541 view
->hex_cursor
-= view
->hex_cursor
% view
->bytes_per_line
;
1542 } else if (view
->text_wrap_mode
) {
1545 line
= view_get_first_showed_line (view
);
1546 line
= view_get_start_of_whole_line (view
, line
);
1547 view
->dpy_text_column
= 0;
1548 view_set_first_showed (view
, line
);
1550 view_movement_fixups (view
, TRUE
);
1554 view_moveto_eol (WView
*view
)
1556 const screen_dimen width
= view
->data_area
.width
;
1557 struct cache_line
*line
;
1560 if (view
->hex_mode
) {
1561 offset_type filesize
, bol
;
1563 bol
= offset_rounddown (view
->hex_cursor
, view
->bytes_per_line
);
1564 if (get_byte_indexed (view
, bol
, view
->bytes_per_line
- 1) != -1) {
1565 view
->hex_cursor
= bol
+ view
->bytes_per_line
- 1;
1567 filesize
= view_get_filesize (view
);
1568 view
->hex_cursor
= offset_doz(filesize
, 1);
1570 } else if (view
->text_wrap_mode
) {
1573 line
= view_get_first_showed_line (view
);
1574 line
= view_get_start_of_whole_line (view
, line
);
1575 w
= view_width_of_whole_line (view
, line
);
1577 view
->dpy_text_column
= w
- width
;
1579 /* if (w + width <= view->dpy_text_column) {*/
1580 view
->dpy_text_column
= 0;
1583 view_set_first_showed (view
, line
);
1585 view_movement_fixups (view
, FALSE
);
1589 view_moveto_offset (WView
*view
, offset_type offset
)
1591 struct cache_line
*line
;
1593 if (view
->hex_mode
) {
1594 view
->hex_cursor
= offset
;
1595 view
->dpy_start
= offset
- offset
% view
->bytes_per_line
;
1597 line
= view_offset_to_line (view
, offset
);
1598 view
->dpy_start
= (view
->text_wrap_mode
) ? line
->start
: offset
;
1599 view
->first_showed_line
= line
;
1600 view
->dpy_text_column
= (view
->text_wrap_mode
) ?
1601 0 : view_offset_to_column (view
, line
, offset
);
1603 view_movement_fixups (view
, TRUE
);
1607 view_moveto (WView
*view
, offset_type row
, offset_type col
)
1609 struct cache_line
*act
;
1610 struct cache_line
*t
;
1612 act
= view_get_first_line (view
);
1613 view_move_to_stop (act
, t
, (off_t
)act
->number
!= row
, view_get_next_line
)
1615 view
->dpy_text_column
= (view
->text_wrap_mode
) ? 0 : col
;
1616 view
->dpy_start
= view_column_to_offset (view
, &act
, col
);
1617 view
->dpy_start
= (view
->text_wrap_mode
) ? act
->start
: view
->dpy_start
;
1618 view
->first_showed_line
= act
;
1621 view_moveto_offset (view
, view
->dpy_start
);
1624 /* extendet view_move_to_stop, now has counter, too */
1625 #define view_count_to_stop(l,t,i,stop,nf)\
1626 while (((t = nf(view, l)) != NULL) && (stop)) {\
1630 view_move_up (WView
*view
, offset_type lines
)
1632 struct cache_line
*line
, *t
;
1635 if (view
->hex_mode
) {
1636 offset_type bytes
= lines
* view
->bytes_per_line
;
1637 if (view
->hex_cursor
>= bytes
) {
1638 view
->hex_cursor
-= bytes
;
1639 if (view
->hex_cursor
< view
->dpy_start
)
1640 view
->dpy_start
= offset_doz (view
->dpy_start
, bytes
);
1642 view
->hex_cursor
%= view
->bytes_per_line
;
1644 } else if (view
->text_wrap_mode
) {
1645 line
= view_get_first_showed_line (view
);
1647 view_count_to_stop (line
, t
, li
, (li
< lines
), view_get_previous_line
)
1648 view_set_first_showed (view
, line
);
1650 line
= view_get_first_showed_line (view
);
1652 view_count_to_stop (line
, t
, li
, (li
< lines
), view_get_previous_whole_line
)
1653 view_set_first_showed (view
, line
);
1655 view_movement_fixups (view
, (lines
!= 1));
1659 return_up (struct cache_line * (*ne) (WView *, struct cache_line *),
1660 struct cache_line * (*pr) (WView *, struct cache_line *),
1661 off_t *li, struct cache_line **t, struct cache_line **line,
1666 while ((*t != NULL) && (*li < view->data_area.height)) {
1670 *li = view->data_area.height - *li;
1671 *t = pr (view, *line);
1672 while ((*t != NULL) && (*li > 0)) {
1674 *t = pr (view, *line);
1680 view_move_down (WView
*view
, offset_type lines
)
1682 struct cache_line
*line
;
1683 struct cache_line
*t
;
1687 if (view
->hex_mode
) {
1688 offset_type i
, limit
, last_byte
;
1690 last_byte
= view_get_filesize (view
);
1691 if (last_byte
>= (offset_type
) view
->bytes_per_line
)
1692 limit
= last_byte
- view
->bytes_per_line
;
1695 for (i
= 0; i
< lines
&& view
->hex_cursor
< limit
; i
++) {
1696 view
->hex_cursor
+= view
->bytes_per_line
;
1698 view
->dpy_start
+= view
->bytes_per_line
;
1701 } else if (view
->text_wrap_mode
) {
1702 line
= view_get_first_showed_line (view
);
1704 view_count_to_stop (line
, t
, li
, (off_t
)li
< lines
, view_get_next_line
)
1706 /* return_up (view_get_next_line, view_get_previous_line); */
1707 view_set_first_showed (view
, line
);
1709 line
= view_get_first_showed_line (view
);
1711 view_count_to_stop (line
, t
, li
, li
< lines
, view_get_next_whole_line
)
1713 /* return_up (view_get_next_whole_line, view_get_previous_whole_line); */
1714 view_set_first_showed (view
, line
);
1716 view_movement_fixups (view
, (lines
!= 1));
1720 view_move_left (WView
*view
, offset_type columns
)
1722 struct cache_line
*line
;
1724 if (view
->hex_mode
) {
1725 assert (columns
== 1);
1726 if (view
->hexview_in_text
|| !view
->hexedit_lownibble
) {
1727 if (view
->hex_cursor
> 0)
1730 if (!view
->hexview_in_text
)
1731 view
->hexedit_lownibble
= !view
->hexedit_lownibble
;
1732 } else if (view
->text_wrap_mode
) {
1735 if (view
->dpy_text_column
>= columns
)
1736 view
->dpy_text_column
-= columns
;
1738 view
->dpy_text_column
= 0;
1740 line
= view_get_first_showed_line (view
);
1741 view_set_first_showed (view
, line
);
1743 view_movement_fixups (view
, FALSE
);
1747 view_move_right (WView
*view
, offset_type columns
)
1749 struct cache_line
*line
;
1751 if (view
->hex_mode
) {
1752 assert (columns
== 1);
1753 if (view
->hexview_in_text
|| view
->hexedit_lownibble
) {
1754 if (get_byte_indexed (view
, view
->hex_cursor
, 1) != -1)
1757 if (!view
->hexview_in_text
)
1758 view
->hexedit_lownibble
= !view
->hexedit_lownibble
;
1759 } else if (view
->text_wrap_mode
) {
1762 view
->dpy_text_column
+= columns
;
1763 line
= view_get_first_showed_line (view
);
1764 view_set_first_showed (view
, line
);
1766 view_movement_fixups (view
, FALSE
);
1769 /* {{{ Toggling of viewer modes }}} */
1772 view_toggle_hex_mode (WView
*view
)
1774 struct cache_line
*line
;
1776 view
->hex_mode
= !view
->hex_mode
;
1777 view
->search_type
= (view
->hex_mode
)?MC_SEARCH_T_HEX
:MC_SEARCH_T_NORMAL
;
1779 if (view
->hex_mode
) {
1780 view
->hex_cursor
= view
->dpy_start
;
1782 offset_rounddown (view
->dpy_start
, view
->bytes_per_line
);
1783 view
->widget
.options
|= W_WANT_CURSOR
;
1785 line
= view_offset_to_line (view
, view
->hex_cursor
);
1786 view
->dpy_text_column
= (view
->text_wrap_mode
) ? 0 :
1787 view_offset_to_column (view
, line
, view
->hex_cursor
);
1788 view_set_first_showed (view
, line
);
1789 view
->widget
.options
&= ~W_WANT_CURSOR
;
1791 altered_hex_mode
= 1;
1792 view
->dpy_bbar_dirty
= TRUE
;
1797 view_toggle_hexedit_mode (WView
*view
)
1799 view
->hexedit_mode
= !view
->hexedit_mode
;
1800 view
->dpy_bbar_dirty
= TRUE
;
1805 view_toggle_wrap_mode (WView
*view
)
1807 struct cache_line
*line
;
1809 view
->text_wrap_mode
= !view
->text_wrap_mode
;
1810 if (view
->text_wrap_mode
) {
1811 view
->dpy_text_column
= 0;
1812 view
->dpy_start
= view_get_first_showed_line (view
)->start
;
1814 line
= view_get_first_showed_line (view
);
1815 view
->dpy_text_column
= view_width_of_whole_line_before (view
, line
);
1817 view
->dpy_bbar_dirty
= TRUE
;
1822 view_toggle_nroff_mode (WView
*view
)
1824 struct cache_line
*line
;
1825 struct cache_line
*next
;
1827 view
->text_nroff_mode
= !view
->text_nroff_mode
;
1828 altered_nroff_flag
= 1;
1829 view
->dpy_bbar_dirty
= TRUE
;
1832 line
= view_get_first_line (view
);
1833 view_move_to_stop (line
, next
, line
->end
<= view
->dpy_start
, view_get_next_line
)
1835 view_set_first_showed (view
, line
);
1839 view_toggle_magic_mode (WView
*view
)
1841 char *filename
, *command
;
1843 altered_magic_flag
= 1;
1844 view
->magic_mode
= !view
->magic_mode
;
1845 filename
= g_strdup (view
->filename
);
1846 command
= g_strdup (view
->command
);
1849 view_load (view
, command
, filename
, 0);
1852 view
->dpy_bbar_dirty
= TRUE
;
1856 /* {{{ Miscellaneous functions }}} */
1859 view_done (WView
*view
)
1861 /* Save current file position */
1862 if (mcview_remember_file_position
&& view
->filename
!= NULL
) {
1863 struct cache_line
*line
;
1865 offset_type row
, col
;
1867 canon_fname
= vfs_canon (view
->filename
);
1868 line
= view_get_first_showed_line (view
);
1869 row
= line
->number
+ 1;
1870 col
= view_offset_to_column (view
, line
, view
->dpy_start
);
1872 save_file_position (canon_fname
, row
, col
);
1873 g_free (canon_fname
);
1876 /* Write back the global viewer mode */
1877 default_hex_mode
= view
->hex_mode
;
1878 default_nroff_flag
= view
->text_nroff_mode
;
1879 default_magic_flag
= view
->magic_mode
;
1880 global_wrap_mode
= view
->text_wrap_mode
;
1883 /* view->widget needs no destructor */
1885 g_free (view
->filename
), view
->filename
= NULL
;
1886 g_free (view
->command
), view
->command
= NULL
;
1888 view_close_datasource (view
);
1889 /* the growing buffer is freed with the datasource */
1891 view_hexedit_free_change_list (view
);
1893 g_free(view
->last_search_string
);
1894 view
->last_search_string
= NULL
;
1896 /* Free memory used by the viewer */
1897 view_reduce_cache_lines (view
);
1898 if (view
->converter
!= str_cnv_from_term
) str_close_conv (view
->converter
);
1903 view_show_error (WView
*view
, const char *msg
)
1905 view_close_datasource (view
);
1906 if (view_is_in_panel (view
)) {
1907 view_set_datasource_string (view
, msg
);
1909 message (D_ERROR
, MSG_ERROR
, "%s", msg
);
1914 view_load_command_output (WView
*view
, const char *command
)
1918 view_close_datasource (view
);
1921 if ((fp
= popen (command
, "r")) == NULL
) {
1922 /* Avoid two messages. Message from stderr has priority. */
1924 if (!close_error_pipe (view_is_in_panel (view
) ? -1 : D_ERROR
, NULL
))
1925 view_show_error (view
, _(" Cannot spawn child process "));
1929 /* First, check if filter produced any output */
1930 view_set_datasource_stdio_pipe (view
, fp
);
1931 if (get_byte (view
, 0) == -1) {
1932 view_close_datasource (view
);
1934 /* Avoid two messages. Message from stderr has priority. */
1936 if (!close_error_pipe (view_is_in_panel (view
) ? -1 : D_ERROR
, NULL
))
1937 view_show_error (view
, _("Empty output from child filter"));
1944 view_load (WView
*view
, const char *command
, const char *file
,
1949 char tmp
[BUF_MEDIUM
];
1953 gboolean retval
= FALSE
;
1955 assert (view
->bytes_per_line
!= 0);
1959 /* Set up the state */
1960 view_set_datasource_none (view
);
1961 view
->filename
= g_strdup (file
);
1964 /* Clear the markers */
1966 for (i
= 0; i
< 10; i
++)
1969 if (!view_is_in_panel (view
)) {
1970 view
->dpy_text_column
= 0;
1973 if (command
&& (view
->magic_mode
|| file
== NULL
|| file
[0] == '\0')) {
1974 retval
= view_load_command_output (view
, command
);
1975 } else if (file
!= NULL
&& file
[0] != '\0') {
1977 if ((fd
= mc_open (file
, O_RDONLY
| O_NONBLOCK
)) == -1) {
1978 g_snprintf (tmp
, sizeof (tmp
), _(" Cannot open \"%s\"\n %s "),
1979 file
, unix_error_string (errno
));
1980 view_show_error (view
, tmp
);
1981 g_free (view
->filename
);
1982 view
->filename
= NULL
;
1986 /* Make sure we are working with a regular file */
1987 if (mc_fstat (fd
, &st
) == -1) {
1989 g_snprintf (tmp
, sizeof (tmp
), _(" Cannot stat \"%s\"\n %s "),
1990 file
, unix_error_string (errno
));
1991 view_show_error (view
, tmp
);
1992 g_free (view
->filename
);
1993 view
->filename
= NULL
;
1997 if (!S_ISREG (st
.st_mode
)) {
1999 view_show_error (view
, _(" Cannot view: not a regular file "));
2000 g_free (view
->filename
);
2001 view
->filename
= NULL
;
2005 if (st
.st_size
== 0 || mc_lseek (fd
, 0, SEEK_SET
) == -1) {
2006 /* Must be one of those nice files that grow (/proc) */
2007 view_set_datasource_vfs_pipe (view
, fd
);
2009 type
= get_compression_type (fd
);
2011 if (view
->magic_mode
&& (type
!= COMPRESSION_NONE
)) {
2012 g_free (view
->filename
);
2013 view
->filename
= g_strconcat (file
, decompress_extension (type
), (char *) NULL
);
2015 view_set_datasource_file (view
, fd
, &st
);
2021 view
->command
= g_strdup (command
);
2022 view
->dpy_start
= 0;
2023 view
->search_start
= 0;
2024 view
->search_end
= 0;
2025 view
->dpy_text_column
= 0;
2027 view
->converter
= str_cnv_from_term
;
2028 /* try detect encoding from path */
2029 if (view
->filename
!= NULL
) {
2030 canon_fname
= vfs_canon (view
->filename
);
2031 enc
= vfs_get_encoding (canon_fname
);
2033 view
->converter
= str_crt_conv_from (enc
);
2034 if (view
->converter
== INVALID_CONV
)
2035 view
->converter
= str_cnv_from_term
;
2037 g_free (canon_fname
);
2040 view_compute_areas (view
);
2041 view_reset_cache_lines (view
);
2042 view
->first_showed_line
= view_get_first_line (view
);
2044 assert (view
->bytes_per_line
!= 0);
2045 if (mcview_remember_file_position
&& view
->filename
!= NULL
&& start_line
== 0) {
2048 canon_fname
= vfs_canon (view
->filename
);
2049 load_file_position (file
, &line
, &col
);
2050 g_free (canon_fname
);
2051 view_moveto (view
, offset_doz(line
, 1), col
);
2052 } else if (start_line
> 0) {
2053 view_moveto (view
, start_line
- 1, 0);
2056 view
->hexedit_lownibble
= FALSE
;
2057 view
->hexview_in_text
= FALSE
;
2058 view
->change_list
= NULL
;
2063 /* {{{ Display management }}} */
2066 view_update_bytes_per_line (WView
*view
)
2068 const screen_dimen cols
= view
->data_area
.width
;
2074 bytes
= 4 * ((cols
- 8) / ((cols
< 80) ? 17 : 18));
2077 view
->bytes_per_line
= bytes
;
2078 view
->dirty
= max_dirt_limit
+ 1; /* To force refresh */
2082 view_percent (WView
*view
, offset_type p
)
2084 const screen_dimen top
= view
->status_area
.top
;
2085 const screen_dimen right
= view
->status_area
.left
+ view
->status_area
.width
;
2086 const screen_dimen height
= view
->status_area
.height
;
2088 offset_type filesize
;
2090 if (height
< 1 || right
< 4)
2092 if (view_may_still_grow (view
))
2094 filesize
= view_get_filesize (view
);
2096 if (filesize
== 0 || view
->dpy_end
== filesize
)
2098 else if (p
> (INT_MAX
/ 100))
2099 percent
= p
/ (filesize
/ 100);
2101 percent
= p
* 100 / filesize
;
2103 widget_move (view
, top
, right
- 4);
2104 tty_printf ("%3d%%", percent
);
2108 view_display_status (WView
*view
)
2110 const screen_dimen top
= view
->status_area
.top
;
2111 const screen_dimen left
= view
->status_area
.left
;
2112 const screen_dimen width
= view
->status_area
.width
;
2113 const screen_dimen height
= view
->status_area
.height
;
2114 const char *file_label
, *file_name
;
2115 screen_dimen file_label_width
;
2122 tty_setcolor (SELECTED_COLOR
);
2123 widget_move (view
, top
, left
);
2126 file_label
= _("File: %s");
2127 file_label_width
= str_term_width1 (file_label
) - 2;
2128 file_name
= view
->filename
? view
->filename
2129 : view
->command
? view
->command
2132 if (width
< file_label_width
+ 6)
2133 addstr (str_fit_to_term (file_name
, width
, J_LEFT_FIT
));
2135 i
= (width
> 22 ? 22 : width
) - file_label_width
;
2137 tmp
= g_strdup_printf (file_label
, str_fit_to_term (file_name
, i
, J_LEFT_FIT
));
2141 widget_move (view
, top
, left
+ 24);
2142 /* FIXME: the format strings need to be changed when offset_type changes */
2144 tty_printf (_("Offset 0x%08lx"), (unsigned long) view
->hex_cursor
);
2146 screen_dimen row
, col
;
2147 struct cache_line
*line
;
2149 line
= view_get_first_showed_line (view
);
2150 row
= line
->number
+ 1;
2152 col
= (view
->text_wrap_mode
) ?
2153 view_width_of_whole_line_before (view
, line
) :
2154 view
->dpy_text_column
;
2157 tty_printf (_("Line %lu Col %lu"),
2158 (unsigned long) row
, (unsigned long) col
);
2162 offset_type filesize
;
2163 filesize
= view_get_filesize (view
);
2164 widget_move (view
, top
, left
+ 43);
2165 if (!view_may_still_grow (view
)) {
2166 tty_printf (_("%s bytes"), size_trunc (filesize
));
2168 tty_printf (_(">= %s bytes"), size_trunc (filesize
));
2172 view_percent (view
, view
->hex_mode
2177 tty_setcolor (SELECTED_COLOR
);
2181 view_display_clean (WView
*view
)
2183 tty_setcolor (NORMAL_COLOR
);
2184 widget_erase ((Widget
*) view
);
2185 if (view
->dpy_frame_size
!= 0) {
2186 draw_double_box (view
->widget
.parent
, view
->widget
.y
,
2187 view
->widget
.x
, view
->widget
.lines
,
2200 view_count_backspaces (WView
*view
, off_t offset
)
2203 while (offset
>= 2 * backspaces
2204 && get_byte (view
, offset
- 2 * backspaces
) == '\b')
2210 view_display_ruler (WView
*view
)
2212 static const char ruler_chars
[] = "|----*----";
2213 const screen_dimen top
= view
->ruler_area
.top
;
2214 const screen_dimen left
= view
->ruler_area
.left
;
2215 const screen_dimen width
= view
->ruler_area
.width
;
2216 const screen_dimen height
= view
->ruler_area
.height
;
2217 const screen_dimen line_row
= (ruler
== RULER_TOP
) ? 0 : 1;
2218 const screen_dimen nums_row
= (ruler
== RULER_TOP
) ? 1 : 0;
2224 if (ruler
== RULER_NONE
|| height
< 1)
2227 tty_setcolor (MARKED_COLOR
);
2228 for (c
= 0; c
< width
; c
++) {
2229 cl
= view
->dpy_text_column
+ c
;
2230 if (line_row
< height
) {
2231 widget_move (view
, top
+ line_row
, left
+ c
);
2232 tty_print_char (ruler_chars
[cl
% 10]);
2235 if ((cl
!= 0) && (cl
% 10) == 0) {
2236 g_snprintf (r_buff
, sizeof (r_buff
), "%"OFFSETTYPE_PRId
, cl
);
2237 if (nums_row
< height
) {
2238 widget_move (view
, top
+ nums_row
, left
+ c
- 1);
2239 tty_print_string (r_buff
);
2243 attrset (NORMAL_COLOR
);
2247 view_display_hex (WView
*view
)
2249 const screen_dimen top
= view
->data_area
.top
;
2250 const screen_dimen left
= view
->data_area
.left
;
2251 const screen_dimen height
= view
->data_area
.height
;
2252 const screen_dimen width
= view
->data_area
.width
;
2253 const int ngroups
= view
->bytes_per_line
/ 4;
2254 const screen_dimen text_start
=
2255 8 + 13 * ngroups
+ ((width
< 80) ? 0 : (ngroups
- 1 + 1));
2256 /* 8 characters are used for the file offset, and every hex group
2257 * takes 13 characters. On ``big'' screens, the groups are separated
2258 * by an extra vertical line, and there is an extra space before the
2262 screen_dimen row
, col
;
2265 mark_t boldflag
= MARK_NORMAL
;
2266 struct hexedit_change_node
*curr
= view
->change_list
;
2269 char hex_buff
[10]; /* A temporary buffer for sprintf and mvwaddstr */
2270 int bytes
; /* Number of bytes already printed on the line */
2272 view_display_clean (view
);
2274 /* Find the first displayable changed byte */
2275 from
= view
->dpy_start
;
2276 while (curr
&& (curr
->offset
< from
)) {
2280 for (row
= 0; get_byte (view
, from
) != -1 && row
< height
; row
++) {
2283 /* Print the hex offset */
2284 g_snprintf (hex_buff
, sizeof (hex_buff
), "%08"OFFSETTYPE_PRIX
" ", from
);
2285 widget_move (view
, top
+ row
, left
);
2286 tty_setcolor (MARKED_COLOR
);
2287 for (i
= 0; col
< width
&& hex_buff
[i
] != '\0'; i
++) {
2288 addch (hex_buff
[i
]);
2289 /* tty_print_char(hex_buff[i]);*/
2292 tty_setcolor (NORMAL_COLOR
);
2294 for (bytes
= 0; bytes
< view
->bytes_per_line
; bytes
++, from
++) {
2296 if ((c
= get_byte (view
, from
)) == -1)
2299 /* Save the cursor position for view_place_cursor() */
2300 if (from
== view
->hex_cursor
&& !view
->hexview_in_text
) {
2301 view
->cursor_row
= row
;
2302 view
->cursor_col
= col
;
2305 /* Determine the state of the current byte */
2307 (from
== view
->hex_cursor
) ? MARK_CURSOR
2308 : (curr
!= NULL
&& from
== curr
->offset
) ? MARK_CHANGED
2309 : (view
->search_start
<= from
&&
2310 from
< view
->search_end
) ? MARK_SELECTED
2313 /* Determine the value of the current byte */
2314 if (curr
!= NULL
&& from
== curr
->offset
) {
2319 /* Select the color for the hex number */
2321 boldflag
== MARK_NORMAL
? NORMAL_COLOR
:
2322 boldflag
== MARK_SELECTED
? MARKED_COLOR
:
2323 boldflag
== MARK_CHANGED
? VIEW_UNDERLINED_COLOR
:
2324 /* boldflag == MARK_CURSOR */
2325 view
->hexview_in_text
? MARKED_SELECTED_COLOR
:
2326 VIEW_UNDERLINED_COLOR
);
2328 /* Print the hex number */
2329 widget_move (view
, top
+ row
, left
+ col
);
2331 tty_print_char (hex_char
[c
/ 16]);
2335 tty_print_char (hex_char
[c
% 16]);
2339 /* Print the separator */
2340 tty_setcolor (NORMAL_COLOR
);
2341 if (bytes
!= view
->bytes_per_line
- 1) {
2343 tty_print_char (' ');
2347 /* After every four bytes, print a group separator */
2348 if (bytes
% 4 == 3) {
2349 if (view
->data_area
.width
>= 80 && col
< width
) {
2350 tty_print_one_vline ();
2354 tty_print_char (' ');
2360 /* Select the color for the character; this differs from the
2361 * hex color when boldflag == MARK_CURSOR */
2363 boldflag
== MARK_NORMAL
? NORMAL_COLOR
:
2364 boldflag
== MARK_SELECTED
? MARKED_COLOR
:
2365 boldflag
== MARK_CHANGED
? VIEW_UNDERLINED_COLOR
:
2366 /* boldflag == MARK_CURSOR */
2367 view
->hexview_in_text
? VIEW_UNDERLINED_COLOR
:
2368 MARKED_SELECTED_COLOR
);
2370 c
= convert_to_display_c (c
);
2371 if (!g_ascii_isprint (c
))
2374 /* Print corresponding character on the text side */
2375 if (text_start
+ bytes
< width
) {
2376 widget_move (view
, top
+ row
, left
+ text_start
+ bytes
);
2380 /* Save the cursor position for view_place_cursor() */
2381 if (from
== view
->hex_cursor
&& view
->hexview_in_text
) {
2382 view
->cursor_row
= row
;
2383 view
->cursor_col
= text_start
+ bytes
;
2388 /* Be polite to the other functions */
2389 tty_setcolor (NORMAL_COLOR
);
2391 view_place_cursor (view
);
2392 view
->dpy_end
= from
;
2396 view_display_text (WView
* view
)
2398 #define cmp(t1,t2) (strcmp((t1),(t2)) == 0)
2400 const screen_dimen left
= view
->data_area
.left
;
2401 const screen_dimen top
= view
->data_area
.top
;
2402 const screen_dimen width
= view
->data_area
.width
;
2403 const screen_dimen height
= view
->data_area
.height
;
2404 struct read_info info
;
2405 offset_type row
, col
;
2407 struct cache_line
*line_act
;
2408 struct cache_line
*line_nxt
;
2410 view_display_clean (view
);
2411 view_display_ruler (view
);
2413 tty_setcolor (NORMAL_COLOR
);
2415 widget_move (view
, top
, left
);
2417 line_act
= view_get_first_showed_line (view
);
2420 /* set col correct value */
2421 col
= (view
->text_wrap_mode
) ? 0 : view_width_of_whole_line_before (view
, line_act
);
2422 col
+= line_act
->left
;
2424 view_read_start (view
, &info
, line_act
->start
);
2425 while ((info
.result
!= -1) && (row
< height
)) {
2426 /* real detection of new line */
2427 if (info
.next
>= line_act
->end
) {
2428 line_nxt
= view_get_next_line (view
, line_act
);
2429 if (line_nxt
== NULL
) break;
2431 if (view
->text_wrap_mode
|| (line_act
->number
!= line_nxt
->number
)){
2433 col
= line_nxt
->left
;
2435 line_act
= line_nxt
;
2440 view_read_continue (view
, &info
);
2441 if (view_read_test_nroff_back (view
, &info
)) {
2444 w
= str_term_width1 (info
.chi1
);
2446 if (col
>= view
->dpy_text_column
2447 && col
+ w
- view
->dpy_text_column
<= width
) {
2449 widget_move (view
, top
+ row
, left
+ (col
- view
->dpy_text_column
));
2450 for (c
= 0; c
< w
; c
++) addch (' ');
2452 if (cmp (info
.chi1
, "_") && (!cmp (info
.cnxt
, "_") || !cmp (info
.chi2
, "\b")))
2453 tty_setcolor (VIEW_UNDERLINED_COLOR
);
2455 tty_setcolor (MARKED_COLOR
);
2459 if (view_read_test_new_line (view
, &info
))
2463 if (view_read_test_tabulator (view
, &info
)) {
2464 col
+= (8 - (col
% 8));
2468 if (view
->search_start
<= info
.actual
2469 && info
.actual
< view
->search_end
) {
2470 tty_setcolor (SELECTED_COLOR
);
2473 w
= str_isprint (info
.cact
) ? str_term_width1 (info
.cact
) : 1;
2475 if (col
>= view
->dpy_text_column
2476 && col
+ w
- view
->dpy_text_column
<= width
) {
2477 widget_move (view
, top
+ row
, left
+ (col
- view
->dpy_text_column
));
2479 if (!str_iscombiningmark (info
.cnxt
)) {
2480 if (str_isprint (info
.cact
)) {
2481 addstr (str_term_form (info
.cact
));
2486 GString
*comb
= g_string_new ("");
2487 if (str_isprint (info
.cact
)) {
2488 g_string_append(comb
,info
.cact
);
2490 g_string_append(comb
,".");
2492 while (str_iscombiningmark (info
.cnxt
)) {
2493 view_read_continue (view
, &info
);
2494 g_string_append(comb
,info
.cact
);
2496 addstr (str_term_form (comb
->str
));
2497 g_string_free (comb
, TRUE
);
2500 while (str_iscombiningmark (info
.cnxt
)) {
2501 view_read_continue (view
, &info
);
2506 tty_setcolor (NORMAL_COLOR
);
2508 view
->dpy_end
= info
.next
;
2511 /* Displays as much data from view->dpy_start as fits on the screen */
2513 display (WView
*view
)
2515 view_compute_areas (view
);
2516 if (view
->hex_mode
) {
2517 view_display_hex (view
);
2519 view_display_text (view
);
2521 view_display_status (view
);
2525 view_place_cursor (WView
*view
)
2527 const screen_dimen top
= view
->data_area
.top
;
2528 const screen_dimen left
= view
->data_area
.left
;
2531 col
= view
->cursor_col
;
2532 if (!view
->hexview_in_text
&& view
->hexedit_lownibble
)
2534 widget_move (&view
->widget
, top
+ view
->cursor_row
, left
+ col
);
2538 view_update (WView
*view
)
2540 static int dirt_limit
= 1;
2542 if (view
->dpy_bbar_dirty
) {
2543 view
->dpy_bbar_dirty
= FALSE
;
2545 buttonbar_redraw (view
->widget
.parent
);
2548 if (view
->dirty
> dirt_limit
) {
2549 /* Too many updates skipped -> force a update */
2552 /* Raise the update skipping limit */
2554 if (dirt_limit
> max_dirt_limit
)
2555 dirt_limit
= max_dirt_limit
;
2559 /* We have time to update the screen properly */
2565 /* We are busy -> skipping full update,
2566 only the status line is updated */
2567 view_display_status (view
);
2569 /* Here we had a refresh, if fast scrolling does not work
2570 restore the refresh, although this should not happen */
2574 /* {{{ Hex editor }}} */
2577 enqueue_change (struct hexedit_change_node
**head
,
2578 struct hexedit_change_node
*node
)
2580 /* chnode always either points to the head of the list or
2581 * to one of the ->next fields in the list. The value at
2582 * this location will be overwritten with the new node. */
2583 struct hexedit_change_node
**chnode
= head
;
2585 while (*chnode
!= NULL
&& (*chnode
)->offset
< node
->offset
)
2586 chnode
= &((*chnode
)->next
);
2588 node
->next
= *chnode
;
2593 view_handle_editkey (WView
*view
, int key
)
2595 struct hexedit_change_node
*node
;
2598 /* Has there been a change at this position? */
2599 node
= view
->change_list
;
2600 while (node
&& (node
->offset
!= view
->hex_cursor
))
2603 if (!view
->hexview_in_text
) {
2605 unsigned int hexvalue
= 0;
2607 if (key
>= '0' && key
<= '9')
2608 hexvalue
= 0 + (key
- '0');
2609 else if (key
>= 'A' && key
<= 'F')
2610 hexvalue
= 10 + (key
- 'A');
2611 else if (key
>= 'a' && key
<= 'f')
2612 hexvalue
= 10 + (key
- 'a');
2614 return MSG_NOT_HANDLED
;
2617 byte_val
= node
->value
;
2619 byte_val
= get_byte (view
, view
->hex_cursor
);
2621 if (view
->hexedit_lownibble
) {
2622 byte_val
= (byte_val
& 0xf0) | (hexvalue
);
2624 byte_val
= (byte_val
& 0x0f) | (hexvalue
<< 4);
2628 if (key
< 256 && (is_printable (key
) || (key
== '\n')))
2631 return MSG_NOT_HANDLED
;
2634 node
= g_new (struct hexedit_change_node
, 1);
2635 node
->offset
= view
->hex_cursor
;
2636 node
->value
= byte_val
;
2637 enqueue_change (&view
->change_list
, node
);
2639 node
->value
= byte_val
;
2643 view_move_right (view
, 1);
2648 view_hexedit_save_changes (WView
*view
)
2650 struct hexedit_change_node
*curr
, *next
;
2654 if (view
->change_list
== NULL
)
2658 assert (view
->filename
!= NULL
);
2659 fp
= mc_open (view
->filename
, O_WRONLY
);
2663 for (curr
= view
->change_list
; curr
!= NULL
; curr
= next
) {
2666 if (mc_lseek (fp
, curr
->offset
, SEEK_SET
) == -1
2667 || mc_write (fp
, &(curr
->value
), 1) != 1)
2670 /* delete the saved item from the change list */
2671 view
->change_list
= next
;
2673 view_set_byte (view
, curr
->offset
, curr
->value
);
2677 if (mc_close (fp
) == -1) {
2678 error
= g_strdup (strerror (errno
));
2679 message (D_ERROR
, _(" Save file "), _(
2680 " Error while closing the file: \n %s \n"
2681 " Data may have been written or not. "), error
);
2688 error
= g_strdup (strerror (errno
));
2689 text
= g_strdup_printf (_(" Cannot save file: \n %s "), error
);
2691 (void) mc_close (fp
);
2693 answer
= query_dialog (_(" Save file "), text
, D_ERROR
,
2694 2, _("&Retry"), _("&Cancel"));
2702 /* {{{ Miscellaneous functions }}} */
2705 view_ok_to_quit (WView
*view
)
2709 if (view
->change_list
== NULL
)
2712 r
= query_dialog (_("Quit"),
2713 _(" File was modified, Save with exit? "), D_NORMAL
, 3,
2714 _("&Cancel quit"), _("&Yes"), _("&No"));
2718 return view_hexedit_save_changes (view
);
2720 view_hexedit_free_change_list (view
);
2728 my_define (Dlg_head
*h
, int idx
, const char *text
, void (*fn
) (WView
*),
2731 buttonbar_set_label_data (h
, idx
, text
, (buttonbarfn
) fn
, view
);
2734 /* {{{ Searching }}} */
2736 /* read one whole line into buffer, return where line start and end */
2738 view_get_line_at (WView
*view
, offset_type from
, GString
* buffer
,
2739 offset_type
*buff_start
, offset_type
*buff_end
)
2741 #define cmp(t1,t2) (strcmp((t1),(t2)) == 0)
2742 struct read_info info
;
2743 struct cache_line
*line
;
2747 line
= view_get_first_showed_line (view
);
2749 line
= view_offset_to_line_from (view
, from
, line
);
2751 if (!view
->search_backwards
) {
2753 end
= view_get_end_of_whole_line (view
, line
)->end
;
2754 if (start
>= end
) return 0;
2756 start
= view_get_start_of_whole_line (view
, line
)->start
;
2760 (*buff_start
) = start
;
2763 g_string_set_size(buffer
,0);
2765 view_read_start (view
, &info
, start
);
2766 while ((info
.result
!= -1) && (info
.next
< end
)) {
2767 view_read_continue (view
, &info
);
2769 /* if text contains '\0' */
2770 if (cmp (info
.cact
, "")) {
2771 if (info
.actual
< from
) {
2772 /* '\0' before start offset, continue */
2773 g_string_set_size(buffer
,0);
2774 (*buff_start
) = info
.next
;
2777 /* '\0' after start offset, end */
2778 (*buff_end
) = info
.next
;
2783 if (view_read_test_new_line (view
, &info
))
2786 if (view_read_test_nroff_back (view
, &info
)) {
2787 g_string_truncate (buffer
, buffer
->len
-1);
2791 g_string_append(buffer
,info
.cact
);
2797 /* map search result positions to offsets in text */
2799 view_matchs_to_offsets (WView
*view
, offset_type start
, offset_type end
,
2800 size_t match_start
, size_t match_end
,
2801 offset_type
*search_start
, offset_type
*search_end
)
2803 struct read_info info
;
2806 (*search_start
) = INVALID_OFFSET
;
2807 (*search_end
) = INVALID_OFFSET
;
2809 view_read_start (view
, &info
, start
);
2811 while ((info
.result
!= -1) && (info
.next
< end
)) {
2812 view_read_continue (view
, &info
);
2814 if (view_read_test_nroff_back (view
, &info
)) {
2818 if ((c
== match_start
) && (*search_start
== INVALID_OFFSET
))
2819 *search_start
= info
.actual
;
2820 if (c
== match_end
) (*search_end
) = info
.actual
;
2821 c
+= !str_iscombiningmark (info
.cact
) || (c
== 0);
2824 if ((c
== match_start
) && (*search_start
== INVALID_OFFSET
)) *search_start
= info
.next
;
2825 if (c
== match_end
) (*search_end
) = info
.next
;
2828 /* we have set view->search_start and view->search_end and must set
2829 * view->dpy_text_column, view->first_showed_line and view->dpy_start
2830 * try to displaye maximum of match */
2832 view_moveto_match (WView
*view
)
2834 const screen_dimen height
= view
->data_area
.height
;
2835 const screen_dimen height3
= height
/ 3;
2836 const screen_dimen width
= view
->data_area
.width
;
2837 struct cache_line
*line
;
2838 struct cache_line
*line_end
, *line_start
;
2839 struct cache_line
*t
;
2844 line
= view_get_first_showed_line (view
);
2845 if (view
->text_wrap_mode
) {
2846 if (line
->start
> view
->search_start
) {
2847 if (line
->start
<= view
->search_start
&& line
->end
> view
->search_start
)
2849 if (line
->start
<= view
->search_end
&& line
->end
>= view
->search_end
)
2851 t
= view_get_previous_line (view
, line
);
2852 while ((t
!= NULL
) && ((start_off
== -1) || (end_off
== -1))) {
2854 t
= view_get_previous_line (view
, line
);
2856 if (line
->start
<= view
->search_start
&& line
->end
> view
->search_start
)
2858 if (line
->start
<= view
->search_end
&& line
->end
>= view
->search_end
)
2862 line
= view_get_first_showed_line (view
);
2864 off
= ((off_t
)(start_off
- end_off
) < (off_t
)(height
- height3
))
2865 ? (int)(start_off
+ height3
)
2867 for (;off
>= 0 && line
->start
> 0; off
--)
2868 line
= view_get_previous_line (view
, line
);
2870 /* start_off, end_off - how many cache_lines far are
2871 * view->search_start, end from line */
2872 if (line
->start
<= view
->search_start
&& line
->end
> view
->search_start
)
2874 if (line
->start
<= view
->search_end
&& line
->end
>= view
->search_end
)
2876 t
= view_get_next_line (view
, line
);
2877 while ((t
!= NULL
) && ((start_off
== -1) || (end_off
== -1))) {
2879 t
= view_get_next_line (view
, line
);
2881 if (line
->start
<= view
->search_start
&& line
->end
> view
->search_start
)
2883 if (line
->start
<= view
->search_end
&& line
->end
>= view
->search_end
)
2887 line
= view_get_first_showed_line (view
);
2888 /* if view->search_end is farther then screen heigth */
2889 if ((off_t
)end_off
>= height
) {
2890 off
= ((off_t
)(end_off
- start_off
) < (off_t
)(height
- height3
))
2891 ? (int) (end_off
- height
+ height3
)
2894 for (;off
>= 0; off
--)
2895 line
= view_get_next_line (view
, line
);
2899 /* first part similar like in wrap mode,only wokrs with whole lines */
2900 line
= view_get_first_showed_line (view
);
2901 line
= view_get_start_of_whole_line (view
, line
);
2902 if (line
->start
> view
->search_start
) {
2903 line_start
= view_get_start_of_whole_line (view
, line
);
2904 if (line_start
->start
<= view
->search_start
&& line
->end
> view
->search_start
)
2906 if (line_start
->start
<= view
->search_end
&& line
->end
>= view
->search_end
)
2908 t
= view_get_previous_whole_line (view
, line_start
);
2909 while ((t
!= NULL
) && ((start_off
== -1) || (end_off
== -1))) {
2911 line_start
= view_get_start_of_whole_line (view
, line
);
2912 t
= view_get_previous_whole_line (view
, line_start
);
2914 if (line_start
->start
<= view
->search_start
&& line
->end
> view
->search_start
)
2916 if (line_start
->start
<= view
->search_end
&& line
->end
>= view
->search_end
)
2920 line
= view_get_first_showed_line (view
);
2921 line
= view_get_start_of_whole_line (view
, line
);
2922 off
= ((off_t
)(start_off
- end_off
) < (off_t
)(height
- height3
))
2923 ? (int)(start_off
+ height3
)
2925 for (;off
>= 0 && line
->start
> 0; off
--) {
2926 line
= view_get_previous_whole_line (view
, line
);
2927 line
= view_get_start_of_whole_line (view
, line
);
2930 line_end
= view_get_end_of_whole_line (view
, line
);
2931 if (line
->start
<= view
->search_start
&& line_end
->end
> view
->search_start
)
2933 if (line
->start
<= view
->search_end
&& line_end
->end
>= view
->search_end
)
2935 t
= view_get_next_whole_line (view
, line_end
);
2936 while ((t
!= NULL
) && ((start_off
== -1) || (end_off
== -1))) {
2938 line_end
= view_get_end_of_whole_line (view
, line
);
2939 t
= view_get_next_whole_line (view
, line_end
);
2941 if (line
->start
<= view
->search_start
&& line_end
->end
> view
->search_start
)
2943 if (line
->start
<= view
->search_end
&& line_end
->end
>= view
->search_end
)
2947 line
= view_get_first_showed_line (view
);
2948 line
= view_get_start_of_whole_line (view
, line
);
2949 if ((off_t
)end_off
>= height
) {
2950 off
= ((off_t
)(end_off
- start_off
) < (off_t
)(height
- height3
))
2951 ? (int)(end_off
- height
+ height3
)
2954 for (;off
>= 0; off
--)
2955 line
= view_get_next_whole_line (view
, line
);
2958 /*now line point to begin of line, that we want show*/
2960 t
= view_offset_to_line_from (view
, view
->search_start
, line
);
2961 start_off
= view_offset_to_column (view
, t
, view
->search_start
);
2962 t
= view_offset_to_line_from (view
, view
->search_end
, line
);
2963 end_off
= view_offset_to_column (view
, t
, view
->search_end
);
2965 if ((off_t
)(end_off
- start_off
) > width
) end_off
= start_off
+ width
;
2966 if (view
->dpy_text_column
> (off_t
)start_off
) {
2967 view
->dpy_text_column
= start_off
;
2969 if (view
->dpy_text_column
+ width
< (off_t
)end_off
) {
2970 view
->dpy_text_column
= end_off
- width
;
2975 view_set_first_showed (view
, line
);
2979 search_update_steps (WView
*view
)
2981 offset_type filesize
= view_get_filesize (view
);
2983 view
->update_steps
= 40000;
2984 else /* viewing a data stream, not a file */
2985 view
->update_steps
= filesize
/ 100;
2987 /* Do not update the percent display but every 20 ks */
2988 if (view
->update_steps
< 20000)
2989 view
->update_steps
= 20000;
2992 /* {{{ User-definable commands }}} */
2995 The functions in this section can be bound to hotkeys. They are all
2996 of the same type (taking a pointer to WView as parameter and
2997 returning void). TODO: In the not-too-distant future, these commands
2998 will become fully configurable, like they already are in the
2999 internal editor. By convention, all the function names end in
3004 view_help_cmd (void)
3006 interactive_display (NULL
, "[Internal File Viewer]");
3009 /* Toggle between hexview and hexedit mode */
3011 view_toggle_hexedit_mode_cmd (WView
*view
)
3013 view_toggle_hexedit_mode (view
);
3017 /* Toggle between wrapped and unwrapped view */
3019 view_toggle_wrap_mode_cmd (WView
*view
)
3021 view_toggle_wrap_mode (view
);
3025 /* Toggle between hex view and text view */
3027 view_toggle_hex_mode_cmd (WView
*view
)
3029 view_toggle_hex_mode (view
);
3034 view_moveto_line_cmd (WView
*view
)
3036 char *answer
, *answer_end
, prompt
[BUF_SMALL
];
3037 struct cache_line
*line
;
3040 line
= view_get_first_showed_line (view
);
3041 row
= line
->number
+ 1;
3043 g_snprintf (prompt
, sizeof (prompt
),
3044 _(" The current line number is %lu.\n"
3045 " Enter the new line number:"), line
->number
);
3046 answer
= input_dialog (_(" Goto line "), prompt
, MC_HISTORY_VIEW_GOTO_LINE
, "");
3047 if (answer
!= NULL
&& answer
[0] != '\0') {
3049 row
= strtoul (answer
, &answer_end
, 10);
3050 if (*answer_end
== '\0' && errno
== 0 && row
>= 1)
3051 view_moveto (view
, row
- 1, 0);
3059 view_moveto_addr_cmd (WView
*view
)
3061 char *line
, *error
, prompt
[BUF_SMALL
];
3064 g_snprintf (prompt
, sizeof (prompt
),
3065 _(" The current address is 0x%08"OFFSETTYPE_PRIX
".\n"
3066 " Enter the new address:"), view
->hex_cursor
);
3068 line
= input_dialog (_(" Goto Address "), prompt
, MC_HISTORY_VIEW_GOTO_ADDR
, "");
3070 if (*line
!= '\0') {
3071 addr
= strtoul (line
, &error
, 0);
3072 if ((*error
== '\0') && get_byte (view
, addr
) != -1) {
3073 view_moveto_offset (view
, addr
);
3075 message (D_ERROR
, _("Warning"), _(" Invalid address "));
3085 view_hexedit_save_changes_cmd (WView
*view
)
3087 (void) view_hexedit_save_changes (view
);
3091 view__get_nroff_real_len(WView
*view
, offset_type start
, offset_type length
)
3093 offset_type loop1
= 0;
3095 struct read_info info
;
3097 view_read_start (view
, &info
, start
);
3098 while((loop1
< length
) && (info
.result
!= -1))
3100 view_read_continue (view
, &info
);
3101 if (*info
.cnxt
== '\b')
3103 view_read_continue (view
, &info
);
3104 view_read_continue (view
, &info
);
3114 do_search (WView
*view
)
3117 offset_type search_start
;
3121 offset_type line_start
;
3122 offset_type line_end
;
3126 d
= create_message (D_NORMAL
, _("Search"), _("Searching %s"), view
->last_search_string
);
3130 buffer
= g_string_new ("");
3132 search_start
= (view
->search_backwards
) ? view
->search_start
-1 : view
->search_end
;
3133 if (view
->search_backwards
&& (int) search_start
< 0 )
3136 /* Compute the percent steps */
3137 search_update_steps (view
);
3138 view
->update_activate
= 0;
3140 enable_interrupt_key ();
3144 if (search_start
>= view
->update_activate
) {
3145 view
->update_activate
+= view
->update_steps
;
3147 view_percent (view
, search_start
);
3150 if (got_interrupt ())
3154 if (!view_get_line_at (view
, search_start
, buffer
, &line_start
, &line_end
))
3157 if (! mc_search_run( view
->search
, buffer
->str
, 0, buffer
->len
, &match_len
)){
3158 if (view
->search
->error
!= MC_SEARCH_E_NOTFOUND
) {
3163 if (! view
->search_backwards
) {
3164 search_start
= line_end
;
3166 if (line_start
> 0) search_start
= line_start
- 1;
3172 if (view
->search_backwards
){
3173 search_start
= line_start
;
3176 view
->search_start
= search_start
+ view
->search
->normal_offset
+
3177 view__get_nroff_real_len(view
, search_start
, view
->search
->normal_offset
);
3179 view
->search_end
= view
->search_start
+ match_len
+
3180 view__get_nroff_real_len(view
, view
->search_start
, match_len
);
3182 if (view
->hex_mode
){
3183 view
->hex_cursor
= view
->search_start
;
3184 view
->hexedit_lownibble
= FALSE
;
3185 view
->dpy_start
= view
->search_start
- view
->search_start
% view
->bytes_per_line
;
3186 view
->dpy_end
= view
->search_end
- view
->search_end
% view
->bytes_per_line
;
3189 view_moveto_match (view
);
3193 disable_interrupt_key ();
3198 switch (search_status
)
3201 message (D_NORMAL
, _("Search"), _(" Search string not found "));
3202 view
->search_end
= view
->search_start
;
3205 message (D_NORMAL
, _("Search"), "%s", view
->search
->error_str
);
3206 view
->search_end
= view
->search_start
;
3209 g_string_free (buffer
, TRUE
);
3218 view_search_cmd (WView
*view
)
3221 SEARCH_DLG_MIN_HEIGHT
= 10,
3222 SEARCH_DLG_HEIGHT_SUPPLY
= 3,
3223 SEARCH_DLG_WIDTH
= 58
3226 char *defval
= g_strdup (view
->last_search_string
!= NULL
? view
->last_search_string
: "");
3229 int ttype_of_search
= (int) view
->search_type
;
3230 int tall_codepages
= (int) view
->search_all_codepages
;
3231 int tsearch_case
= (int) view
->search_case
;
3232 int tsearch_backwards
= (int) view
->search_backwards
;
3234 gchar
**list_of_types
= mc_search_get_types_strings_array();
3235 int SEARCH_DLG_HEIGHT
= SEARCH_DLG_MIN_HEIGHT
+ g_strv_length (list_of_types
) - SEARCH_DLG_HEIGHT_SUPPLY
;
3237 QuickWidget quick_widgets
[] = {
3239 {quick_button
, 6, 10, SEARCH_DLG_HEIGHT
- 3, SEARCH_DLG_HEIGHT
, N_("&Cancel"), 0,
3240 B_CANCEL
, 0, 0, NULL
, NULL
, NULL
},
3242 {quick_button
, 2, 10, SEARCH_DLG_HEIGHT
- 3, SEARCH_DLG_HEIGHT
, N_("&OK"), 0, B_ENTER
,
3243 0, 0, NULL
, NULL
, NULL
},
3246 {quick_checkbox
, SEARCH_DLG_WIDTH
/2 + 3, SEARCH_DLG_WIDTH
, 6, SEARCH_DLG_HEIGHT
, N_("All charsets"), 0, 0,
3247 &tall_codepages
, 0, NULL
, NULL
, NULL
},
3250 {quick_checkbox
, SEARCH_DLG_WIDTH
/2 + 3, SEARCH_DLG_WIDTH
, 5, SEARCH_DLG_HEIGHT
,
3251 N_("&Backwards"), 0, 0, &tsearch_backwards
, 0, NULL
, NULL
, NULL
},
3253 {quick_checkbox
, SEARCH_DLG_WIDTH
/2 + 3, SEARCH_DLG_WIDTH
, 4, SEARCH_DLG_HEIGHT
, N_("case &Sensitive"), 0, 0,
3254 &tsearch_case
, 0, NULL
, NULL
, NULL
},
3256 {quick_radio
, 3, SEARCH_DLG_WIDTH
, 4, SEARCH_DLG_HEIGHT
, 0, g_strv_length (list_of_types
), ttype_of_search
,
3257 (void *) &ttype_of_search
, const_cast (char **, list_of_types
), NULL
, NULL
, NULL
},
3260 {quick_input
, 3, SEARCH_DLG_WIDTH
, 3, SEARCH_DLG_HEIGHT
, defval
, 52, 0,
3261 0, &exp
, N_("Search"), NULL
, NULL
},
3263 {quick_label
, 2, SEARCH_DLG_WIDTH
, 2, SEARCH_DLG_HEIGHT
,
3264 N_(" Enter search string:"), 0, 0, 0, 0, 0, NULL
, NULL
},
3269 QuickDialog Quick_input
= {
3270 SEARCH_DLG_WIDTH
, SEARCH_DLG_HEIGHT
, -1, 0, N_("Search"),
3271 "[Input Line Keys]", quick_widgets
, 0
3274 convert_to_display (defval
);
3277 if (quick_dialog (&Quick_input
) == B_CANCEL
)
3280 view
->search_backwards
= tsearch_backwards
;
3281 view
->search_type
= (mc_search_type_t
) ttype_of_search
;
3283 view
->search_all_codepages
= (gboolean
) tall_codepages
;
3284 view
->search_case
= (gboolean
) tsearch_case
;
3286 if (exp
== NULL
|| exp
[0] == '\0')
3289 convert_from_input (exp
);
3291 g_free (view
->last_search_string
);
3292 view
->last_search_string
= exp
;
3296 mc_search_free(view
->search
);
3298 view
->search
= mc_search_new(view
->last_search_string
, -1);
3302 view
->search
->search_type
= view
->search_type
;
3303 view
->search
->is_all_charsets
= view
->search_all_codepages
;
3304 view
->search
->is_case_sentitive
= view
->search_case
;
3314 view_toggle_magic_mode_cmd (WView
*view
)
3316 view_toggle_magic_mode (view
);
3321 view_toggle_nroff_mode_cmd (WView
*view
)
3323 view_toggle_nroff_mode (view
);
3328 view_quit_cmd (WView
*view
)
3330 if (view_ok_to_quit (view
))
3331 dlg_stop (view
->widget
.parent
);
3334 /* {{{ Miscellaneous functions }}} */
3336 /* Define labels and handlers for functional keys */
3338 view_labels (WView
*view
)
3341 Dlg_head
*h
= view
->widget
.parent
;
3343 buttonbar_set_label (h
, 1, Q_("ButtonBar|Help"), view_help_cmd
);
3345 my_define (h
, 10, Q_("ButtonBar|Quit"), view_quit_cmd
, view
);
3346 text
= view
->hex_mode
? "ButtonBar|Ascii" : "ButtonBar|Hex";
3347 my_define (h
, 4, Q_(text
), view_toggle_hex_mode_cmd
, view
);
3348 text
= view
->hex_mode
?"ButtonBar|Goto": "ButtonBar|Line";
3349 my_define (h
, 5, Q_(text
),
3350 view
->hex_mode
? view_moveto_addr_cmd
: view_moveto_line_cmd
, view
);
3352 if (view
->hex_mode
) {
3353 if (view
->hexedit_mode
) {
3354 my_define (h
, 2, Q_("ButtonBar|View"),
3355 view_toggle_hexedit_mode_cmd
, view
);
3356 } else if (view
->datasource
== DS_FILE
) {
3357 my_define (h
, 2, Q_("ButtonBar|Edit"),
3358 view_toggle_hexedit_mode_cmd
, view
);
3360 buttonbar_clear_label (h
, 2);
3362 my_define (h
, 6, Q_("ButtonBar|Save"),
3363 view_hexedit_save_changes_cmd
, view
);
3365 text
= view
->text_wrap_mode
? "ButtonBar|UnWrap" : "ButtonBar|Wrap";
3366 my_define (h
, 2, Q_(text
), view_toggle_wrap_mode_cmd
, view
);
3369 text
= view
->hex_mode
? "ButtonBar|HxSrch" : "ButtonBar|Search";
3370 my_define (h
, 7, Q_(text
), view_search_cmd
, view
);
3371 text
= view
->magic_mode
? "ButtonBar|Raw" : "ButtonBar|Parse";
3372 my_define (h
, 8, Q_(text
), view_toggle_magic_mode_cmd
, view
);
3374 /* don't override the key to access the main menu */
3375 if (!view_is_in_panel (view
)) {
3376 text
= view
->text_nroff_mode
? "ButtonBar|Unform" : "ButtonBar|Format";
3377 my_define (h
, 9, Q_(text
), view_toggle_nroff_mode_cmd
, view
);
3378 my_define (h
, 3, Q_("ButtonBar|Quit"), view_quit_cmd
, view
);
3382 /* {{{ Event handling }}} */
3384 /* Check for left and right arrows, possibly with modifiers */
3386 check_left_right_keys (WView
*view
, int c
)
3388 if (c
== KEY_LEFT
) {
3389 view_move_left (view
, 1);
3393 if (c
== KEY_RIGHT
) {
3394 view_move_right (view
, 1);
3398 /* Ctrl with arrows moves by 10 postions in the unwrap mode */
3399 if (view
->hex_mode
|| view
->text_wrap_mode
)
3400 return MSG_NOT_HANDLED
;
3402 if (c
== (KEY_M_CTRL
| KEY_LEFT
)) {
3403 if (view
->dpy_text_column
>= 10)
3404 view
->dpy_text_column
-= 10;
3406 view
->dpy_text_column
= 0;
3411 if (c
== (KEY_M_CTRL
| KEY_RIGHT
)) {
3412 if (view
->dpy_text_column
<= OFFSETTYPE_MAX
- 10)
3413 view
->dpy_text_column
+= 10;
3415 view
->dpy_text_column
= OFFSETTYPE_MAX
;
3420 return MSG_NOT_HANDLED
;
3423 /* {{{ User-definable commands }}} */
3426 view_continue_search_cmd (WView
*view
)
3428 if (view
->last_search_string
!=NULL
) {
3431 /* if not... then ask for an expression */
3432 view_search_cmd (view
);
3437 view_toggle_ruler_cmd (WView
*view
)
3439 static const enum ruler_type next
[3] = {
3445 assert ((size_t) ruler
< 3);
3446 ruler
= next
[(size_t) ruler
];
3450 /* {{{ Event handling }}} */
3452 static void view_cmk_move_up (void *w
, int n
) {
3453 view_move_up ((WView
*) w
, n
);
3455 static void view_cmk_move_down (void *w
, int n
) {
3456 view_move_down ((WView
*) w
, n
);
3458 static void view_cmk_moveto_top (void *w
, int n
) {
3460 view_moveto_top ((WView
*) w
);
3462 static void view_cmk_moveto_bottom (void *w
, int n
) {
3464 view_moveto_bottom ((WView
*) w
);
3468 view_select_encoding (WView
*view
)
3473 if (!do_select_codepage ())
3476 enc
= get_codepage_id (source_codepage
);
3479 struct cache_line
*line
;
3481 conv
= str_crt_conv_from (enc
);
3482 if (conv
!= INVALID_CONV
) {
3483 if (view
->converter
!= str_cnv_from_term
)
3484 str_close_conv (view
->converter
);
3485 view
->converter
= conv
;
3486 view_reset_cache_lines (view
);
3487 line
= view_offset_to_line (view
, view
->dpy_start
);
3488 view_set_first_showed (view
, line
);
3497 view_handle_key (WView
*view
, int c
)
3499 c
= convert_from_input_c (c
);
3501 if (view
->hex_mode
) {
3504 view
->hexview_in_text
= !view
->hexview_in_text
;
3509 view_moveto_bol (view
);
3514 view_move_left (view
, 1);
3518 view_moveto_eol (view
);
3522 view_move_right (view
, 1);
3526 if (view
->hexedit_mode
3527 && view_handle_editkey (view
, c
) == MSG_HANDLED
)
3531 if (check_left_right_keys (view
, c
))
3534 if (check_movement_keys (c
, view
->data_area
.height
+ 1, view
,
3535 view_cmk_move_up
, view_cmk_move_down
,
3536 view_cmk_moveto_top
, view_cmk_moveto_bottom
))
3543 view
->search_type
= MC_SEARCH_T_REGEX
;
3544 view_search_cmd(view
);
3547 /* Continue search */
3552 view_continue_search_cmd (view
);
3557 view_toggle_ruler_cmd (view
);
3561 view_move_left (view
, 1);
3567 view_move_down (view
, 1);
3571 view_move_down (view
, (view
->data_area
.height
+ 1) / 2);
3575 view_move_up (view
, (view
->data_area
.height
+ 1) / 2);
3580 view_move_up (view
, 1);
3584 view_move_right (view
, 1);
3589 view_move_down (view
, view
->data_area
.height
);
3596 /* Unlike Ctrl-O, run a new shell if the subshell is not running. */
3602 view_move_up (view
, view
->data_area
.height
);
3606 view_move_up (view
, 2);
3610 view_move_down (view
, 2);
3614 view
->marks
[view
->marker
] = view
->dpy_start
;
3618 view
->dpy_start
= view
->marks
[view
->marker
];
3622 /* Use to indicate parent that we want to see the next/previous file */
3623 /* Does not work in panel mode */
3626 if (!view_is_in_panel (view
))
3627 view
->move_dir
= c
== XCTRL ('f') ? 1 : -1;
3632 if (view_ok_to_quit (view
))
3633 view
->want_to_quit
= TRUE
;
3637 view_select_encoding (view
);
3642 #ifdef MC_ENABLE_DEBUGGING_CODE
3643 case 't': /* mnemonic: "test" */
3644 view_ccache_dump (view
);
3648 if (c
>= '0' && c
<= '9')
3649 view
->marker
= c
- '0';
3652 return MSG_NOT_HANDLED
;
3657 view_event (WView
*view
, Gpm_Event
*event
, int *result
)
3661 *result
= MOU_NORMAL
;
3663 /* We are not interested in the release events */
3664 if (!(event
->type
& (GPM_DOWN
| GPM_DRAG
)))
3668 if ((event
->buttons
& GPM_B_UP
) && (event
->type
& GPM_DOWN
)) {
3669 view_move_up (view
, 2);
3672 if ((event
->buttons
& GPM_B_DOWN
) && (event
->type
& GPM_DOWN
)) {
3673 view_move_down (view
, 2);
3680 /* Scrolling left and right */
3681 if (!view
->text_wrap_mode
) {
3682 if (x
< view
->data_area
.width
* 1/4) {
3683 view_move_left (view
, 1);
3685 } else if (x
< view
->data_area
.width
* 3/4) {
3686 /* ignore the click */
3688 view_move_right (view
, 1);
3693 /* Scrolling up and down */
3694 if (y
< view
->data_area
.top
+ view
->data_area
.height
* 1/3) {
3695 if (mouse_move_pages_viewer
)
3696 view_move_up (view
, view
->data_area
.height
/ 2);
3698 view_move_up (view
, 1);
3700 } else if (y
< view
->data_area
.top
+ view
->data_area
.height
* 2/3) {
3701 /* ignore the click */
3703 if (mouse_move_pages_viewer
)
3704 view_move_down (view
, view
->data_area
.height
/ 2);
3706 view_move_down (view
, 1);
3713 *result
= MOU_REPEAT
;
3717 /* Real view only */
3719 real_view_event (Gpm_Event
*event
, void *x
)
3721 WView
*view
= (WView
*) x
;
3724 if (view_event (view
, event
, &result
))
3730 view_adjust_size (Dlg_head
*h
)
3735 /* Look up the viewer and the buttonbar, we assume only two widgets here */
3736 view
= (WView
*) find_widget_type (h
, view_callback
);
3737 bar
= find_buttonbar (h
);
3738 widget_set_size (&view
->widget
, 0, 0, LINES
- 1, COLS
);
3739 widget_set_size ((Widget
*) bar
, LINES
- 1, 0, 1, COLS
);
3741 view_compute_areas (view
);
3742 view_update_bytes_per_line (view
);
3745 /* Callback for the view dialog */
3747 view_dialog_callback (Dlg_head
*h
, dlg_msg_t msg
, int parm
)
3751 view_adjust_size (h
);
3755 return default_dlg_callback (h
, msg
, parm
);
3759 /* {{{ External interface }}} */
3761 /* Real view only */
3763 mc_internal_viewer (const char *command
, const char *file
,
3764 int *move_dir_p
, int start_line
)
3771 /* Create dialog and widgets, put them on the dialog */
3773 create_dlg (0, 0, LINES
, COLS
, NULL
, view_dialog_callback
,
3774 "[Internal File Viewer]", NULL
, DLG_WANT_TAB
);
3776 wview
= view_new (0, 0, COLS
, LINES
- 1, 0);
3778 bar
= buttonbar_new (1);
3780 add_widget (view_dlg
, bar
);
3781 add_widget (view_dlg
, wview
);
3783 succeeded
= view_load (wview
, command
, file
, start_line
);
3787 *move_dir_p
= wview
->move_dir
;
3792 destroy_dlg (view_dlg
);
3797 /* {{{ Miscellaneous functions }}} */
3802 WView
*view
= (WView
*) v
;
3805 /* If the user is busy typing, wait until he finishes to update the
3808 if (!hook_present (idle_hook
, view_hook
))
3809 add_hook (&idle_hook
, view_hook
, v
);
3813 delete_hook (&idle_hook
, view_hook
);
3815 if (get_current_type () == view_listing
)
3816 panel
= current_panel
;
3817 else if (get_other_type () == view_listing
)
3818 panel
= other_panel
;
3822 view_load (view
, 0, panel
->dir
.list
[panel
->selected
].fname
, 0);
3826 /* {{{ Event handling }}} */
3829 view_callback (Widget
*w
, widget_msg_t msg
, int parm
)
3831 WView
*view
= (WView
*) w
;
3833 Dlg_head
*h
= view
->widget
.parent
;
3835 view_compute_areas (view
);
3836 view_update_bytes_per_line (view
);
3840 if (view_is_in_panel (view
))
3841 add_hook (&select_file_hook
, view_hook
, view
);
3843 view
->dpy_bbar_dirty
= TRUE
;
3852 view_place_cursor (view
);
3856 i
= view_handle_key ((WView
*) view
, parm
);
3857 if (view
->want_to_quit
&& !view_is_in_panel (view
))
3865 view
->dpy_bbar_dirty
= TRUE
;
3869 case WIDGET_DESTROY
:
3871 if (view_is_in_panel (view
))
3872 delete_hook (&select_file_hook
, view_hook
);
3876 return default_proc (msg
, parm
);
3880 /* {{{ External interface }}} */
3883 view_new (int y
, int x
, int cols
, int lines
, int is_panel
)
3885 WView
*view
= g_new0 (WView
, 1);
3888 init_widget (&view
->widget
, y
, x
, lines
, cols
,
3892 view
->filename
= NULL
;
3893 view
->command
= NULL
;
3895 view_set_datasource_none (view
);
3897 view
->growbuf_in_use
= FALSE
;
3898 /* leave the other growbuf fields uninitialized */
3900 view
->hex_mode
= FALSE
;
3901 view
->hexedit_mode
= FALSE
;
3902 view
->hexview_in_text
= FALSE
;
3903 view
->text_nroff_mode
= FALSE
;
3904 view
->text_wrap_mode
= FALSE
;
3905 view
->magic_mode
= FALSE
;
3907 view
->hexedit_lownibble
= FALSE
;
3909 view
->dpy_frame_size
= is_panel
? 1 : 0;
3910 view
->dpy_start
= 0;
3911 view
->dpy_text_column
= 0;
3913 view
->hex_cursor
= 0;
3914 view
->cursor_col
= 0;
3915 view
->cursor_row
= 0;
3916 view
->change_list
= NULL
;
3917 view
->converter
= str_cnv_from_term
;
3919 /* {status,ruler,data}_area are left uninitialized */
3922 view
->dpy_bbar_dirty
= TRUE
;
3923 view
->bytes_per_line
= 1;
3925 view
->search_start
= 0;
3926 view
->search_end
= 0;
3928 view
->want_to_quit
= FALSE
;
3930 for (i
= 0; i
< sizeof(view
->marks
) / sizeof(view
->marks
[0]); i
++)
3934 view
->update_steps
= 0;
3935 view
->update_activate
= 0;
3937 if (default_hex_mode
)
3938 view_toggle_hex_mode (view
);
3939 if (default_nroff_flag
)
3940 view_toggle_nroff_mode (view
);
3941 if (global_wrap_mode
)
3942 view_toggle_wrap_mode (view
);
3943 if (default_magic_flag
)
3944 view_toggle_magic_mode (view
);