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
45 #include <sys/types.h>
51 #include "cmd.h" /* For view_other_cmd */
52 #include "dialog.h" /* Needed by widget.h */
53 #include "widget.h" /* Needed for buttonbar_new */
57 #include "key.h" /* For mi_getch() */
60 #include "wtools.h" /* For query_set_sel() */
62 #include "panel.h" /* Needed for current_panel and other_panel */
65 #include "main.h" /* slow_terminal */
69 #include "selcodepage.h"
71 #include "../src/search/search.h"
73 /* Block size for reading files in parts */
74 #define VIEW_PAGE_SIZE ((size_t) 8192)
76 typedef unsigned char byte
;
78 /* Offset in bytes into a file */
79 typedef unsigned long offset_type
;
80 #define INVALID_OFFSET ((offset_type) -1)
81 #define OFFSETTYPE_MAX (~((offset_type) 0))
82 #define OFFSETTYPE_PRIX "lX"
83 #define OFFSETTYPE_PRId "lu"
85 /* A width or height on the screen */
86 typedef unsigned int screen_dimen
;
88 /* A node for building a change list on change_list */
89 struct hexedit_change_node
{
90 struct hexedit_change_node
*next
;
95 /* data sources of the view */
97 DS_NONE
, /* No data available */
98 DS_STDIO_PIPE
, /* Data comes from a pipe using popen/pclose */
99 DS_VFS_PIPE
, /* Data comes from a piped-in VFS file */
100 DS_FILE
, /* Data comes from a VFS file */
101 DS_STRING
/* Data comes from a string in memory */
105 screen_dimen top
, left
;
106 screen_dimen height
, width
;
109 #define VLF_DISCARD 1
111 #define VLF_COMPLETE 3
113 /* basic structure for caching text, it correspond to one line in wrapped text.
114 * That makes easier to move in wrap mode.
115 * cache_lines are stored in two linked lists, one for normal mode
116 * and one for nroff mode
117 * cache_line is valid of end is not INVALID_OFFSET
118 * last line has set width to (screen_dimen) (-1)*/
119 /* never access next and previous in cache_line directly, use appropriately function
122 /* number of line in text, so two cache_line have same number
123 * if they are on same text line (text line is wider than screen) */
124 unsigned long number
;
125 /* previous cache_line in list*/
126 struct cache_line
*prev
;
127 /* next cache_line in list*/
128 struct cache_line
*next
;
129 /* offset when cache_line start in text,
130 * cache_line.start = cache_line->prev.end */
132 /* offset when cache_line ends
133 * cache_line.end = cache_line->next.start */
135 /* how many column take on screen */
137 /* correct ident, if prevoius line ends by tabulator */
144 char *filename
; /* Name of the file */
145 char *command
; /* Command used to pipe data in */
147 enum view_ds datasource
; /* Where the displayed data comes from */
149 /* stdio pipe data source */
150 FILE *ds_stdio_pipe
; /* Output of a shell command */
152 /* vfs pipe data source */
153 int ds_vfs_pipe
; /* Non-seekable vfs file descriptor */
155 /* vfs file data source */
156 int ds_file_fd
; /* File with random access */
157 off_t ds_file_filesize
; /* Size of the file */
158 off_t ds_file_offset
; /* Offset of the currently loaded data */
159 byte
*ds_file_data
; /* Currently loaded data */
160 size_t ds_file_datalen
; /* Number of valid bytes in file_data */
161 size_t ds_file_datasize
; /* Number of allocated bytes in file_data */
163 /* string data source */
164 byte
*ds_string_data
; /* The characters of the string */
165 size_t ds_string_len
; /* The length of the string */
167 /* Growing buffers information */
168 gboolean growbuf_in_use
; /* Use the growing buffers? */
169 byte
**growbuf_blockptr
; /* Pointer to the block pointers */
170 size_t growbuf_blocks
; /* The number of blocks in *block_ptr */
171 size_t growbuf_lastindex
; /* Number of bytes in the last page of the
173 gboolean growbuf_finished
; /* TRUE when all data has been read. */
176 gboolean hex_mode
; /* Hexview or Hexedit */
177 gboolean hexedit_mode
; /* Hexedit */
178 gboolean hexview_in_text
; /* Is the hexview cursor in the text area? */
179 gboolean text_nroff_mode
; /* Nroff-style highlighting */
180 gboolean text_wrap_mode
; /* Wrap text lines to fit them on the screen */
181 gboolean magic_mode
; /* Preprocess the file using external programs */
183 /* Additional editor state */
184 gboolean hexedit_lownibble
; /* Are we editing the last significant nibble? */
186 /* Display information */
187 screen_dimen dpy_frame_size
;/* Size of the frame surrounding the real viewer */
188 offset_type dpy_start
; /* Offset of the displayed data */
189 offset_type dpy_end
; /* Offset after the displayed data */
190 offset_type dpy_text_column
;/* Number of skipped columns in non-wrap
192 offset_type hex_cursor
; /* Hexview cursor position in file */
193 screen_dimen cursor_col
; /* Cursor column */
194 screen_dimen cursor_row
; /* Cursor row */
195 struct hexedit_change_node
*change_list
; /* Linked list of changes */
196 struct area status_area
; /* Where the status line is displayed */
197 struct area ruler_area
; /* Where the ruler is displayed */
198 struct area data_area
; /* Where the data is displayed */
200 int dirty
; /* Number of skipped updates */
201 gboolean dpy_bbar_dirty
; /* Does the button bar need to be updated? */
204 int bytes_per_line
; /* Number of bytes per line in hex mode */
206 /* Search variables */
207 offset_type search_start
; /* First character to start searching from */
208 offset_type search_end
; /* Length of found string or 0 if none was found */
210 /* Pointer to the last search command */
211 gboolean want_to_quit
; /* Prepare for cleanup ... */
214 int marker
; /* mark to use */
215 offset_type marks
[10]; /* 10 marks: 0..9 */
217 int move_dir
; /* return value from widget:
219 * -1 view previous file
223 offset_type update_steps
; /* The number of bytes between percent
225 offset_type update_activate
;/* Last point where we updated the status */
227 /* never access cache_line in view directly, use appropriately function
229 /* first cache_line for normal mode */
230 struct cache_line
*lines
;
231 /* last cache_line for normal mode, is set when text is read to the end */
232 struct cache_line
*lines_end
;
233 /* first cache_line for nroff mode */
234 struct cache_line
*nroff_lines
;
235 /* last cache_line for nroff mode, is set when text is read to the end */
236 struct cache_line
*nroff_lines_end
;
237 /* cache_line, that is first showed on display (something like cursor)
238 * used for both normal adn nroff mode */
239 struct cache_line
*first_showed_line
;
240 /* converter for translation of text */
243 /* handle of search engine */
245 gchar
*last_search_string
;
246 mc_search_type_t search_type
;
247 gboolean search_all_codepages
;
248 gboolean search_case
;
249 gboolean search_backwards
;
253 /* {{{ Global Variables }}} */
255 /* Maxlimit for skipping updates */
256 int max_dirt_limit
= 10;
258 /* If set, show a ruler */
259 static enum ruler_type
{
263 } ruler
= RULER_NONE
;
265 /* Scrolling is done in pages or line increments */
266 int mouse_move_pages_viewer
= 1;
268 /* wrap mode default */
269 int global_wrap_mode
= 1;
271 int default_hex_mode
= 0;
272 int default_magic_flag
= 1;
273 int default_nroff_flag
= 1;
274 int altered_hex_mode
= 0;
275 int altered_magic_flag
= 0;
276 int altered_nroff_flag
= 0;
278 static const char hex_char
[] = "0123456789ABCDEF";
280 int mcview_remember_file_position
= FALSE
;
282 /* {{{ Function Prototypes }}} */
284 /* Our widget callback */
285 static cb_ret_t
view_callback (Widget
*, widget_msg_t
, int);
287 static void view_labels (WView
* view
);
289 static void view_init_growbuf (WView
*);
290 static void view_place_cursor (WView
*view
);
291 static void display (WView
*);
292 static void view_done (WView
*);
294 /* {{{ Helper Functions }}} */
296 /* difference or zero */
297 static inline screen_dimen
298 dimen_doz (screen_dimen a
, screen_dimen b
)
300 return (a
>= b
) ? a
- b
: 0;
303 static inline screen_dimen
304 dimen_min (screen_dimen a
, screen_dimen b
)
306 return (a
< b
) ? a
: b
;
309 static inline offset_type
310 offset_doz (offset_type a
, offset_type b
)
312 return (a
>= b
) ? a
- b
: 0;
315 static inline offset_type
316 offset_rounddown (offset_type a
, offset_type b
)
322 /* {{{ Simple Primitive Functions for WView }}} */
324 static inline gboolean
325 view_is_in_panel (WView
*view
)
327 return (view
->dpy_frame_size
!= 0);
330 static inline int get_byte (WView
*view
, offset_type offset
);
332 /* read on character from text, character is translated into terminal encoding
333 * writes result in ch, return how many bytes was read or -1 if end of text */
335 view_get_char (WView
*view
, offset_type from
, char *ch
, int size
)
337 static char buffer
[MB_LEN_MAX
+ 1];
339 static size_t result
;
343 while (result
< sizeof (buffer
) - 1) {
344 c
= get_byte (view
, from
+ result
);
346 buffer
[result
] = (unsigned char) c
;
348 buffer
[result
] = '\0';
349 switch (str_translate_char (view
->converter
, buffer
, result
, ch
, size
)) {
363 /* this structure and view_read_* functions make reading text simpler
364 * view need remeber 4 following charsets: actual, next and two previous */
366 char ch
[4][MB_LEN_MAX
+ 1];
376 /* set read_info into initial state and read first character to cnxt
377 * return how many bytes was read or -1 if end of text */
379 view_read_start (WView
*view
, struct read_info
*info
, offset_type from
)
381 info
->ch
[0][0] = '\0';
382 info
->ch
[1][0] = '\0';
383 info
->ch
[2][0] = '\0';
384 info
->ch
[3][0] = '\0';
386 info
->cnxt
= info
->ch
[0];
387 info
->cact
= info
->ch
[1];
388 info
->chi1
= info
->ch
[2];
389 info
->chi2
= info
->ch
[3];
393 info
->result
= view_get_char (view
, info
->next
, info
->cnxt
, MB_LEN_MAX
+ 1);
396 /* move characters in read_info one forward (chi2 = last previous is forgotten)
397 * read additional charsets into cnxt
398 * return how many bytes was read or -1 if end of text */
400 view_read_continue (WView
*view
, struct read_info
*info
)
405 info
->chi2
= info
->chi1
;
406 info
->chi1
= info
->cact
;
407 info
->cact
= info
->cnxt
;
410 info
->actual
= info
->next
;
411 info
->next
+= info
->result
;
413 info
->result
= view_get_char (view
, info
->next
, info
->cnxt
, MB_LEN_MAX
+ 1);
418 #define VRT_NW_CONTINUE 2
420 /* test if cact of read_info is newline
421 * return VRT_NW_NO, VRT_NW_YES
422 * or VRT_NW_CONTINUE follow after cact ("\r\n", ...) */
424 view_read_test_new_line (WView
*view
, struct read_info
*info
)
426 #define cmp(t1,t2) (strcmp((t1),(t2)) == 0)
428 if (cmp (info
->cact
, "\n")) return VRT_NW_YES
;
430 if (cmp (info
->cact
, "\r")) {
431 if (info
->result
!= -1 && (cmp (info
->cnxt
, "\r") || cmp (info
->cnxt
, "\n")))
432 return VRT_NW_CONTINUE
;
439 /* test if cact of read_info is tabulator
440 * return VRT_NW_NO or VRT_NW_YES */
442 view_read_test_tabulator (WView
*view
, struct read_info
*info
)
444 #define cmp(t1,t2) (strcmp((t1),(t2)) == 0)
446 return cmp (info
->cact
, "\t");
449 /* test if read_info.cact is '\b' and charsets in read_info are correct
450 * nroff sequence, return VRT_NW_NO or VRT_NW_YES */
452 view_read_test_nroff_back (WView
*view
, struct read_info
*info
)
454 #define cmp(t1,t2) (strcmp((t1),(t2)) == 0)
455 return view
->text_nroff_mode
&& cmp (info
->cact
, "\b") &&
456 (info
->result
!= -1) && str_isprint (info
->cnxt
) &&
457 !cmp (info
->chi1
, "") && str_isprint (info
->chi1
) &&
458 (cmp (info
->chi1
, info
->cnxt
) || cmp (info
->chi1
, "_")
459 || (cmp (info
->chi1
, "+") && cmp (info
->cnxt
, "o")));
463 /* rutine for view_load_cache_line, line is ended and following cache_line is
465 * used when end of line has been read in text */
466 static struct cache_line
*
467 view_lc_create_next_line (struct cache_line
*line
, offset_type start
)
469 struct cache_line
*result
= NULL
;
473 if (line
->next
== NULL
) {
474 result
= g_new0 (struct cache_line
, 1);
477 } else result
= line
->next
;
479 result
->start
= start
;
482 result
->number
= line
->number
+ 1;
483 result
->end
= INVALID_OFFSET
;
488 /* rutine for view_load_cache_line, line is ended and following cache_line is
490 * used when line has maximum width */
491 static struct cache_line
*
492 view_lc_create_wrap_line (struct cache_line
*line
, offset_type start
,
495 struct cache_line
*result
= NULL
;
499 if (line
->next
== NULL
) {
500 result
= g_new0 (struct cache_line
, 1);
503 } else result
= line
->next
;
505 result
->start
= start
;
508 result
->number
= line
->number
;
509 result
->end
= INVALID_OFFSET
;
514 /* read charsets fro text and set up correct width, end and start of next line
515 * view->data_area.width must be greater than 0 */
516 static struct cache_line
*
517 view_load_cache_line (WView
*view
, struct cache_line
*line
)
519 const screen_dimen width
= view
->data_area
.width
;
520 struct read_info info
;
521 offset_type nroff_start
= 0;
527 view_read_start (view
, &info
, line
->start
);
528 while (info
.result
!= -1) {
529 view_read_continue (view
, &info
);
531 switch (view_read_test_new_line (view
, &info
)) {
533 line
= view_lc_create_next_line (line
, info
.next
);
535 case VRT_NW_CONTINUE
:
539 if (view_read_test_tabulator (view
, &info
)) {
540 line
->width
+= 8 - (line
->left
+ line
->width
) % 8;
541 if ((width
!= 0) && (line
->left
+ line
->width
>= width
)) {
542 w
= line
->left
+ line
->width
- width
;
543 /* if width of screen is very small, tabulator is cut to line
544 * left of next line is 0 */
545 w
= (w
< width
) ? w
: 0;
546 line
->width
= width
- line
->left
;
547 line
= view_lc_create_wrap_line (line
, info
.next
, w
);
552 if (view_read_test_nroff_back (view
, &info
)) {
553 w
= str_term_width1 (info
.chi1
);
558 /* assure, that nroff sequence never start in previous cache_line */
562 nroff_start
= info
.actual
;
564 w
= str_isprint (info
.cact
) ? str_term_width1 (info
.cact
) : 1;
566 if (line
->left
+ line
->width
+ w
> width
) {
567 line
= view_lc_create_wrap_line (line
, nroff_start
, 0);
570 while (info
.result
!= -1 && str_iscombiningmark (info
.cnxt
)) {
571 view_read_continue (view
, &info
);
578 /* text read to the end, seting lines_end*/
579 if (view
->text_nroff_mode
)
580 view
->nroff_lines_end
= line
;
582 view
->lines_end
= line
;
584 line
= view_lc_create_next_line (line
, info
.next
);
585 line
->width
= (screen_dimen
) (-1);
586 line
->end
= info
.next
;
592 view_lc_set_param (offset_type
*param
, offset_type value
)
594 if ((*param
) > value
) (*param
) = value
;
597 /* offset to column or column to offset, value that will be maped must be
598 * set to INVALID_OFFSET, it is very analogous to view_load_cache_line
599 * and it is possible to integrate them in one function */
601 view_map_offset_and_column (WView
*view
, struct cache_line
*line
,
602 offset_type
*column
, offset_type
*offset
)
604 const screen_dimen width
= view
->data_area
.width
;
605 struct read_info info
;
606 offset_type nroff_start
= 0;
611 /* HACK: valgrind screams here.
612 * TODO: to figure out WHY valgrind detects uninitialized
613 * variables. Maybe, info isn't fully initialized?
615 memset (&info
, 0, sizeof info
);
618 view_read_start (view
, &info
, line
->start
);
619 while (info
.result
!= -1) {
620 view_read_continue (view
, &info
);
622 if (*column
== INVALID_OFFSET
) {
623 if (*offset
< info
.next
) {
624 (*column
) = col
+ line
->left
;
629 switch (view_read_test_new_line (view
, &info
)) {
631 view_lc_set_param (offset
, info
.actual
);
632 view_lc_set_param (column
, line
->left
+ col
);
634 case VRT_NW_CONTINUE
:
638 if (view_read_test_tabulator (view
, &info
)) {
639 col
+= 8 - (line
->left
+ col
) % 8;
640 if ((width
!= 0) && (line
->left
+ col
>= width
)) {
641 w
= line
->left
+ col
- width
;
642 w
= (w
< width
) ? w
: 0;
643 col
= width
- line
->left
;
644 view_lc_set_param (offset
, info
.actual
);
645 view_lc_set_param (column
, line
->left
+ col
);
650 if (view_read_test_nroff_back (view
, &info
)) {
651 w
= str_term_width1 (info
.chi1
);
659 nroff_start
= info
.actual
;
661 w
= str_isprint (info
.cact
) ? str_term_width1 (info
.cact
) : 1;
663 if (line
->left
+ col
+ w
> width
) {
664 view_lc_set_param (offset
, nroff_start
);
665 view_lc_set_param (column
, line
->left
+ col
);
668 while (info
.result
!= -1 && str_iscombiningmark (info
.cnxt
)) {
669 view_read_continue (view
, &info
);
675 if (*offset
== INVALID_OFFSET
) {
676 if (*column
< col
+ line
->left
) {
677 (*offset
) = nroff_start
;
683 view_lc_set_param (offset
, info
.actual
);
684 view_lc_set_param (column
, line
->left
+ col
);
687 /* macro, that iterate cache_line until stop holds,
688 * nf is function to iterate cache_line
689 * l is line to iterate and t is temporary line only */
690 #define view_move_to_stop(l,t,stop,nf) \
691 while (((t = nf (view, l)) != NULL) && (stop)) l = t;
693 /* make all cache_lines invalidet */
695 view_reset_cache_lines (WView
*view
)
697 if (view
->lines
!= NULL
) {
698 view
->lines
->end
= INVALID_OFFSET
;
700 view
->lines_end
= NULL
;
702 if (view
->nroff_lines
!= NULL
) {
703 view
->nroff_lines
->end
= INVALID_OFFSET
;
705 view
->nroff_lines_end
= NULL
;
707 view
->first_showed_line
= NULL
;
710 #define MAX_UNLOADED_CACHE_LINE 100
712 /* free some cache_lines, if count of cache_lines is bigger
713 * than MAX_UNLOADED_CACHE_LINE */
715 view_reduce_cache_lines (WView
*view
)
717 struct cache_line
*line
;
718 struct cache_line
*next
;
723 while (line
!= NULL
&& li
< MAX_UNLOADED_CACHE_LINE
) {
727 if (line
!= NULL
) line
->prev
->next
= NULL
;
728 while (line
!= NULL
) {
733 view
->lines_end
= NULL
;
735 line
= view
->nroff_lines
;
736 while (line
!= NULL
&& li
< MAX_UNLOADED_CACHE_LINE
) {
740 if (line
!= NULL
) line
->prev
->next
= NULL
;
741 while (line
!= NULL
) {
746 view
->nroff_lines_end
= NULL
;
748 view
->first_showed_line
= NULL
;
751 /* return first cache_line for actual mode (normal / nroff) */
752 static struct cache_line
*
753 view_get_first_line (WView
*view
)
755 struct cache_line
**first_line
;
757 first_line
= (view
->text_nroff_mode
) ?
758 &(view
->nroff_lines
) : &(view
->lines
);
760 if (*first_line
== NULL
) {
761 (*first_line
) = g_new0 (struct cache_line
, 1);
762 (*first_line
)->end
= INVALID_OFFSET
;
765 if ((*first_line
)->end
== INVALID_OFFSET
)
766 view_load_cache_line (view
, *first_line
);
768 return (*first_line
);
771 /* return following chahe_line or NULL */
772 static struct cache_line
*
773 view_get_next_line (WView
*view
, struct cache_line
*line
)
775 struct cache_line
*result
;
777 if (line
->next
!= NULL
) {
780 if (result
->end
== INVALID_OFFSET
)
781 view_load_cache_line (view
, result
);
783 return (result
->width
!= (screen_dimen
) (-1)) ? result
: NULL
;
788 /* return last cache_line, it could take same time, because whole read text must
789 * be read (only once) */
790 static struct cache_line
*
791 view_get_last_line (WView
*view
)
793 struct cache_line
*result
;
794 struct cache_line
*next
;
796 result
= (view
->text_nroff_mode
) ?
797 view
->nroff_lines_end
: view
->nroff_lines_end
;
799 if (result
!= NULL
) return result
;
801 if (view
->first_showed_line
== NULL
)
802 view
->first_showed_line
= view_get_first_line (view
);
804 result
= view
->first_showed_line
;
805 next
= view_get_next_line (view
, result
);
806 while (next
!= NULL
) {
808 next
= view_get_next_line (view
, result
);
813 /* return previous cache_line or NULL */
814 static struct cache_line
*
815 view_get_previous_line (WView
*view
, struct cache_line
*line
)
821 /* return first displayed cache_line */
822 static struct cache_line
*
823 view_get_first_showed_line (WView
*view
)
825 struct cache_line
*result
;
827 if (view
->first_showed_line
== NULL
)
828 view
->first_showed_line
= view_get_first_line (view
);
830 result
= view
->first_showed_line
;
834 /* return first cache_line with same number as line */
835 static struct cache_line
*
836 view_get_start_of_whole_line (WView
*view
, struct cache_line
*line
)
838 struct cache_line
*t
;
841 view_move_to_stop (line
, t
, t
->number
== line
->number
, view_get_previous_line
)
847 /* return last cache_line with same number as line */
848 static struct cache_line
*
849 view_get_end_of_whole_line (WView
*view
, struct cache_line
*line
)
851 struct cache_line
*t
;
854 view_move_to_stop (line
, t
, t
->number
== line
->number
, view_get_next_line
)
860 /* return last cache_line, that has number lesser than line
862 static struct cache_line
*
863 view_get_previous_whole_line (WView
*view
, struct cache_line
*line
)
865 line
= view_get_start_of_whole_line (view
, line
);
866 return view_get_previous_line (view
, line
);
869 /* return first cache_line, that has number greater than line
871 static struct cache_line
*
872 view_get_next_whole_line (WView
*view
, struct cache_line
*line
)
874 line
= view_get_end_of_whole_line (view
, line
);
875 return view_get_next_line (view
, line
);
878 /* return sum of widths of all cache_lines that has same number as line */
880 view_width_of_whole_line (WView
*view
, struct cache_line
*line
)
882 struct cache_line
*next
;
883 screen_dimen result
= 0;
885 line
= view_get_start_of_whole_line (view
, line
);
886 next
= view_get_next_line (view
, line
);
887 while ((next
!= NULL
) && (next
->number
== line
->number
)) {
888 result
+= line
->left
+ line
->width
;
890 next
= view_get_next_line (view
, line
);
892 result
+= line
->left
+ line
->width
;
896 /* return sum of widths of cache_lines before line, that has same number as line */
898 view_width_of_whole_line_before (WView
*view
, struct cache_line
*line
)
900 struct cache_line
*next
;
901 screen_dimen result
= 0;
903 next
= view_get_start_of_whole_line (view
, line
);
904 while (next
!= line
) {
905 result
+= next
->left
+ next
->width
;
906 next
= view_get_next_line (view
, next
);
911 /* map column to offset and cache_line */
913 view_column_to_offset (WView
*view
, struct cache_line
**line
, offset_type column
)
915 struct cache_line
*next
;
918 *line
= view_get_start_of_whole_line (view
, *line
);
920 while (column
>= (*line
)->left
+ (*line
)->width
) {
921 column
-= (*line
)->left
+ (*line
)->width
;
922 result
= (*line
)->end
;
923 next
= view_get_next_line (view
, *line
);
924 if ((next
== NULL
) || (next
->number
!= (*line
)->number
)) break;
927 /* if (column < (*line)->left + (*line)->width) { */
928 result
= INVALID_OFFSET
,
929 view_map_offset_and_column (view
, *line
, &column
, &result
);
934 /* map offset to cache_line */
935 static struct cache_line
*
936 view_offset_to_line (WView
*view
, offset_type from
)
938 struct cache_line
*result
;
939 struct cache_line
*t
;
941 result
= view_get_first_line (view
);
943 view_move_to_stop (result
, t
, result
->end
<= from
, view_get_next_line
)
947 /* map offset to cache_line, searching starts from line */
948 static struct cache_line
*
949 view_offset_to_line_from (WView
*view
, offset_type from
, struct cache_line
*line
)
951 struct cache_line
*result
;
952 struct cache_line
*t
;
956 view_move_to_stop (result
, t
, result
->start
> from
, view_get_previous_line
)
957 view_move_to_stop (result
, t
, result
->end
<= from
, view_get_next_line
)
962 /* mam offset to column */
964 view_offset_to_column (WView
*view
, struct cache_line
*line
, offset_type from
)
966 offset_type result
= INVALID_OFFSET
;
968 view_map_offset_and_column (view
, line
, &result
, &from
);
970 result
+= view_width_of_whole_line_before (view
, line
);
976 view_compute_areas (WView
*view
)
978 struct area view_area
;
979 struct cache_line
*next
;
980 screen_dimen height
, rest
, y
;
981 screen_dimen old_width
;
983 old_width
= view
->data_area
.width
;
985 /* The viewer is surrounded by a frame of size view->dpy_frame_size.
986 * Inside that frame, there are: The status line (at the top),
987 * the data area and an optional ruler, which is shown above or
988 * below the data area. */
990 view_area
.top
= view
->dpy_frame_size
;
991 view_area
.left
= view
->dpy_frame_size
;
992 view_area
.height
= dimen_doz(view
->widget
.lines
, 2 * view
->dpy_frame_size
);
993 view_area
.width
= dimen_doz(view
->widget
.cols
, 2 * view
->dpy_frame_size
);
995 /* Most coordinates of the areas equal those of the whole viewer */
996 view
->status_area
= view_area
;
997 view
->ruler_area
= view_area
;
998 view
->data_area
= view_area
;
1000 /* Compute the heights of the areas */
1001 rest
= view_area
.height
;
1003 height
= dimen_min(rest
, 1);
1004 view
->status_area
.height
= height
;
1007 height
= dimen_min(rest
, (ruler
== RULER_NONE
|| view
->hex_mode
) ? 0 : 2);
1008 view
->ruler_area
.height
= height
;
1011 view
->data_area
.height
= rest
;
1013 /* Compute the position of the areas */
1016 view
->status_area
.top
= y
;
1017 y
+= view
->status_area
.height
;
1019 if (ruler
== RULER_TOP
) {
1020 view
->ruler_area
.top
= y
;
1021 y
+= view
->ruler_area
.height
;
1024 view
->data_area
.top
= y
;
1025 y
+= view
->data_area
.height
;
1027 if (ruler
== RULER_BOTTOM
) {
1028 view
->ruler_area
.top
= y
;
1029 y
+= view
->ruler_area
.height
;
1032 if (old_width
!= view
->data_area
.width
) {
1033 view_reset_cache_lines (view
);
1034 view
->first_showed_line
= view_get_first_line (view
);
1035 next
= view_get_next_line (view
, view
->first_showed_line
);
1036 while ((next
!= NULL
) && (view
->first_showed_line
->end
<= view
->dpy_start
)) {
1037 view
->first_showed_line
= next
;
1038 next
= view_get_next_line (view
, view
->first_showed_line
);
1040 view
->dpy_start
= view
->first_showed_line
->start
;
1045 view_hexedit_free_change_list (WView
*view
)
1047 struct hexedit_change_node
*curr
, *next
;
1049 for (curr
= view
->change_list
; curr
!= NULL
; curr
= next
) {
1053 view
->change_list
= NULL
;
1057 /* {{{ Growing buffer }}} */
1060 view_init_growbuf (WView
*view
)
1062 view
->growbuf_in_use
= TRUE
;
1063 view
->growbuf_blockptr
= NULL
;
1064 view
->growbuf_blocks
= 0;
1065 view
->growbuf_lastindex
= VIEW_PAGE_SIZE
;
1066 view
->growbuf_finished
= FALSE
;
1070 view_growbuf_free (WView
*view
)
1074 assert (view
->growbuf_in_use
);
1076 for (i
= 0; i
< view
->growbuf_blocks
; i
++)
1077 g_free (view
->growbuf_blockptr
[i
]);
1078 g_free (view
->growbuf_blockptr
);
1079 view
->growbuf_blockptr
= NULL
;
1080 view
->growbuf_in_use
= FALSE
;
1084 view_growbuf_filesize (WView
*view
)
1086 assert(view
->growbuf_in_use
);
1088 if (view
->growbuf_blocks
== 0)
1091 return ((offset_type
) view
->growbuf_blocks
- 1) * VIEW_PAGE_SIZE
1092 + view
->growbuf_lastindex
;
1095 /* Copies the output from the pipe to the growing buffer, until either
1096 * the end-of-pipe is reached or the interval [0..ofs) of the growing
1097 * buffer is completely filled. */
1099 view_growbuf_read_until (WView
*view
, offset_type ofs
)
1104 gboolean short_read
;
1106 assert (view
->growbuf_in_use
);
1108 if (view
->growbuf_finished
)
1112 while (view_growbuf_filesize (view
) < ofs
|| short_read
) {
1113 if (view
->growbuf_lastindex
== VIEW_PAGE_SIZE
) {
1114 /* Append a new block to the growing buffer */
1115 byte
*newblock
= g_try_malloc (VIEW_PAGE_SIZE
);
1116 byte
**newblocks
= g_try_malloc (sizeof (*newblocks
) * (view
->growbuf_blocks
+ 1));
1117 if (!newblock
|| !newblocks
) {
1122 memcpy (newblocks
, view
->growbuf_blockptr
, sizeof (*newblocks
) * view
->growbuf_blocks
);
1123 g_free (view
->growbuf_blockptr
);
1124 view
->growbuf_blockptr
= newblocks
;
1125 view
->growbuf_blockptr
[view
->growbuf_blocks
++] = newblock
;
1126 view
->growbuf_lastindex
= 0;
1128 p
= view
->growbuf_blockptr
[view
->growbuf_blocks
- 1] + view
->growbuf_lastindex
;
1129 bytesfree
= VIEW_PAGE_SIZE
- view
->growbuf_lastindex
;
1131 if (view
->datasource
== DS_STDIO_PIPE
) {
1132 nread
= fread (p
, 1, bytesfree
, view
->ds_stdio_pipe
);
1134 view
->growbuf_finished
= TRUE
;
1135 (void) pclose (view
->ds_stdio_pipe
);
1137 close_error_pipe (D_NORMAL
, NULL
);
1138 view
->ds_stdio_pipe
= NULL
;
1142 assert (view
->datasource
== DS_VFS_PIPE
);
1144 nread
= mc_read (view
->ds_vfs_pipe
, p
, bytesfree
);
1145 } while (nread
== -1 && errno
== EINTR
);
1146 if (nread
== -1 || nread
== 0) {
1147 view
->growbuf_finished
= TRUE
;
1148 (void) mc_close (view
->ds_vfs_pipe
);
1149 view
->ds_vfs_pipe
= -1;
1153 short_read
= ((size_t)nread
< bytesfree
);
1154 view
->growbuf_lastindex
+= nread
;
1159 get_byte_growing_buffer (WView
*view
, offset_type byte_index
)
1161 offset_type pageno
= byte_index
/ VIEW_PAGE_SIZE
;
1162 offset_type pageindex
= byte_index
% VIEW_PAGE_SIZE
;
1164 assert (view
->growbuf_in_use
);
1166 if ((size_t) pageno
!= pageno
)
1169 view_growbuf_read_until (view
, byte_index
+ 1);
1170 if (view
->growbuf_blocks
== 0)
1172 if (pageno
< view
->growbuf_blocks
- 1)
1173 return view
->growbuf_blockptr
[pageno
][pageindex
];
1174 if (pageno
== view
->growbuf_blocks
- 1 && pageindex
< view
->growbuf_lastindex
)
1175 return view
->growbuf_blockptr
[pageno
][pageindex
];
1179 /* {{{ Data sources }}} */
1182 The data source provides the viewer with data from either a file, a
1183 string or the output of a command. The get_byte() function can be
1184 used to get the value of a byte at a specific offset. If the offset
1185 is out of range, -1 is returned. The function get_byte_indexed(a,b)
1186 returns the byte at the offset a+b, or -1 if a+b is out of range.
1188 The view_set_byte() function has the effect that later calls to
1189 get_byte() will return the specified byte for this offset. This
1190 function is designed only for use by the hexedit component after
1191 saving its changes. Inspect the source before you want to use it for
1194 The view_get_filesize() function returns the current size of the
1195 data source. If the growing buffer is used, this size may increase
1196 later on. Use the view_may_still_grow() function when you want to
1197 know if the size can change later.
1201 view_get_filesize (WView
*view
)
1203 switch (view
->datasource
) {
1208 return view_growbuf_filesize (view
);
1210 return view
->ds_file_filesize
;
1212 return view
->ds_string_len
;
1214 assert(!"Unknown datasource type");
1219 static inline gboolean
1220 view_may_still_grow (WView
*view
)
1222 return (view
->growbuf_in_use
&& !view
->growbuf_finished
);
1225 /* returns TRUE if the idx lies in the half-open interval
1226 * [offset; offset + size), FALSE otherwise.
1228 static inline gboolean
1229 already_loaded (offset_type offset
, offset_type idx
, size_t size
)
1231 return (offset
<= idx
&& idx
- offset
< size
);
1235 view_file_load_data (WView
*view
, offset_type byte_index
)
1237 offset_type blockoffset
;
1241 assert (view
->datasource
== DS_FILE
);
1243 if (already_loaded (view
->ds_file_offset
, byte_index
, view
->ds_file_datalen
))
1246 if (byte_index
>= view
->ds_file_filesize
)
1249 blockoffset
= offset_rounddown (byte_index
, view
->ds_file_datasize
);
1250 if (mc_lseek (view
->ds_file_fd
, blockoffset
, SEEK_SET
) == -1)
1254 while (bytes_read
< view
->ds_file_datasize
) {
1255 res
= mc_read (view
->ds_file_fd
, view
->ds_file_data
+ bytes_read
, view
->ds_file_datasize
- bytes_read
);
1260 bytes_read
+= (size_t) res
;
1262 view
->ds_file_offset
= blockoffset
;
1263 if (bytes_read
> view
->ds_file_filesize
- view
->ds_file_offset
) {
1264 /* the file has grown in the meantime -- stick to the old size */
1265 view
->ds_file_datalen
= view
->ds_file_filesize
- view
->ds_file_offset
;
1267 view
->ds_file_datalen
= bytes_read
;
1272 view
->ds_file_datalen
= 0;
1276 get_byte_none (WView
*view
, offset_type byte_index
)
1278 assert (view
->datasource
== DS_NONE
);
1285 get_byte_file (WView
*view
, offset_type byte_index
)
1287 assert (view
->datasource
== DS_FILE
);
1289 view_file_load_data (view
, byte_index
);
1290 if (already_loaded(view
->ds_file_offset
, byte_index
, view
->ds_file_datalen
))
1291 return view
->ds_file_data
[byte_index
- view
->ds_file_offset
];
1296 get_byte_string (WView
*view
, offset_type byte_index
)
1298 assert (view
->datasource
== DS_STRING
);
1299 if (byte_index
< view
->ds_string_len
)
1300 return view
->ds_string_data
[byte_index
];
1305 get_byte (WView
*view
, offset_type offset
)
1307 switch (view
->datasource
) {
1310 return get_byte_growing_buffer (view
, offset
);
1312 return get_byte_file (view
, offset
);
1314 return get_byte_string (view
, offset
);
1316 return get_byte_none (view
, offset
);
1318 assert(!"Unknown datasource type");
1323 get_byte_indexed (WView
*view
, offset_type base
, offset_type ofs
)
1325 if (base
<= OFFSETTYPE_MAX
- ofs
)
1326 return get_byte (view
, base
+ ofs
);
1331 view_set_byte (WView
*view
, offset_type offset
, byte b
)
1334 assert (offset
< view_get_filesize (view
));
1335 assert (view
->datasource
== DS_FILE
);
1336 view
->ds_file_datalen
= 0; /* just force reloading */
1340 view_set_datasource_none (WView
*view
)
1342 view
->datasource
= DS_NONE
;
1346 view_set_datasource_vfs_pipe (WView
*view
, int fd
)
1349 view
->datasource
= DS_VFS_PIPE
;
1350 view
->ds_vfs_pipe
= fd
;
1352 view_init_growbuf (view
);
1356 view_set_datasource_stdio_pipe (WView
*view
, FILE *fp
)
1358 assert (fp
!= NULL
);
1359 view
->datasource
= DS_STDIO_PIPE
;
1360 view
->ds_stdio_pipe
= fp
;
1362 view_init_growbuf (view
);
1366 view_set_datasource_string (WView
*view
, const char *s
)
1368 view
->datasource
= DS_STRING
;
1369 view
->ds_string_data
= (byte
*) g_strdup (s
);
1370 view
->ds_string_len
= strlen (s
);
1374 view_set_datasource_file (WView
*view
, int fd
, const struct stat
*st
)
1376 view
->datasource
= DS_FILE
;
1377 view
->ds_file_fd
= fd
;
1378 view
->ds_file_filesize
= st
->st_size
;
1379 view
->ds_file_offset
= 0;
1380 view
->ds_file_data
= g_malloc (4096);
1381 view
->ds_file_datalen
= 0;
1382 view
->ds_file_datasize
= 4096;
1386 view_close_datasource (WView
*view
)
1388 switch (view
->datasource
) {
1392 if (view
->ds_stdio_pipe
!= NULL
) {
1393 (void) pclose (view
->ds_stdio_pipe
);
1395 close_error_pipe (D_NORMAL
, NULL
);
1396 view
->ds_stdio_pipe
= NULL
;
1398 view_growbuf_free (view
);
1401 if (view
->ds_vfs_pipe
!= -1) {
1402 (void) mc_close (view
->ds_vfs_pipe
);
1403 view
->ds_vfs_pipe
= -1;
1405 view_growbuf_free (view
);
1408 (void) mc_close (view
->ds_file_fd
);
1409 view
->ds_file_fd
= -1;
1410 g_free (view
->ds_file_data
);
1411 view
->ds_file_data
= NULL
;
1414 g_free (view
->ds_string_data
);
1415 view
->ds_string_data
= NULL
;
1418 assert (!"Unknown datasource type");
1420 view
->datasource
= DS_NONE
;
1423 /* {{{ Cursor Movement }}} */
1426 The following variables have to do with the current position and are
1427 updated by the cursor movement functions.
1429 In hex view and wrapped text view mode, dpy_start marks the offset of
1430 the top-left corner on the screen, in non-wrapping text mode it is
1431 the beginning of the current line. In hex mode, hex_cursor is the
1432 offset of the cursor. In non-wrapping text mode, dpy_text_column is
1433 the number of columns that are hidden on the left side on the screen.
1435 In hex mode, dpy_start is updated by the view_fix_cursor_position()
1436 function in order to keep the other functions simple. In
1437 non-wrapping text mode dpy_start and dpy_text_column are normalized
1438 such that dpy_text_column < view_get_datacolumns().
1441 /* prototypes for functions used by view_moveto_bottom() */
1442 static void view_move_up (WView
*, offset_type
);
1443 static void view_moveto_bol (WView
*);
1445 /* set view->first_showed_line and view->dpy_start
1446 * use view->dpy_text_column in nowrap mode */
1448 view_set_first_showed (WView
*view
, struct cache_line
*line
)
1450 if (view
->text_wrap_mode
) {
1451 view
->dpy_start
= line
->start
;
1452 view
->first_showed_line
= line
;
1454 view
->dpy_start
= view_column_to_offset (view
, &line
, view
->dpy_text_column
);
1455 view
->first_showed_line
= line
;
1457 if (view
->search_start
== view
->search_end
) {
1458 view
->search_start
= view
->dpy_start
;
1459 view
->search_end
= view
->dpy_start
;
1464 view_scroll_to_cursor (WView
*view
)
1466 if (view
->hex_mode
) {
1467 const offset_type bytes
= view
->bytes_per_line
;
1468 const offset_type displaysize
= view
->data_area
.height
* bytes
;
1469 const offset_type cursor
= view
->hex_cursor
;
1470 offset_type topleft
= view
->dpy_start
;
1472 if (topleft
+ displaysize
<= cursor
)
1473 topleft
= offset_rounddown (cursor
, bytes
)
1474 - (displaysize
- bytes
);
1475 if (cursor
< topleft
)
1476 topleft
= offset_rounddown (cursor
, bytes
);
1477 view
->dpy_start
= topleft
;
1478 } else if (view
->text_wrap_mode
) {
1479 view
->dpy_text_column
= 0;
1485 view_movement_fixups (WView
*view
, gboolean reset_search
)
1487 view_scroll_to_cursor (view
);
1489 view
->search_start
= view
->dpy_start
;
1490 view
->search_end
= view
->dpy_start
;
1496 view_moveto_top (WView
*view
)
1498 view
->dpy_start
= 0;
1499 view
->hex_cursor
= 0;
1500 view
->first_showed_line
= view_get_first_line (view
);
1501 view
->dpy_text_column
= 0;
1502 view_movement_fixups (view
, TRUE
);
1506 view_moveto_bottom (WView
*view
)
1508 offset_type datalines
, lines_up
, filesize
, last_offset
;
1509 struct cache_line
*line
;
1511 if (view
->growbuf_in_use
)
1512 view_growbuf_read_until (view
, OFFSETTYPE_MAX
);
1514 filesize
= view_get_filesize (view
);
1515 last_offset
= offset_doz(filesize
, 1);
1516 datalines
= view
->data_area
.height
;
1517 lines_up
= offset_doz(datalines
, 1);
1519 if (view
->hex_mode
) {
1520 view
->hex_cursor
= filesize
;
1521 view_move_up (view
, lines_up
);
1522 view
->hex_cursor
= last_offset
;
1524 line
= view_get_last_line (view
);
1525 if (!view
->text_wrap_mode
)
1526 line
= view_get_start_of_whole_line (view
, line
);
1527 view_set_first_showed (view
, line
);
1528 view
->dpy_text_column
= 0;
1529 view_move_up (view
, lines_up
);
1531 view_movement_fixups (view
, TRUE
);
1535 view_moveto_bol (WView
*view
)
1537 struct cache_line
*line
;
1539 if (view
->hex_mode
) {
1540 view
->hex_cursor
-= view
->hex_cursor
% view
->bytes_per_line
;
1541 } else if (view
->text_wrap_mode
) {
1544 line
= view_get_first_showed_line (view
);
1545 line
= view_get_start_of_whole_line (view
, line
);
1546 view
->dpy_text_column
= 0;
1547 view_set_first_showed (view
, line
);
1549 view_movement_fixups (view
, TRUE
);
1553 view_moveto_eol (WView
*view
)
1555 const screen_dimen width
= view
->data_area
.width
;
1556 struct cache_line
*line
;
1559 if (view
->hex_mode
) {
1560 offset_type filesize
, bol
;
1562 bol
= offset_rounddown (view
->hex_cursor
, view
->bytes_per_line
);
1563 if (get_byte_indexed (view
, bol
, view
->bytes_per_line
- 1) != -1) {
1564 view
->hex_cursor
= bol
+ view
->bytes_per_line
- 1;
1566 filesize
= view_get_filesize (view
);
1567 view
->hex_cursor
= offset_doz(filesize
, 1);
1569 } else if (view
->text_wrap_mode
) {
1572 line
= view_get_first_showed_line (view
);
1573 line
= view_get_start_of_whole_line (view
, line
);
1574 w
= view_width_of_whole_line (view
, line
);
1576 view
->dpy_text_column
= w
- width
;
1578 /* if (w + width <= view->dpy_text_column) {*/
1579 view
->dpy_text_column
= 0;
1582 view_set_first_showed (view
, line
);
1584 view_movement_fixups (view
, FALSE
);
1588 view_moveto_offset (WView
*view
, offset_type offset
)
1590 struct cache_line
*line
;
1592 if (view
->hex_mode
) {
1593 view
->hex_cursor
= offset
;
1594 view
->dpy_start
= offset
- offset
% view
->bytes_per_line
;
1596 line
= view_offset_to_line (view
, offset
);
1597 view
->dpy_start
= (view
->text_wrap_mode
) ? line
->start
: offset
;
1598 view
->first_showed_line
= line
;
1599 view
->dpy_text_column
= (view
->text_wrap_mode
) ?
1600 0 : view_offset_to_column (view
, line
, offset
);
1602 view_movement_fixups (view
, TRUE
);
1606 view_moveto (WView
*view
, offset_type row
, offset_type col
)
1608 struct cache_line
*act
;
1609 struct cache_line
*t
;
1611 act
= view_get_first_line (view
);
1612 view_move_to_stop (act
, t
, (off_t
)act
->number
!= row
, view_get_next_line
)
1614 view
->dpy_text_column
= (view
->text_wrap_mode
) ? 0 : col
;
1615 view
->dpy_start
= view_column_to_offset (view
, &act
, col
);
1616 view
->dpy_start
= (view
->text_wrap_mode
) ? act
->start
: view
->dpy_start
;
1617 view
->first_showed_line
= act
;
1620 view_moveto_offset (view
, view
->dpy_start
);
1623 /* extendet view_move_to_stop, now has counter, too */
1624 #define view_count_to_stop(l,t,i,stop,nf)\
1625 while (((t = nf(view, l)) != NULL) && (stop)) {\
1629 view_move_up (WView
*view
, offset_type lines
)
1631 struct cache_line
*line
, *t
;
1634 if (view
->hex_mode
) {
1635 offset_type bytes
= lines
* view
->bytes_per_line
;
1636 if (view
->hex_cursor
>= bytes
) {
1637 view
->hex_cursor
-= bytes
;
1638 if (view
->hex_cursor
< view
->dpy_start
)
1639 view
->dpy_start
= offset_doz (view
->dpy_start
, bytes
);
1641 view
->hex_cursor
%= view
->bytes_per_line
;
1643 } else if (view
->text_wrap_mode
) {
1644 line
= view_get_first_showed_line (view
);
1646 view_count_to_stop (line
, t
, li
, (li
< lines
), view_get_previous_line
)
1647 view_set_first_showed (view
, line
);
1649 line
= view_get_first_showed_line (view
);
1651 view_count_to_stop (line
, t
, li
, (li
< lines
), view_get_previous_whole_line
)
1652 view_set_first_showed (view
, line
);
1654 view_movement_fixups (view
, (lines
!= 1));
1658 return_up (struct cache_line * (*ne) (WView *, struct cache_line *),
1659 struct cache_line * (*pr) (WView *, struct cache_line *),
1660 off_t *li, struct cache_line **t, struct cache_line **line,
1665 while ((*t != NULL) && (*li < view->data_area.height)) {
1669 *li = view->data_area.height - *li;
1670 *t = pr (view, *line);
1671 while ((*t != NULL) && (*li > 0)) {
1673 *t = pr (view, *line);
1679 view_move_down (WView
*view
, offset_type lines
)
1681 struct cache_line
*line
;
1682 struct cache_line
*t
;
1686 if (view
->hex_mode
) {
1687 offset_type i
, limit
, last_byte
;
1689 last_byte
= view_get_filesize (view
);
1690 if (last_byte
>= (offset_type
) view
->bytes_per_line
)
1691 limit
= last_byte
- view
->bytes_per_line
;
1694 for (i
= 0; i
< lines
&& view
->hex_cursor
< limit
; i
++) {
1695 view
->hex_cursor
+= view
->bytes_per_line
;
1697 view
->dpy_start
+= view
->bytes_per_line
;
1700 } else if (view
->text_wrap_mode
) {
1701 line
= view_get_first_showed_line (view
);
1703 view_count_to_stop (line
, t
, li
, (off_t
)li
< lines
, view_get_next_line
)
1705 /* return_up (view_get_next_line, view_get_previous_line); */
1706 view_set_first_showed (view
, line
);
1708 line
= view_get_first_showed_line (view
);
1710 view_count_to_stop (line
, t
, li
, li
< lines
, view_get_next_whole_line
)
1712 /* return_up (view_get_next_whole_line, view_get_previous_whole_line); */
1713 view_set_first_showed (view
, line
);
1715 view_movement_fixups (view
, (lines
!= 1));
1719 view_move_left (WView
*view
, offset_type columns
)
1721 struct cache_line
*line
;
1723 if (view
->hex_mode
) {
1724 assert (columns
== 1);
1725 if (view
->hexview_in_text
|| !view
->hexedit_lownibble
) {
1726 if (view
->hex_cursor
> 0)
1729 if (!view
->hexview_in_text
)
1730 view
->hexedit_lownibble
= !view
->hexedit_lownibble
;
1731 } else if (view
->text_wrap_mode
) {
1734 if (view
->dpy_text_column
>= columns
)
1735 view
->dpy_text_column
-= columns
;
1737 view
->dpy_text_column
= 0;
1739 line
= view_get_first_showed_line (view
);
1740 view_set_first_showed (view
, line
);
1742 view_movement_fixups (view
, FALSE
);
1746 view_move_right (WView
*view
, offset_type columns
)
1748 struct cache_line
*line
;
1750 if (view
->hex_mode
) {
1751 assert (columns
== 1);
1752 if (view
->hexview_in_text
|| view
->hexedit_lownibble
) {
1753 if (get_byte_indexed (view
, view
->hex_cursor
, 1) != -1)
1756 if (!view
->hexview_in_text
)
1757 view
->hexedit_lownibble
= !view
->hexedit_lownibble
;
1758 } else if (view
->text_wrap_mode
) {
1761 view
->dpy_text_column
+= columns
;
1762 line
= view_get_first_showed_line (view
);
1763 view_set_first_showed (view
, line
);
1765 view_movement_fixups (view
, FALSE
);
1768 /* {{{ Toggling of viewer modes }}} */
1771 view_toggle_hex_mode (WView
*view
)
1773 struct cache_line
*line
;
1775 view
->hex_mode
= !view
->hex_mode
;
1776 view
->search_type
= (view
->hex_mode
)?MC_SEARCH_T_HEX
:MC_SEARCH_T_NORMAL
;
1778 if (view
->hex_mode
) {
1779 view
->hex_cursor
= view
->dpy_start
;
1781 offset_rounddown (view
->dpy_start
, view
->bytes_per_line
);
1782 view
->widget
.options
|= W_WANT_CURSOR
;
1784 line
= view_offset_to_line (view
, view
->hex_cursor
);
1785 view
->dpy_text_column
= (view
->text_wrap_mode
) ? 0 :
1786 view_offset_to_column (view
, line
, view
->hex_cursor
);
1787 view_set_first_showed (view
, line
);
1788 view
->widget
.options
&= ~W_WANT_CURSOR
;
1790 altered_hex_mode
= 1;
1791 view
->dpy_bbar_dirty
= TRUE
;
1796 view_toggle_hexedit_mode (WView
*view
)
1798 view
->hexedit_mode
= !view
->hexedit_mode
;
1799 view
->dpy_bbar_dirty
= TRUE
;
1804 view_toggle_wrap_mode (WView
*view
)
1806 struct cache_line
*line
;
1808 view
->text_wrap_mode
= !view
->text_wrap_mode
;
1809 if (view
->text_wrap_mode
) {
1810 view
->dpy_text_column
= 0;
1811 view
->dpy_start
= view_get_first_showed_line (view
)->start
;
1813 line
= view_get_first_showed_line (view
);
1814 view
->dpy_text_column
= view_width_of_whole_line_before (view
, line
);
1816 view
->dpy_bbar_dirty
= TRUE
;
1821 view_toggle_nroff_mode (WView
*view
)
1823 struct cache_line
*line
;
1824 struct cache_line
*next
;
1826 view
->text_nroff_mode
= !view
->text_nroff_mode
;
1827 altered_nroff_flag
= 1;
1828 view
->dpy_bbar_dirty
= TRUE
;
1831 line
= view_get_first_line (view
);
1832 view_move_to_stop (line
, next
, line
->end
<= view
->dpy_start
, view_get_next_line
)
1834 view_set_first_showed (view
, line
);
1838 view_toggle_magic_mode (WView
*view
)
1840 char *filename
, *command
;
1842 altered_magic_flag
= 1;
1843 view
->magic_mode
= !view
->magic_mode
;
1844 filename
= g_strdup (view
->filename
);
1845 command
= g_strdup (view
->command
);
1848 view_load (view
, command
, filename
, 0);
1851 view
->dpy_bbar_dirty
= TRUE
;
1855 /* {{{ Miscellaneous functions }}} */
1858 view_done (WView
*view
)
1860 /* Save current file position */
1861 if (mcview_remember_file_position
&& view
->filename
!= NULL
) {
1862 struct cache_line
*line
;
1864 offset_type row
, col
;
1866 canon_fname
= vfs_canon (view
->filename
);
1867 line
= view_get_first_showed_line (view
);
1868 row
= line
->number
+ 1;
1869 col
= view_offset_to_column (view
, line
, view
->dpy_start
);
1871 save_file_position (canon_fname
, row
, col
);
1872 g_free (canon_fname
);
1875 /* Write back the global viewer mode */
1876 default_hex_mode
= view
->hex_mode
;
1877 default_nroff_flag
= view
->text_nroff_mode
;
1878 default_magic_flag
= view
->magic_mode
;
1879 global_wrap_mode
= view
->text_wrap_mode
;
1882 /* view->widget needs no destructor */
1884 g_free (view
->filename
), view
->filename
= NULL
;
1885 g_free (view
->command
), view
->command
= NULL
;
1887 view_close_datasource (view
);
1888 /* the growing buffer is freed with the datasource */
1890 view_hexedit_free_change_list (view
);
1892 g_free(view
->last_search_string
);
1893 view
->last_search_string
= NULL
;
1895 /* Free memory used by the viewer */
1896 view_reduce_cache_lines (view
);
1897 if (view
->converter
!= str_cnv_from_term
) str_close_conv (view
->converter
);
1902 view_show_error (WView
*view
, const char *msg
)
1904 view_close_datasource (view
);
1905 if (view_is_in_panel (view
)) {
1906 view_set_datasource_string (view
, msg
);
1908 message (D_ERROR
, MSG_ERROR
, "%s", msg
);
1913 view_load_command_output (WView
*view
, const char *command
)
1917 view_close_datasource (view
);
1920 if ((fp
= popen (command
, "r")) == NULL
) {
1921 /* Avoid two messages. Message from stderr has priority. */
1923 if (!close_error_pipe (view_is_in_panel (view
) ? -1 : D_ERROR
, NULL
))
1924 view_show_error (view
, _(" Cannot spawn child process "));
1928 /* First, check if filter produced any output */
1929 view_set_datasource_stdio_pipe (view
, fp
);
1930 if (get_byte (view
, 0) == -1) {
1931 view_close_datasource (view
);
1933 /* Avoid two messages. Message from stderr has priority. */
1935 if (!close_error_pipe (view_is_in_panel (view
) ? -1 : D_ERROR
, NULL
))
1936 view_show_error (view
, _("Empty output from child filter"));
1943 view_load (WView
*view
, const char *command
, const char *file
,
1948 char tmp
[BUF_MEDIUM
];
1952 gboolean retval
= FALSE
;
1954 assert (view
->bytes_per_line
!= 0);
1958 /* Set up the state */
1959 view_set_datasource_none (view
);
1960 view
->filename
= g_strdup (file
);
1963 /* Clear the markers */
1965 for (i
= 0; i
< 10; i
++)
1968 if (!view_is_in_panel (view
)) {
1969 view
->dpy_text_column
= 0;
1972 if (command
&& (view
->magic_mode
|| file
== NULL
|| file
[0] == '\0')) {
1973 retval
= view_load_command_output (view
, command
);
1974 } else if (file
!= NULL
&& file
[0] != '\0') {
1976 if ((fd
= mc_open (file
, O_RDONLY
| O_NONBLOCK
)) == -1) {
1977 g_snprintf (tmp
, sizeof (tmp
), _(" Cannot open \"%s\"\n %s "),
1978 file
, unix_error_string (errno
));
1979 view_show_error (view
, tmp
);
1980 g_free (view
->filename
);
1981 view
->filename
= NULL
;
1985 /* Make sure we are working with a regular file */
1986 if (mc_fstat (fd
, &st
) == -1) {
1988 g_snprintf (tmp
, sizeof (tmp
), _(" Cannot stat \"%s\"\n %s "),
1989 file
, unix_error_string (errno
));
1990 view_show_error (view
, tmp
);
1991 g_free (view
->filename
);
1992 view
->filename
= NULL
;
1996 if (!S_ISREG (st
.st_mode
)) {
1998 view_show_error (view
, _(" Cannot view: not a regular file "));
1999 g_free (view
->filename
);
2000 view
->filename
= NULL
;
2004 if (st
.st_size
== 0 || mc_lseek (fd
, 0, SEEK_SET
) == -1) {
2005 /* Must be one of those nice files that grow (/proc) */
2006 view_set_datasource_vfs_pipe (view
, fd
);
2008 type
= get_compression_type (fd
);
2010 if (view
->magic_mode
&& (type
!= COMPRESSION_NONE
)) {
2011 g_free (view
->filename
);
2012 view
->filename
= g_strconcat (file
, decompress_extension (type
), (char *) NULL
);
2014 view_set_datasource_file (view
, fd
, &st
);
2020 view
->command
= g_strdup (command
);
2021 view
->dpy_start
= 0;
2022 view
->search_start
= 0;
2023 view
->search_end
= 0;
2024 view
->dpy_text_column
= 0;
2026 view
->converter
= str_cnv_from_term
;
2027 /* try detect encoding from path */
2028 if (view
->filename
!= NULL
) {
2029 canon_fname
= vfs_canon (view
->filename
);
2030 enc
= vfs_get_encoding (canon_fname
);
2032 view
->converter
= str_crt_conv_from (enc
);
2033 if (view
->converter
== INVALID_CONV
)
2034 view
->converter
= str_cnv_from_term
;
2036 g_free (canon_fname
);
2039 view_compute_areas (view
);
2040 view_reset_cache_lines (view
);
2041 view
->first_showed_line
= view_get_first_line (view
);
2043 assert (view
->bytes_per_line
!= 0);
2044 if (mcview_remember_file_position
&& view
->filename
!= NULL
&& start_line
== 0) {
2047 canon_fname
= vfs_canon (view
->filename
);
2048 load_file_position (file
, &line
, &col
);
2049 g_free (canon_fname
);
2050 view_moveto (view
, offset_doz(line
, 1), col
);
2051 } else if (start_line
> 0) {
2052 view_moveto (view
, start_line
- 1, 0);
2055 view
->hexedit_lownibble
= FALSE
;
2056 view
->hexview_in_text
= FALSE
;
2057 view
->change_list
= NULL
;
2062 /* {{{ Display management }}} */
2065 view_update_bytes_per_line (WView
*view
)
2067 const screen_dimen cols
= view
->data_area
.width
;
2073 bytes
= 4 * ((cols
- 8) / ((cols
< 80) ? 17 : 18));
2076 view
->bytes_per_line
= bytes
;
2077 view
->dirty
= max_dirt_limit
+ 1; /* To force refresh */
2081 view_percent (WView
*view
, offset_type p
)
2083 const screen_dimen top
= view
->status_area
.top
;
2084 const screen_dimen right
= view
->status_area
.left
+ view
->status_area
.width
;
2085 const screen_dimen height
= view
->status_area
.height
;
2087 offset_type filesize
;
2089 if (height
< 1 || right
< 4)
2091 if (view_may_still_grow (view
))
2093 filesize
= view_get_filesize (view
);
2095 if (filesize
== 0 || view
->dpy_end
== filesize
)
2097 else if (p
> (INT_MAX
/ 100))
2098 percent
= p
/ (filesize
/ 100);
2100 percent
= p
* 100 / filesize
;
2102 widget_move (view
, top
, right
- 4);
2103 tty_printf ("%3d%%", percent
);
2107 view_display_status (WView
*view
)
2109 const screen_dimen top
= view
->status_area
.top
;
2110 const screen_dimen left
= view
->status_area
.left
;
2111 const screen_dimen width
= view
->status_area
.width
;
2112 const screen_dimen height
= view
->status_area
.height
;
2113 const char *file_label
, *file_name
;
2114 screen_dimen file_label_width
;
2121 tty_setcolor (SELECTED_COLOR
);
2122 widget_move (view
, top
, left
);
2125 file_label
= _("File: %s");
2126 file_label_width
= str_term_width1 (file_label
) - 2;
2127 file_name
= view
->filename
? view
->filename
2128 : view
->command
? view
->command
2131 if (width
< file_label_width
+ 6)
2132 addstr (str_fit_to_term (file_name
, width
, J_LEFT_FIT
));
2134 i
= (width
> 22 ? 22 : width
) - file_label_width
;
2136 tmp
= g_strdup_printf (file_label
, str_fit_to_term (file_name
, i
, J_LEFT_FIT
));
2140 widget_move (view
, top
, left
+ 24);
2141 /* FIXME: the format strings need to be changed when offset_type changes */
2143 tty_printf (_("Offset 0x%08lx"), (unsigned long) view
->hex_cursor
);
2145 screen_dimen row
, col
;
2146 struct cache_line
*line
;
2148 line
= view_get_first_showed_line (view
);
2149 row
= line
->number
+ 1;
2151 col
= (view
->text_wrap_mode
) ?
2152 view_width_of_whole_line_before (view
, line
) :
2153 view
->dpy_text_column
;
2156 tty_printf (_("Line %lu Col %lu"),
2157 (unsigned long) row
, (unsigned long) col
);
2161 offset_type filesize
;
2162 filesize
= view_get_filesize (view
);
2163 widget_move (view
, top
, left
+ 43);
2164 if (!view_may_still_grow (view
)) {
2165 tty_printf (_("%s bytes"), size_trunc (filesize
));
2167 tty_printf (_(">= %s bytes"), size_trunc (filesize
));
2171 view_percent (view
, view
->hex_mode
2176 tty_setcolor (SELECTED_COLOR
);
2180 view_display_clean (WView
*view
)
2182 tty_setcolor (NORMAL_COLOR
);
2183 widget_erase ((Widget
*) view
);
2184 if (view
->dpy_frame_size
!= 0) {
2185 draw_double_box (view
->widget
.parent
, view
->widget
.y
,
2186 view
->widget
.x
, view
->widget
.lines
,
2199 view_count_backspaces (WView
*view
, off_t offset
)
2202 while (offset
>= 2 * backspaces
2203 && get_byte (view
, offset
- 2 * backspaces
) == '\b')
2209 view_display_ruler (WView
*view
)
2211 static const char ruler_chars
[] = "|----*----";
2212 const screen_dimen top
= view
->ruler_area
.top
;
2213 const screen_dimen left
= view
->ruler_area
.left
;
2214 const screen_dimen width
= view
->ruler_area
.width
;
2215 const screen_dimen height
= view
->ruler_area
.height
;
2216 const screen_dimen line_row
= (ruler
== RULER_TOP
) ? 0 : 1;
2217 const screen_dimen nums_row
= (ruler
== RULER_TOP
) ? 1 : 0;
2223 if (ruler
== RULER_NONE
|| height
< 1)
2226 tty_setcolor (MARKED_COLOR
);
2227 for (c
= 0; c
< width
; c
++) {
2228 cl
= view
->dpy_text_column
+ c
;
2229 if (line_row
< height
) {
2230 widget_move (view
, top
+ line_row
, left
+ c
);
2231 tty_print_char (ruler_chars
[cl
% 10]);
2234 if ((cl
!= 0) && (cl
% 10) == 0) {
2235 g_snprintf (r_buff
, sizeof (r_buff
), "%"OFFSETTYPE_PRId
, cl
);
2236 if (nums_row
< height
) {
2237 widget_move (view
, top
+ nums_row
, left
+ c
- 1);
2238 tty_print_string (r_buff
);
2242 attrset (NORMAL_COLOR
);
2246 view_display_hex (WView
*view
)
2248 const screen_dimen top
= view
->data_area
.top
;
2249 const screen_dimen left
= view
->data_area
.left
;
2250 const screen_dimen height
= view
->data_area
.height
;
2251 const screen_dimen width
= view
->data_area
.width
;
2252 const int ngroups
= view
->bytes_per_line
/ 4;
2253 const screen_dimen text_start
=
2254 8 + 13 * ngroups
+ ((width
< 80) ? 0 : (ngroups
- 1 + 1));
2255 /* 8 characters are used for the file offset, and every hex group
2256 * takes 13 characters. On ``big'' screens, the groups are separated
2257 * by an extra vertical line, and there is an extra space before the
2261 screen_dimen row
, col
;
2264 mark_t boldflag
= MARK_NORMAL
;
2265 struct hexedit_change_node
*curr
= view
->change_list
;
2268 char hex_buff
[10]; /* A temporary buffer for sprintf and mvwaddstr */
2269 int bytes
; /* Number of bytes already printed on the line */
2271 view_display_clean (view
);
2273 /* Find the first displayable changed byte */
2274 from
= view
->dpy_start
;
2275 while (curr
&& (curr
->offset
< from
)) {
2279 for (row
= 0; get_byte (view
, from
) != -1 && row
< height
; row
++) {
2282 /* Print the hex offset */
2283 g_snprintf (hex_buff
, sizeof (hex_buff
), "%08"OFFSETTYPE_PRIX
" ", from
);
2284 widget_move (view
, top
+ row
, left
);
2285 tty_setcolor (MARKED_COLOR
);
2286 for (i
= 0; col
< width
&& hex_buff
[i
] != '\0'; i
++) {
2287 addch (hex_buff
[i
]);
2288 /* tty_print_char(hex_buff[i]);*/
2291 tty_setcolor (NORMAL_COLOR
);
2293 for (bytes
= 0; bytes
< view
->bytes_per_line
; bytes
++, from
++) {
2295 if ((c
= get_byte (view
, from
)) == -1)
2298 /* Save the cursor position for view_place_cursor() */
2299 if (from
== view
->hex_cursor
&& !view
->hexview_in_text
) {
2300 view
->cursor_row
= row
;
2301 view
->cursor_col
= col
;
2304 /* Determine the state of the current byte */
2306 (from
== view
->hex_cursor
) ? MARK_CURSOR
2307 : (curr
!= NULL
&& from
== curr
->offset
) ? MARK_CHANGED
2308 : (view
->search_start
<= from
&&
2309 from
< view
->search_end
) ? MARK_SELECTED
2312 /* Determine the value of the current byte */
2313 if (curr
!= NULL
&& from
== curr
->offset
) {
2318 /* Select the color for the hex number */
2320 boldflag
== MARK_NORMAL
? NORMAL_COLOR
:
2321 boldflag
== MARK_SELECTED
? MARKED_COLOR
:
2322 boldflag
== MARK_CHANGED
? VIEW_UNDERLINED_COLOR
:
2323 /* boldflag == MARK_CURSOR */
2324 view
->hexview_in_text
? MARKED_SELECTED_COLOR
:
2325 VIEW_UNDERLINED_COLOR
);
2327 /* Print the hex number */
2328 widget_move (view
, top
+ row
, left
+ col
);
2330 tty_print_char (hex_char
[c
/ 16]);
2334 tty_print_char (hex_char
[c
% 16]);
2338 /* Print the separator */
2339 tty_setcolor (NORMAL_COLOR
);
2340 if (bytes
!= view
->bytes_per_line
- 1) {
2342 tty_print_char (' ');
2346 /* After every four bytes, print a group separator */
2347 if (bytes
% 4 == 3) {
2348 if (view
->data_area
.width
>= 80 && col
< width
) {
2349 tty_print_one_vline ();
2353 tty_print_char (' ');
2359 /* Select the color for the character; this differs from the
2360 * hex color when boldflag == MARK_CURSOR */
2362 boldflag
== MARK_NORMAL
? NORMAL_COLOR
:
2363 boldflag
== MARK_SELECTED
? MARKED_COLOR
:
2364 boldflag
== MARK_CHANGED
? VIEW_UNDERLINED_COLOR
:
2365 /* boldflag == MARK_CURSOR */
2366 view
->hexview_in_text
? VIEW_UNDERLINED_COLOR
:
2367 MARKED_SELECTED_COLOR
);
2369 c
= convert_to_display_c (c
);
2370 if (!g_ascii_isprint (c
))
2373 /* Print corresponding character on the text side */
2374 if (text_start
+ bytes
< width
) {
2375 widget_move (view
, top
+ row
, left
+ text_start
+ bytes
);
2379 /* Save the cursor position for view_place_cursor() */
2380 if (from
== view
->hex_cursor
&& view
->hexview_in_text
) {
2381 view
->cursor_row
= row
;
2382 view
->cursor_col
= text_start
+ bytes
;
2387 /* Be polite to the other functions */
2388 tty_setcolor (NORMAL_COLOR
);
2390 view_place_cursor (view
);
2391 view
->dpy_end
= from
;
2395 view_display_text (WView
* view
)
2397 #define cmp(t1,t2) (strcmp((t1),(t2)) == 0)
2399 const screen_dimen left
= view
->data_area
.left
;
2400 const screen_dimen top
= view
->data_area
.top
;
2401 const screen_dimen width
= view
->data_area
.width
;
2402 const screen_dimen height
= view
->data_area
.height
;
2403 struct read_info info
;
2404 offset_type row
, col
;
2406 struct cache_line
*line_act
;
2407 struct cache_line
*line_nxt
;
2409 view_display_clean (view
);
2410 view_display_ruler (view
);
2412 tty_setcolor (NORMAL_COLOR
);
2414 widget_move (view
, top
, left
);
2416 line_act
= view_get_first_showed_line (view
);
2419 /* set col correct value */
2420 col
= (view
->text_wrap_mode
) ? 0 : view_width_of_whole_line_before (view
, line_act
);
2421 col
+= line_act
->left
;
2423 view_read_start (view
, &info
, line_act
->start
);
2424 while ((info
.result
!= -1) && (row
< height
)) {
2425 /* real detection of new line */
2426 if (info
.next
>= line_act
->end
) {
2427 line_nxt
= view_get_next_line (view
, line_act
);
2428 if (line_nxt
== NULL
) break;
2430 if (view
->text_wrap_mode
|| (line_act
->number
!= line_nxt
->number
)){
2432 col
= line_nxt
->left
;
2434 line_act
= line_nxt
;
2439 view_read_continue (view
, &info
);
2440 if (view_read_test_nroff_back (view
, &info
)) {
2443 w
= str_term_width1 (info
.chi1
);
2445 if (col
>= view
->dpy_text_column
2446 && col
+ w
- view
->dpy_text_column
<= width
) {
2448 widget_move (view
, top
+ row
, left
+ (col
- view
->dpy_text_column
));
2449 for (c
= 0; c
< w
; c
++) addch (' ');
2451 if (cmp (info
.chi1
, "_") && (!cmp (info
.cnxt
, "_") || !cmp (info
.chi2
, "\b")))
2452 tty_setcolor (VIEW_UNDERLINED_COLOR
);
2454 tty_setcolor (MARKED_COLOR
);
2458 if (view_read_test_new_line (view
, &info
))
2462 if (view_read_test_tabulator (view
, &info
)) {
2463 col
+= (8 - (col
% 8));
2467 if (view
->search_start
<= info
.actual
2468 && info
.actual
< view
->search_end
) {
2469 tty_setcolor (SELECTED_COLOR
);
2472 w
= str_isprint (info
.cact
) ? str_term_width1 (info
.cact
) : 1;
2474 if (col
>= view
->dpy_text_column
2475 && col
+ w
- view
->dpy_text_column
<= width
) {
2476 widget_move (view
, top
+ row
, left
+ (col
- view
->dpy_text_column
));
2478 if (!str_iscombiningmark (info
.cnxt
)) {
2479 if (str_isprint (info
.cact
)) {
2480 addstr (str_term_form (info
.cact
));
2485 GString
*comb
= g_string_new ("");
2486 if (str_isprint (info
.cact
)) {
2487 g_string_append(comb
,info
.cact
);
2489 g_string_append(comb
,".");
2491 while (str_iscombiningmark (info
.cnxt
)) {
2492 view_read_continue (view
, &info
);
2493 g_string_append(comb
,info
.cact
);
2495 addstr (str_term_form (comb
->str
));
2496 g_string_free (comb
, TRUE
);
2499 while (str_iscombiningmark (info
.cnxt
)) {
2500 view_read_continue (view
, &info
);
2505 tty_setcolor (NORMAL_COLOR
);
2507 view
->dpy_end
= info
.next
;
2510 /* Displays as much data from view->dpy_start as fits on the screen */
2512 display (WView
*view
)
2514 view_compute_areas (view
);
2515 if (view
->hex_mode
) {
2516 view_display_hex (view
);
2518 view_display_text (view
);
2520 view_display_status (view
);
2524 view_place_cursor (WView
*view
)
2526 const screen_dimen top
= view
->data_area
.top
;
2527 const screen_dimen left
= view
->data_area
.left
;
2530 col
= view
->cursor_col
;
2531 if (!view
->hexview_in_text
&& view
->hexedit_lownibble
)
2533 widget_move (&view
->widget
, top
+ view
->cursor_row
, left
+ col
);
2537 view_update (WView
*view
)
2539 static int dirt_limit
= 1;
2541 if (view
->dpy_bbar_dirty
) {
2542 view
->dpy_bbar_dirty
= FALSE
;
2544 buttonbar_redraw (view
->widget
.parent
);
2547 if (view
->dirty
> dirt_limit
) {
2548 /* Too many updates skipped -> force a update */
2551 /* Raise the update skipping limit */
2553 if (dirt_limit
> max_dirt_limit
)
2554 dirt_limit
= max_dirt_limit
;
2558 /* We have time to update the screen properly */
2564 /* We are busy -> skipping full update,
2565 only the status line is updated */
2566 view_display_status (view
);
2568 /* Here we had a refresh, if fast scrolling does not work
2569 restore the refresh, although this should not happen */
2573 /* {{{ Hex editor }}} */
2576 enqueue_change (struct hexedit_change_node
**head
,
2577 struct hexedit_change_node
*node
)
2579 /* chnode always either points to the head of the list or
2580 * to one of the ->next fields in the list. The value at
2581 * this location will be overwritten with the new node. */
2582 struct hexedit_change_node
**chnode
= head
;
2584 while (*chnode
!= NULL
&& (*chnode
)->offset
< node
->offset
)
2585 chnode
= &((*chnode
)->next
);
2587 node
->next
= *chnode
;
2592 view_handle_editkey (WView
*view
, int key
)
2594 struct hexedit_change_node
*node
;
2597 /* Has there been a change at this position? */
2598 node
= view
->change_list
;
2599 while (node
&& (node
->offset
!= view
->hex_cursor
))
2602 if (!view
->hexview_in_text
) {
2604 unsigned int hexvalue
= 0;
2606 if (key
>= '0' && key
<= '9')
2607 hexvalue
= 0 + (key
- '0');
2608 else if (key
>= 'A' && key
<= 'F')
2609 hexvalue
= 10 + (key
- 'A');
2610 else if (key
>= 'a' && key
<= 'f')
2611 hexvalue
= 10 + (key
- 'a');
2613 return MSG_NOT_HANDLED
;
2616 byte_val
= node
->value
;
2618 byte_val
= get_byte (view
, view
->hex_cursor
);
2620 if (view
->hexedit_lownibble
) {
2621 byte_val
= (byte_val
& 0xf0) | (hexvalue
);
2623 byte_val
= (byte_val
& 0x0f) | (hexvalue
<< 4);
2627 if (key
< 256 && (is_printable (key
) || (key
== '\n')))
2630 return MSG_NOT_HANDLED
;
2633 node
= g_new (struct hexedit_change_node
, 1);
2634 node
->offset
= view
->hex_cursor
;
2635 node
->value
= byte_val
;
2636 enqueue_change (&view
->change_list
, node
);
2638 node
->value
= byte_val
;
2642 view_move_right (view
, 1);
2647 view_hexedit_save_changes (WView
*view
)
2649 struct hexedit_change_node
*curr
, *next
;
2653 if (view
->change_list
== NULL
)
2657 assert (view
->filename
!= NULL
);
2658 fp
= mc_open (view
->filename
, O_WRONLY
);
2662 for (curr
= view
->change_list
; curr
!= NULL
; curr
= next
) {
2665 if (mc_lseek (fp
, curr
->offset
, SEEK_SET
) == -1
2666 || mc_write (fp
, &(curr
->value
), 1) != 1)
2669 /* delete the saved item from the change list */
2670 view
->change_list
= next
;
2672 view_set_byte (view
, curr
->offset
, curr
->value
);
2676 if (mc_close (fp
) == -1) {
2677 error
= g_strdup (strerror (errno
));
2678 message (D_ERROR
, _(" Save file "), _(
2679 " Error while closing the file: \n %s \n"
2680 " Data may have been written or not. "), error
);
2687 error
= g_strdup (strerror (errno
));
2688 text
= g_strdup_printf (_(" Cannot save file: \n %s "), error
);
2690 (void) mc_close (fp
);
2692 answer
= query_dialog (_(" Save file "), text
, D_ERROR
,
2693 2, _("&Retry"), _("&Cancel"));
2701 /* {{{ Miscellaneous functions }}} */
2704 view_ok_to_quit (WView
*view
)
2708 if (view
->change_list
== NULL
)
2711 r
= query_dialog (_("Quit"),
2712 _(" File was modified, Save with exit? "), D_NORMAL
, 3,
2713 _("&Cancel quit"), _("&Yes"), _("&No"));
2717 return view_hexedit_save_changes (view
);
2719 view_hexedit_free_change_list (view
);
2727 my_define (Dlg_head
*h
, int idx
, const char *text
, void (*fn
) (WView
*),
2730 buttonbar_set_label_data (h
, idx
, text
, (buttonbarfn
) fn
, view
);
2733 /* {{{ Searching }}} */
2735 /* read one whole line into buffer, return where line start and end */
2737 view_get_line_at (WView
*view
, offset_type from
, GString
* buffer
,
2738 offset_type
*buff_start
, offset_type
*buff_end
)
2740 #define cmp(t1,t2) (strcmp((t1),(t2)) == 0)
2741 struct read_info info
;
2742 struct cache_line
*line
;
2746 line
= view_get_first_showed_line (view
);
2748 line
= view_offset_to_line_from (view
, from
, line
);
2750 if (!view
->search_backwards
) {
2752 end
= view_get_end_of_whole_line (view
, line
)->end
;
2753 if (start
>= end
) return 0;
2755 start
= view_get_start_of_whole_line (view
, line
)->start
;
2759 (*buff_start
) = start
;
2762 g_string_set_size(buffer
,0);
2764 view_read_start (view
, &info
, start
);
2765 while ((info
.result
!= -1) && (info
.next
< end
)) {
2766 view_read_continue (view
, &info
);
2768 /* if text contains '\0' */
2769 if (cmp (info
.cact
, "")) {
2770 if (info
.actual
< from
) {
2771 /* '\0' before start offset, continue */
2772 g_string_set_size(buffer
,0);
2773 (*buff_start
) = info
.next
;
2776 /* '\0' after start offset, end */
2777 (*buff_end
) = info
.next
;
2782 if (view_read_test_new_line (view
, &info
))
2785 if (view_read_test_nroff_back (view
, &info
)) {
2786 g_string_truncate (buffer
, buffer
->len
-1);
2790 g_string_append(buffer
,info
.cact
);
2796 /* map search result positions to offsets in text */
2798 view_matchs_to_offsets (WView
*view
, offset_type start
, offset_type end
,
2799 size_t match_start
, size_t match_end
,
2800 offset_type
*search_start
, offset_type
*search_end
)
2802 struct read_info info
;
2805 (*search_start
) = INVALID_OFFSET
;
2806 (*search_end
) = INVALID_OFFSET
;
2808 view_read_start (view
, &info
, start
);
2810 while ((info
.result
!= -1) && (info
.next
< end
)) {
2811 view_read_continue (view
, &info
);
2813 if (view_read_test_nroff_back (view
, &info
)) {
2817 if ((c
== match_start
) && (*search_start
== INVALID_OFFSET
))
2818 *search_start
= info
.actual
;
2819 if (c
== match_end
) (*search_end
) = info
.actual
;
2820 c
+= !str_iscombiningmark (info
.cact
) || (c
== 0);
2823 if ((c
== match_start
) && (*search_start
== INVALID_OFFSET
)) *search_start
= info
.next
;
2824 if (c
== match_end
) (*search_end
) = info
.next
;
2827 /* we have set view->search_start and view->search_end and must set
2828 * view->dpy_text_column, view->first_showed_line and view->dpy_start
2829 * try to displaye maximum of match */
2831 view_moveto_match (WView
*view
)
2833 const screen_dimen height
= view
->data_area
.height
;
2834 const screen_dimen height3
= height
/ 3;
2835 const screen_dimen width
= view
->data_area
.width
;
2836 struct cache_line
*line
;
2837 struct cache_line
*line_end
, *line_start
;
2838 struct cache_line
*t
;
2843 line
= view_get_first_showed_line (view
);
2844 if (view
->text_wrap_mode
) {
2845 if (line
->start
> view
->search_start
) {
2846 if (line
->start
<= view
->search_start
&& line
->end
> view
->search_start
)
2848 if (line
->start
<= view
->search_end
&& line
->end
>= view
->search_end
)
2850 t
= view_get_previous_line (view
, line
);
2851 while ((t
!= NULL
) && ((start_off
== -1) || (end_off
== -1))) {
2853 t
= view_get_previous_line (view
, line
);
2855 if (line
->start
<= view
->search_start
&& line
->end
> view
->search_start
)
2857 if (line
->start
<= view
->search_end
&& line
->end
>= view
->search_end
)
2861 line
= view_get_first_showed_line (view
);
2863 off
= ((off_t
)(start_off
- end_off
) < (off_t
)(height
- height3
))
2864 ? (int)(start_off
+ height3
)
2866 for (;off
>= 0 && line
->start
> 0; off
--)
2867 line
= view_get_previous_line (view
, line
);
2869 /* start_off, end_off - how many cache_lines far are
2870 * view->search_start, end from line */
2871 if (line
->start
<= view
->search_start
&& line
->end
> view
->search_start
)
2873 if (line
->start
<= view
->search_end
&& line
->end
>= view
->search_end
)
2875 t
= view_get_next_line (view
, line
);
2876 while ((t
!= NULL
) && ((start_off
== -1) || (end_off
== -1))) {
2878 t
= view_get_next_line (view
, line
);
2880 if (line
->start
<= view
->search_start
&& line
->end
> view
->search_start
)
2882 if (line
->start
<= view
->search_end
&& line
->end
>= view
->search_end
)
2886 line
= view_get_first_showed_line (view
);
2887 /* if view->search_end is farther then screen heigth */
2888 if ((off_t
)end_off
>= height
) {
2889 off
= ((off_t
)(end_off
- start_off
) < (off_t
)(height
- height3
))
2890 ? (int) (end_off
- height
+ height3
)
2893 for (;off
>= 0; off
--)
2894 line
= view_get_next_line (view
, line
);
2898 /* first part similar like in wrap mode,only wokrs with whole lines */
2899 line
= view_get_first_showed_line (view
);
2900 line
= view_get_start_of_whole_line (view
, line
);
2901 if (line
->start
> view
->search_start
) {
2902 line_start
= view_get_start_of_whole_line (view
, line
);
2903 if (line_start
->start
<= view
->search_start
&& line
->end
> view
->search_start
)
2905 if (line_start
->start
<= view
->search_end
&& line
->end
>= view
->search_end
)
2907 t
= view_get_previous_whole_line (view
, line_start
);
2908 while ((t
!= NULL
) && ((start_off
== -1) || (end_off
== -1))) {
2910 line_start
= view_get_start_of_whole_line (view
, line
);
2911 t
= view_get_previous_whole_line (view
, line_start
);
2913 if (line_start
->start
<= view
->search_start
&& line
->end
> view
->search_start
)
2915 if (line_start
->start
<= view
->search_end
&& line
->end
>= view
->search_end
)
2919 line
= view_get_first_showed_line (view
);
2920 line
= view_get_start_of_whole_line (view
, line
);
2921 off
= ((off_t
)(start_off
- end_off
) < (off_t
)(height
- height3
))
2922 ? (int)(start_off
+ height3
)
2924 for (;off
>= 0 && line
->start
> 0; off
--) {
2925 line
= view_get_previous_whole_line (view
, line
);
2926 line
= view_get_start_of_whole_line (view
, line
);
2929 line_end
= view_get_end_of_whole_line (view
, line
);
2930 if (line
->start
<= view
->search_start
&& line_end
->end
> view
->search_start
)
2932 if (line
->start
<= view
->search_end
&& line_end
->end
>= view
->search_end
)
2934 t
= view_get_next_whole_line (view
, line_end
);
2935 while ((t
!= NULL
) && ((start_off
== -1) || (end_off
== -1))) {
2937 line_end
= view_get_end_of_whole_line (view
, line
);
2938 t
= view_get_next_whole_line (view
, line_end
);
2940 if (line
->start
<= view
->search_start
&& line_end
->end
> view
->search_start
)
2942 if (line
->start
<= view
->search_end
&& line_end
->end
>= view
->search_end
)
2946 line
= view_get_first_showed_line (view
);
2947 line
= view_get_start_of_whole_line (view
, line
);
2948 if ((off_t
)end_off
>= height
) {
2949 off
= ((off_t
)(end_off
- start_off
) < (off_t
)(height
- height3
))
2950 ? (int)(end_off
- height
+ height3
)
2953 for (;off
>= 0; off
--)
2954 line
= view_get_next_whole_line (view
, line
);
2957 /*now line point to begin of line, that we want show*/
2959 t
= view_offset_to_line_from (view
, view
->search_start
, line
);
2960 start_off
= view_offset_to_column (view
, t
, view
->search_start
);
2961 t
= view_offset_to_line_from (view
, view
->search_end
, line
);
2962 end_off
= view_offset_to_column (view
, t
, view
->search_end
);
2964 if ((off_t
)(end_off
- start_off
) > width
) end_off
= start_off
+ width
;
2965 if (view
->dpy_text_column
> (off_t
)start_off
) {
2966 view
->dpy_text_column
= start_off
;
2968 if (view
->dpy_text_column
+ width
< (off_t
)end_off
) {
2969 view
->dpy_text_column
= end_off
- width
;
2974 view_set_first_showed (view
, line
);
2978 search_update_steps (WView
*view
)
2980 offset_type filesize
= view_get_filesize (view
);
2982 view
->update_steps
= 40000;
2983 else /* viewing a data stream, not a file */
2984 view
->update_steps
= filesize
/ 100;
2986 /* Do not update the percent display but every 20 ks */
2987 if (view
->update_steps
< 20000)
2988 view
->update_steps
= 20000;
2991 /* {{{ User-definable commands }}} */
2994 The functions in this section can be bound to hotkeys. They are all
2995 of the same type (taking a pointer to WView as parameter and
2996 returning void). TODO: In the not-too-distant future, these commands
2997 will become fully configurable, like they already are in the
2998 internal editor. By convention, all the function names end in
3003 view_help_cmd (void)
3005 interactive_display (NULL
, "[Internal File Viewer]");
3008 /* Toggle between hexview and hexedit mode */
3010 view_toggle_hexedit_mode_cmd (WView
*view
)
3012 view_toggle_hexedit_mode (view
);
3016 /* Toggle between wrapped and unwrapped view */
3018 view_toggle_wrap_mode_cmd (WView
*view
)
3020 view_toggle_wrap_mode (view
);
3024 /* Toggle between hex view and text view */
3026 view_toggle_hex_mode_cmd (WView
*view
)
3028 view_toggle_hex_mode (view
);
3033 view_moveto_line_cmd (WView
*view
)
3035 char *answer
, *answer_end
, prompt
[BUF_SMALL
];
3036 struct cache_line
*line
;
3039 line
= view_get_first_showed_line (view
);
3040 row
= line
->number
+ 1;
3042 g_snprintf (prompt
, sizeof (prompt
),
3043 _(" The current line number is %lu.\n"
3044 " Enter the new line number:"), line
->number
);
3045 answer
= input_dialog (_(" Goto line "), prompt
, MC_HISTORY_VIEW_GOTO_LINE
, "");
3046 if (answer
!= NULL
&& answer
[0] != '\0') {
3048 row
= strtoul (answer
, &answer_end
, 10);
3049 if (*answer_end
== '\0' && errno
== 0 && row
>= 1)
3050 view_moveto (view
, row
- 1, 0);
3058 view_moveto_addr_cmd (WView
*view
)
3060 char *line
, *error
, prompt
[BUF_SMALL
];
3063 g_snprintf (prompt
, sizeof (prompt
),
3064 _(" The current address is 0x%08"OFFSETTYPE_PRIX
".\n"
3065 " Enter the new address:"), view
->hex_cursor
);
3067 line
= input_dialog (_(" Goto Address "), prompt
, MC_HISTORY_VIEW_GOTO_ADDR
, "");
3069 if (*line
!= '\0') {
3070 addr
= strtoul (line
, &error
, 0);
3071 if ((*error
== '\0') && get_byte (view
, addr
) != -1) {
3072 view_moveto_offset (view
, addr
);
3074 message (D_ERROR
, _("Warning"), _(" Invalid address "));
3084 view_hexedit_save_changes_cmd (WView
*view
)
3086 (void) view_hexedit_save_changes (view
);
3091 do_search (WView
*view
)
3094 offset_type search_start
;
3098 offset_type line_start
;
3099 offset_type line_end
;
3103 d
= create_message (D_NORMAL
, _("Search"), _("Searching %s"), view
->last_search_string
);
3107 buffer
= g_string_new ("");
3109 search_start
= (view
->search_backwards
) ? view
->search_start
: view
->search_end
;
3111 /* Compute the percent steps */
3112 search_update_steps (view
);
3113 view
->update_activate
= 0;
3115 enable_interrupt_key ();
3119 if (search_start
>= view
->update_activate
) {
3120 view
->update_activate
+= view
->update_steps
;
3122 view_percent (view
, search_start
);
3125 if (got_interrupt ())
3129 if (!view_get_line_at (view
, search_start
, buffer
, &line_start
, &line_end
))
3132 if (! mc_search_run( view
->search
, buffer
->str
, 0, buffer
->len
, &match_len
)){
3133 if (view
->search
->error
!= MC_SEARCH_E_NOTFOUND
) {
3138 if (! view
->search_backwards
) {
3139 search_start
= line_end
;
3141 if (line_start
> 0) search_start
= line_start
- 1;
3148 view
->search_start
= view
->search
->normal_offset
+search_start
;
3149 view
->search_end
= view
->search_start
+ match_len
;
3151 if (view
->hex_mode
){
3152 view
->hex_cursor
= view
->search_start
;
3153 view
->hexedit_lownibble
= FALSE
;
3155 view
->dpy_start
= view
->search_start
- view
->search_start
% view
->bytes_per_line
;
3156 view
->dpy_end
= view
->search_end
- view
->search_end
% view
->bytes_per_line
;
3160 view_moveto_match (view
);
3164 disable_interrupt_key ();
3169 switch (search_status
)
3172 message (D_NORMAL
, _("Search"), _(" Search string not found "));
3173 view
->search_end
= view
->search_start
;
3176 message (D_NORMAL
, _("Search"), "%s", view
->search
->error_str
);
3177 view
->search_end
= view
->search_start
;
3180 g_string_free (buffer
, TRUE
);
3189 view_search_cmd (WView
*view
)
3192 SEARCH_DLG_MIN_HEIGHT
= 10,
3193 SEARCH_DLG_HEIGHT_SUPPLY
= 3,
3194 SEARCH_DLG_WIDTH
= 58
3197 char *defval
= g_strdup (view
->last_search_string
!= NULL
? view
->last_search_string
: "");
3200 int ttype_of_search
= (int) view
->search_type
;
3201 int tall_codepages
= (int) view
->search_all_codepages
;
3202 int tsearch_case
= (int) view
->search_case
;
3203 int tsearch_backwards
= (int) view
->search_backwards
;
3205 gchar
**list_of_types
= mc_search_get_types_strings_array();
3206 int SEARCH_DLG_HEIGHT
= SEARCH_DLG_MIN_HEIGHT
+ g_strv_length (list_of_types
) - SEARCH_DLG_HEIGHT_SUPPLY
;
3208 QuickWidget quick_widgets
[] = {
3210 {quick_button
, 6, 10, SEARCH_DLG_HEIGHT
- 3, SEARCH_DLG_HEIGHT
, N_("&Cancel"), 0,
3211 B_CANCEL
, 0, 0, NULL
, NULL
, NULL
},
3213 {quick_button
, 2, 10, SEARCH_DLG_HEIGHT
- 3, SEARCH_DLG_HEIGHT
, N_("&OK"), 0, B_ENTER
,
3214 0, 0, NULL
, NULL
, NULL
},
3216 {quick_checkbox
, SEARCH_DLG_WIDTH
/2 + 3, SEARCH_DLG_WIDTH
, 6, SEARCH_DLG_HEIGHT
, N_("All charsets"), 0, 0,
3217 &tall_codepages
, 0, NULL
, NULL
, NULL
},
3219 {quick_checkbox
, SEARCH_DLG_WIDTH
/2 + 3, SEARCH_DLG_WIDTH
, 5, SEARCH_DLG_HEIGHT
,
3220 N_("&Backwards"), 0, 0, &tsearch_backwards
, 0, NULL
, NULL
, NULL
},
3222 {quick_checkbox
, SEARCH_DLG_WIDTH
/2 + 3, SEARCH_DLG_WIDTH
, 4, SEARCH_DLG_HEIGHT
, N_("case &Sensitive"), 0, 0,
3223 &tsearch_case
, 0, NULL
, NULL
, NULL
},
3225 {quick_radio
, 3, SEARCH_DLG_WIDTH
, 4, SEARCH_DLG_HEIGHT
, 0, g_strv_length (list_of_types
), ttype_of_search
,
3226 (void *) &ttype_of_search
, const_cast (char **, list_of_types
), NULL
, NULL
, NULL
},
3229 {quick_input
, 3, SEARCH_DLG_WIDTH
, 3, SEARCH_DLG_HEIGHT
, defval
, 52, 0,
3230 0, &exp
, N_("Search"), NULL
, NULL
},
3232 {quick_label
, 2, SEARCH_DLG_WIDTH
, 2, SEARCH_DLG_HEIGHT
,
3233 N_(" Enter search string:"), 0, 0, 0, 0, 0, NULL
, NULL
},
3238 QuickDialog Quick_input
= {
3239 SEARCH_DLG_WIDTH
, SEARCH_DLG_HEIGHT
, -1, 0, N_("Search"),
3240 "[Input Line Keys]", quick_widgets
, 0
3243 convert_to_display (defval
);
3246 if (quick_dialog (&Quick_input
) == B_CANCEL
)
3249 view
->search_backwards
= tsearch_backwards
;
3250 view
->search_type
= (mc_search_type_t
) ttype_of_search
;
3252 view
->search_all_codepages
= (gboolean
) tall_codepages
;
3253 view
->search_case
= (gboolean
) tsearch_case
;
3255 if (exp
== NULL
|| exp
[0] == '\0')
3258 convert_from_input (exp
);
3260 g_free (view
->last_search_string
);
3261 view
->last_search_string
= exp
;
3265 mc_search_free(view
->search
);
3267 view
->search
= mc_search_new(view
->last_search_string
, -1);
3271 view
->search
->search_type
= view
->search_type
;
3272 view
->search
->is_all_charsets
= view
->search_all_codepages
;
3273 view
->search
->is_case_sentitive
= view
->search_case
;
3283 view_toggle_magic_mode_cmd (WView
*view
)
3285 view_toggle_magic_mode (view
);
3290 view_toggle_nroff_mode_cmd (WView
*view
)
3292 view_toggle_nroff_mode (view
);
3297 view_quit_cmd (WView
*view
)
3299 if (view_ok_to_quit (view
))
3300 dlg_stop (view
->widget
.parent
);
3303 /* {{{ Miscellaneous functions }}} */
3305 /* Define labels and handlers for functional keys */
3307 view_labels (WView
*view
)
3310 Dlg_head
*h
= view
->widget
.parent
;
3312 buttonbar_set_label (h
, 1, Q_("ButtonBar|Help"), view_help_cmd
);
3314 my_define (h
, 10, Q_("ButtonBar|Quit"), view_quit_cmd
, view
);
3315 text
= view
->hex_mode
? "ButtonBar|Ascii" : "ButtonBar|Hex";
3316 my_define (h
, 4, Q_(text
), view_toggle_hex_mode_cmd
, view
);
3317 text
= view
->hex_mode
?"ButtonBar|Goto": "ButtonBar|Line";
3318 my_define (h
, 5, Q_(text
),
3319 view
->hex_mode
? view_moveto_addr_cmd
: view_moveto_line_cmd
, view
);
3321 if (view
->hex_mode
) {
3322 if (view
->hexedit_mode
) {
3323 my_define (h
, 2, Q_("ButtonBar|View"),
3324 view_toggle_hexedit_mode_cmd
, view
);
3325 } else if (view
->datasource
== DS_FILE
) {
3326 my_define (h
, 2, Q_("ButtonBar|Edit"),
3327 view_toggle_hexedit_mode_cmd
, view
);
3329 buttonbar_clear_label (h
, 2);
3331 my_define (h
, 6, Q_("ButtonBar|Save"),
3332 view_hexedit_save_changes_cmd
, view
);
3334 text
= view
->text_wrap_mode
? "ButtonBar|UnWrap" : "ButtonBar|Wrap";
3335 my_define (h
, 2, Q_(text
), view_toggle_wrap_mode_cmd
, view
);
3338 text
= view
->hex_mode
? "ButtonBar|HxSrch" : "ButtonBar|Search";
3339 my_define (h
, 7, Q_(text
), view_search_cmd
, view
);
3340 text
= view
->magic_mode
? "ButtonBar|Raw" : "ButtonBar|Parse";
3341 my_define (h
, 8, Q_(text
), view_toggle_magic_mode_cmd
, view
);
3343 /* don't override the key to access the main menu */
3344 if (!view_is_in_panel (view
)) {
3345 text
= view
->text_nroff_mode
? "ButtonBar|Unform" : "ButtonBar|Format";
3346 my_define (h
, 9, Q_(text
), view_toggle_nroff_mode_cmd
, view
);
3347 my_define (h
, 3, Q_("ButtonBar|Quit"), view_quit_cmd
, view
);
3351 /* {{{ Event handling }}} */
3353 /* Check for left and right arrows, possibly with modifiers */
3355 check_left_right_keys (WView
*view
, int c
)
3357 if (c
== KEY_LEFT
) {
3358 view_move_left (view
, 1);
3362 if (c
== KEY_RIGHT
) {
3363 view_move_right (view
, 1);
3367 /* Ctrl with arrows moves by 10 postions in the unwrap mode */
3368 if (view
->hex_mode
|| view
->text_wrap_mode
)
3369 return MSG_NOT_HANDLED
;
3371 if (c
== (KEY_M_CTRL
| KEY_LEFT
)) {
3372 if (view
->dpy_text_column
>= 10)
3373 view
->dpy_text_column
-= 10;
3375 view
->dpy_text_column
= 0;
3380 if (c
== (KEY_M_CTRL
| KEY_RIGHT
)) {
3381 if (view
->dpy_text_column
<= OFFSETTYPE_MAX
- 10)
3382 view
->dpy_text_column
+= 10;
3384 view
->dpy_text_column
= OFFSETTYPE_MAX
;
3389 return MSG_NOT_HANDLED
;
3392 /* {{{ User-definable commands }}} */
3395 view_continue_search_cmd (WView
*view
)
3397 if (view
->last_search_string
!=NULL
) {
3400 /* if not... then ask for an expression */
3401 view_search_cmd (view
);
3406 view_toggle_ruler_cmd (WView
*view
)
3408 static const enum ruler_type next
[3] = {
3414 assert ((size_t) ruler
< 3);
3415 ruler
= next
[(size_t) ruler
];
3419 /* {{{ Event handling }}} */
3421 static void view_cmk_move_up (void *w
, int n
) {
3422 view_move_up ((WView
*) w
, n
);
3424 static void view_cmk_move_down (void *w
, int n
) {
3425 view_move_down ((WView
*) w
, n
);
3427 static void view_cmk_moveto_top (void *w
, int n
) {
3429 view_moveto_top ((WView
*) w
);
3431 static void view_cmk_moveto_bottom (void *w
, int n
) {
3433 view_moveto_bottom ((WView
*) w
);
3437 view_select_encoding (WView
*view
)
3441 struct cache_line
*line
;
3444 do_select_codepage ();
3445 enc
= g_strdup( get_codepage_id ( source_codepage
) );
3448 conv
= str_crt_conv_from (enc
);
3449 if (conv
!= INVALID_CONV
) {
3450 if (view
->converter
!= str_cnv_from_term
)
3451 str_close_conv (view
->converter
);
3452 view
->converter
= conv
;
3453 view_reset_cache_lines (view
);
3454 line
= view_offset_to_line (view
, view
->dpy_start
);
3455 view_set_first_showed (view
, line
);
3465 view_handle_key (WView
*view
, int c
)
3467 c
= convert_from_input_c (c
);
3469 if (view
->hex_mode
) {
3472 view
->hexview_in_text
= !view
->hexview_in_text
;
3477 view_moveto_bol (view
);
3482 view_move_left (view
, 1);
3486 view_moveto_eol (view
);
3490 view_move_right (view
, 1);
3494 if (view
->hexedit_mode
3495 && view_handle_editkey (view
, c
) == MSG_HANDLED
)
3499 if (check_left_right_keys (view
, c
))
3502 if (check_movement_keys (c
, view
->data_area
.height
+ 1, view
,
3503 view_cmk_move_up
, view_cmk_move_down
,
3504 view_cmk_moveto_top
, view_cmk_moveto_bottom
))
3511 view
->search_type
= MC_SEARCH_T_REGEX
;
3512 view_search_cmd(view
);
3515 /* Continue search */
3520 view_continue_search_cmd (view
);
3525 view_toggle_ruler_cmd (view
);
3529 view_move_left (view
, 1);
3535 view_move_down (view
, 1);
3539 view_move_down (view
, (view
->data_area
.height
+ 1) / 2);
3543 view_move_up (view
, (view
->data_area
.height
+ 1) / 2);
3548 view_move_up (view
, 1);
3552 view_move_right (view
, 1);
3557 view_move_down (view
, view
->data_area
.height
);
3564 /* Unlike Ctrl-O, run a new shell if the subshell is not running. */
3570 view_move_up (view
, view
->data_area
.height
);
3574 view_move_up (view
, 2);
3578 view_move_down (view
, 2);
3582 view
->marks
[view
->marker
] = view
->dpy_start
;
3586 view
->dpy_start
= view
->marks
[view
->marker
];
3590 /* Use to indicate parent that we want to see the next/previous file */
3591 /* Does not work in panel mode */
3594 if (!view_is_in_panel (view
))
3595 view
->move_dir
= c
== XCTRL ('f') ? 1 : -1;
3600 if (view_ok_to_quit (view
))
3601 view
->want_to_quit
= TRUE
;
3605 view_select_encoding (view
);
3610 #ifdef MC_ENABLE_DEBUGGING_CODE
3611 case 't': /* mnemonic: "test" */
3612 view_ccache_dump (view
);
3616 if (c
>= '0' && c
<= '9')
3617 view
->marker
= c
- '0';
3620 return MSG_NOT_HANDLED
;
3625 view_event (WView
*view
, Gpm_Event
*event
, int *result
)
3629 *result
= MOU_NORMAL
;
3631 /* We are not interested in the release events */
3632 if (!(event
->type
& (GPM_DOWN
| GPM_DRAG
)))
3636 if ((event
->buttons
& GPM_B_UP
) && (event
->type
& GPM_DOWN
)) {
3637 view_move_up (view
, 2);
3640 if ((event
->buttons
& GPM_B_DOWN
) && (event
->type
& GPM_DOWN
)) {
3641 view_move_down (view
, 2);
3648 /* Scrolling left and right */
3649 if (!view
->text_wrap_mode
) {
3650 if (x
< view
->data_area
.width
* 1/4) {
3651 view_move_left (view
, 1);
3653 } else if (x
< view
->data_area
.width
* 3/4) {
3654 /* ignore the click */
3656 view_move_right (view
, 1);
3661 /* Scrolling up and down */
3662 if (y
< view
->data_area
.top
+ view
->data_area
.height
* 1/3) {
3663 if (mouse_move_pages_viewer
)
3664 view_move_up (view
, view
->data_area
.height
/ 2);
3666 view_move_up (view
, 1);
3668 } else if (y
< view
->data_area
.top
+ view
->data_area
.height
* 2/3) {
3669 /* ignore the click */
3671 if (mouse_move_pages_viewer
)
3672 view_move_down (view
, view
->data_area
.height
/ 2);
3674 view_move_down (view
, 1);
3681 *result
= MOU_REPEAT
;
3685 /* Real view only */
3687 real_view_event (Gpm_Event
*event
, void *x
)
3689 WView
*view
= (WView
*) x
;
3692 if (view_event (view
, event
, &result
))
3698 view_adjust_size (Dlg_head
*h
)
3703 /* Look up the viewer and the buttonbar, we assume only two widgets here */
3704 view
= (WView
*) find_widget_type (h
, view_callback
);
3705 bar
= find_buttonbar (h
);
3706 widget_set_size (&view
->widget
, 0, 0, LINES
- 1, COLS
);
3707 widget_set_size ((Widget
*) bar
, LINES
- 1, 0, 1, COLS
);
3709 view_compute_areas (view
);
3710 view_update_bytes_per_line (view
);
3713 /* Callback for the view dialog */
3715 view_dialog_callback (Dlg_head
*h
, dlg_msg_t msg
, int parm
)
3719 view_adjust_size (h
);
3723 return default_dlg_callback (h
, msg
, parm
);
3727 /* {{{ External interface }}} */
3729 /* Real view only */
3731 mc_internal_viewer (const char *command
, const char *file
,
3732 int *move_dir_p
, int start_line
)
3739 /* Create dialog and widgets, put them on the dialog */
3741 create_dlg (0, 0, LINES
, COLS
, NULL
, view_dialog_callback
,
3742 "[Internal File Viewer]", NULL
, DLG_WANT_TAB
);
3744 wview
= view_new (0, 0, COLS
, LINES
- 1, 0);
3746 bar
= buttonbar_new (1);
3748 add_widget (view_dlg
, bar
);
3749 add_widget (view_dlg
, wview
);
3751 succeeded
= view_load (wview
, command
, file
, start_line
);
3755 *move_dir_p
= wview
->move_dir
;
3760 destroy_dlg (view_dlg
);
3765 /* {{{ Miscellaneous functions }}} */
3770 WView
*view
= (WView
*) v
;
3773 /* If the user is busy typing, wait until he finishes to update the
3776 if (!hook_present (idle_hook
, view_hook
))
3777 add_hook (&idle_hook
, view_hook
, v
);
3781 delete_hook (&idle_hook
, view_hook
);
3783 if (get_current_type () == view_listing
)
3784 panel
= current_panel
;
3785 else if (get_other_type () == view_listing
)
3786 panel
= other_panel
;
3790 view_load (view
, 0, panel
->dir
.list
[panel
->selected
].fname
, 0);
3794 /* {{{ Event handling }}} */
3797 view_callback (Widget
*w
, widget_msg_t msg
, int parm
)
3799 WView
*view
= (WView
*) w
;
3801 Dlg_head
*h
= view
->widget
.parent
;
3803 view_compute_areas (view
);
3804 view_update_bytes_per_line (view
);
3808 if (view_is_in_panel (view
))
3809 add_hook (&select_file_hook
, view_hook
, view
);
3811 view
->dpy_bbar_dirty
= TRUE
;
3820 view_place_cursor (view
);
3824 i
= view_handle_key ((WView
*) view
, parm
);
3825 if (view
->want_to_quit
&& !view_is_in_panel (view
))
3833 view
->dpy_bbar_dirty
= TRUE
;
3837 case WIDGET_DESTROY
:
3839 if (view_is_in_panel (view
))
3840 delete_hook (&select_file_hook
, view_hook
);
3844 return default_proc (msg
, parm
);
3848 /* {{{ External interface }}} */
3851 view_new (int y
, int x
, int cols
, int lines
, int is_panel
)
3853 WView
*view
= g_new0 (WView
, 1);
3856 init_widget (&view
->widget
, y
, x
, lines
, cols
,
3860 view
->filename
= NULL
;
3861 view
->command
= NULL
;
3863 view_set_datasource_none (view
);
3865 view
->growbuf_in_use
= FALSE
;
3866 /* leave the other growbuf fields uninitialized */
3868 view
->hex_mode
= FALSE
;
3869 view
->hexedit_mode
= FALSE
;
3870 view
->hexview_in_text
= FALSE
;
3871 view
->text_nroff_mode
= FALSE
;
3872 view
->text_wrap_mode
= FALSE
;
3873 view
->magic_mode
= FALSE
;
3875 view
->hexedit_lownibble
= FALSE
;
3877 view
->dpy_frame_size
= is_panel
? 1 : 0;
3878 view
->dpy_start
= 0;
3879 view
->dpy_text_column
= 0;
3881 view
->hex_cursor
= 0;
3882 view
->cursor_col
= 0;
3883 view
->cursor_row
= 0;
3884 view
->change_list
= NULL
;
3885 view
->converter
= str_cnv_from_term
;
3887 /* {status,ruler,data}_area are left uninitialized */
3890 view
->dpy_bbar_dirty
= TRUE
;
3891 view
->bytes_per_line
= 1;
3893 view
->search_start
= 0;
3894 view
->search_end
= 0;
3896 view
->want_to_quit
= FALSE
;
3898 for (i
= 0; i
< sizeof(view
->marks
) / sizeof(view
->marks
[0]); i
++)
3902 view
->update_steps
= 0;
3903 view
->update_activate
= 0;
3905 if (default_hex_mode
)
3906 view_toggle_hex_mode (view
);
3907 if (default_nroff_flag
)
3908 view_toggle_nroff_mode (view
);
3909 if (global_wrap_mode
)
3910 view_toggle_wrap_mode (view
);
3911 if (default_magic_flag
)
3912 view_toggle_magic_mode (view
);