* view.c: Eliminated two global variables and put them into the
[midnight-commander.git] / src / view.c
blob13e845aa6a1f3e18d1be7049059d53471e702eb7
1 /* View file module for the Midnight Commander
2 Copyright (C) 1994, 1995, 1996 The Free Software Foundation
3 Written by: 1994, 1995, 1998 Miguel de Icaza
4 1994, 1995 Janne Kukonlehto
5 1995 Jakub Jelinek
6 1996 Joseph M. Hinkle
7 1997 Norbert Warmuth
8 1998 Pavel Machek
9 2004 Roland Illig <roland.illig@gmx.de>
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 2 of the License, or
14 (at your option) any later version.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with this program; if not, write to the Free Software
23 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
25 #include <config.h>
26 #include <stdio.h>
27 #include <sys/types.h>
28 #ifdef HAVE_UNISTD_H
29 # include <unistd.h>
30 #endif
31 #include <string.h>
32 #include <sys/stat.h>
33 #ifdef HAVE_MMAP
34 # include <sys/mman.h>
35 #endif
36 #include <ctype.h> /* For toupper() */
37 #include <errno.h>
38 #include <limits.h>
40 #include "global.h"
41 #include "tty.h"
42 #include "cmd.h" /* For view_other_cmd */
43 #include "dialog.h" /* Needed by widget.h */
44 #include "widget.h" /* Needed for buttonbar_new */
45 #include "color.h"
46 #include "mouse.h"
47 #include "help.h"
48 #include "key.h" /* For mi_getch() */
49 #include "layout.h"
50 #include "setup.h"
51 #include "wtools.h" /* For query_set_sel() */
52 #include "dir.h"
53 #include "panel.h" /* Needed for current_panel and other_panel */
54 #include "win.h"
55 #include "execute.h"
56 #include "main.h" /* For the externs */
57 #define WANT_WIDGETS
58 #include "view.h"
60 #include "charsets.h"
61 #include "selcodepage.h"
63 #ifndef MAP_FILE
64 #define MAP_FILE 0
65 #endif
67 /* Block size for reading files in parts */
68 #define READ_BLOCK 8192
69 #define VIEW_PAGE_SIZE 8192
71 #define vwidth (view->widget.cols - (view->have_frame ? 2 : 0))
72 #define vheight (view->widget.lines - (view->have_frame ? 2 : 0))
74 /* Offset in bytes into a file */
75 typedef unsigned long offset_type;
76 #define INVALID_OFFSET ((offset_type) -1)
77 #define OFFSETTYPE_MAX (~((offset_type) 0))
79 /* A width or height on the screen */
80 typedef unsigned int screen_dimen;
82 /* A node for building a change list on change_list */
83 struct hexedit_change_node {
84 struct hexedit_change_node *next;
85 offset_type offset;
86 unsigned char value;
89 struct WView {
90 Widget widget;
92 char *filename; /* Name of the file */
93 char *command; /* Command used to pipe data in */
94 int view_active;
95 int have_frame;
97 unsigned char *data; /* Memory area for the file to be viewed */
98 size_t datasize; /* Number of bytes in the data */
99 /* view_update_last_byte() must be called after assignment to datasize */
101 /* File information */
102 int file; /* File descriptor (for mmap and munmap) */
103 FILE *stdfile; /* Stdio struct for reading file in parts */
104 int reading_pipe; /* Flag: Reading from pipe(use popen/pclose) */
105 int mmapping; /* Did we use mmap on the file? */
106 size_t mmappedsize; /* Number of bytes that are mmapped; used only for munmap() */
108 /* Display information */
109 offset_type last; /* Last byte shown */
110 offset_type last_byte; /* Last byte of file */
111 offset_type first; /* First byte in file */
112 offset_type bottom_first; /* First byte shown when very last page is displayed */
113 /* For the case of WINCH we should reset it to -1 */
114 offset_type start_display; /* First char displayed */
115 int start_col; /* First displayed column, negative */
116 offset_type edit_cursor; /* HexEdit cursor position in file */
117 int hexedit_mode:1; /* Hexadecimal editing mode flag */
118 int nib_shift:1; /* Set if editing the least significant nibble */
119 int hexedit_text:1; /* Set if hexedit is in the text mode */
120 screen_dimen start_save; /* Line start shift between text and hex */
121 screen_dimen cursor_col; /* Cursor column */
122 screen_dimen cursor_row; /* Cursor row */
123 struct hexedit_change_node *change_list; /* Linked list of changes */
125 int dirty; /* Number of skipped updates */
126 int wrap_mode:1; /* wrap_mode */
128 /* Mode variables */
129 int hex_mode:1; /* Hexadecimal mode flag */
130 int bytes_per_line; /* Number of bytes per line in hex mode */
131 int viewer_magic_flag:1; /* Selected viewer */
132 int viewer_nroff_flag:1; /* Do we do nroff style highlighting? */
134 /* Growing buffers information */
135 int growing_buffer; /* Use the growing buffers? */
136 char **block_ptr; /* Pointer to the block pointers */
137 int blocks; /* The number of blocks in *block_ptr */
138 size_t growbuf_lastindex; /* Number of bytes in the last page of the
139 growing buffer */
140 /* view_update_last_byte() must be called after assignment to
141 growing_buffer, blocks, growbuf_lastindex */
143 /* Search variables */
144 offset_type search_start; /* First character to start searching from */
145 offset_type found_len; /* Length of found string or 0 if none was found */
146 char *search_exp; /* The search expression */
147 int direction; /* 1= forward; -1 backward */
148 void (*last_search)(void *, char *);
149 /* Pointer to the last search command */
150 int view_quit:1; /* Quit flag */
152 int monitor; /* Monitor file growth (like tail -f) */
153 /* Markers */
154 int marker; /* mark to use */
155 offset_type marks [10]; /* 10 marks: 0..9 */
157 int move_dir; /* return value from widget:
158 * 0 do nothing
159 * -1 view previous file
160 * 1 view next file
163 offset_type update_steps; /* The number of bytes between percent
164 * increments */
165 offset_type update_activate;/* Last point where we updated the status */
168 static void view_update_last_byte (WView *view)
170 if (view->growing_buffer) {
171 if (view->blocks == 0)
172 view->last_byte = 0;
173 else
174 view->last_byte = ((offset_type) view->blocks - 1)
175 * VIEW_PAGE_SIZE + view->growbuf_lastindex;
176 } else
177 view->last_byte = view->datasize;
178 view->bottom_first = INVALID_OFFSET;
181 static void view_move_cursor_to_eol(WView *view)
183 offset_type last_line = (view->last_byte - 1) / view->bytes_per_line;
184 offset_type current_line = view->edit_cursor / view->bytes_per_line;
186 if (current_line == last_line) {
187 view->edit_cursor = view->last_byte - 1;
188 } else {
189 view->edit_cursor = (1 + current_line) * view->bytes_per_line - 1;
191 view->dirty++;
194 /* Maxlimit for skipping updates */
195 int max_dirt_limit = 10;
197 /* If set, show a ruler */
198 static int ruler = 0;
200 /* Scrolling is done in pages or line increments */
201 int mouse_move_pages_viewer = 1;
203 /* Used to compute the bottom first variable */
204 int have_fast_cpu = 0;
206 /* wrap mode default */
207 int global_wrap_mode = 1;
209 int default_hex_mode = 0;
210 static int default_hexedit_mode = 0;
211 int default_magic_flag = 1;
212 int default_nroff_flag = 1;
213 int altered_hex_mode = 0;
214 int altered_magic_flag = 0;
215 int altered_nroff_flag = 0;
217 static const char hex_char[] = "0123456789ABCDEF";
219 /* Our widget callback */
220 static cb_ret_t view_callback (WView *view, widget_msg_t msg, int parm);
222 static int regexp_view_search (WView * view, char *pattern, char *string,
223 int match_type);
224 static void view_move_forward (WView * view, int i);
225 static void view_labels (WView * view);
226 static void set_monitor (WView * view, int set_on);
227 static void view_update (WView * view, gboolean update_gui);
230 static void
231 close_view_file (WView *view)
233 if (view->file != -1) {
234 mc_close (view->file);
235 view->file = -1;
239 static void
240 free_file (WView *view)
242 int i;
244 #ifdef HAVE_MMAP
245 if (view->mmapping) {
246 mc_munmap (view->data, view->mmappedsize);
247 close_view_file (view);
248 } else
249 #endif /* HAVE_MMAP */
251 if (view->reading_pipe) {
252 /* Close pipe */
253 pclose (view->stdfile);
254 view->stdfile = NULL;
256 /* Close error pipe and show warnings if any */
257 close_error_pipe (0, NULL);
258 } else
259 close_view_file (view);
261 /* Block_ptr may be zero if the file was a file with 0 bytes */
262 if (view->growing_buffer && view->block_ptr) {
263 for (i = 0; i < view->blocks; i++) {
264 g_free (view->block_ptr[i]);
266 g_free (view->block_ptr);
270 /* Valid parameters for second parameter to set_monitor */
271 enum { off, on };
273 /* Both views */
274 static void
275 view_done (WView *view)
277 set_monitor (view, off);
279 /* alex: release core, used to replace mmap */
280 if (!view->mmapping && !view->growing_buffer && view->data != NULL) {
281 g_free (view->data);
282 view->data = NULL;
285 if (view->view_active) {
286 free_file (view);
287 g_free (view->filename);
288 g_free (view->command);
290 view->view_active = 0;
291 default_hex_mode = view->hex_mode;
292 default_nroff_flag = view->viewer_nroff_flag;
293 default_magic_flag = view->viewer_magic_flag;
294 global_wrap_mode = view->wrap_mode;
297 static void view_hook (void *);
299 /* Copies the output from the pipe to the growing buffer, until either
300 * the end-of-pipe is reached or the interval [0..ofs) of the growing
301 * buffer is completely filled. */
302 static void
303 view_growbuf_read_until (WView *view, offset_type ofs)
305 ssize_t nread;
306 unsigned char *p;
307 size_t bytesfree;
309 /* g_assert (view->growing_buffer, NULL); */
310 while (view->last_byte < ofs) {
311 if (view->blocks == 0 || view->growbuf_lastindex == VIEW_PAGE_SIZE) {
312 char *newblock = g_try_malloc (VIEW_PAGE_SIZE);
313 char **newblocks = g_try_malloc (sizeof (char *) * (view->blocks + 1));
314 if (!newblock || !newblocks) {
315 g_free (newblock);
316 return;
318 memcpy (newblocks, view->block_ptr, sizeof (char *) * view->blocks);
319 g_free (view->block_ptr);
320 view->block_ptr = newblocks;
321 view->block_ptr[view->blocks++] = newblock;
322 view->growbuf_lastindex = 0;
323 view_update_last_byte (view);
325 p = view->block_ptr[view->blocks - 1] + view->growbuf_lastindex;
326 bytesfree = VIEW_PAGE_SIZE - view->growbuf_lastindex;
327 if (view->stdfile != NULL)
328 nread = fread (p, 1, bytesfree, view->stdfile);
329 else
330 nread = mc_read (view->file, p, bytesfree);
332 if (nread == -1 || nread == 0)
333 return;
334 view->growbuf_lastindex += nread;
335 view_update_last_byte (view);
339 static int
340 get_byte (WView *view, unsigned int byte_index)
342 if (view->growing_buffer) {
343 size_t pageno = byte_index / VIEW_PAGE_SIZE;
344 size_t pageindex = byte_index % VIEW_PAGE_SIZE;
346 view_growbuf_read_until (view, byte_index + 1);
347 if (view->blocks == 0)
348 return -1;
349 if (pageno < view->blocks - 1)
350 return view->block_ptr[pageno][pageindex];
351 if (pageno == view->blocks - 1 && pageindex < view->growbuf_lastindex)
352 return view->block_ptr[pageno][pageindex];
353 return -1;
356 /* g_assert (view->data != NULL); */
357 if (byte_index < view->datasize)
358 return view->data[byte_index];
359 return -1;
362 static void
363 enqueue_change (struct hexedit_change_node **head,
364 struct hexedit_change_node *node)
366 struct hexedit_change_node *curr = *head;
368 while (curr) {
369 if (node->offset < curr->offset) {
370 *head = node;
371 node->next = curr;
372 return;
374 head = (struct hexedit_change_node **) curr;
375 curr = curr->next;
377 *head = node;
378 node->next = curr;
381 static void move_right (WView *);
383 static cb_ret_t
384 view_handle_editkey (WView *view, int key)
386 struct hexedit_change_node *node;
387 unsigned char byte_val;
389 /* Has there been a change at this position ? */
390 node = view->change_list;
391 while (node && (node->offset != view->edit_cursor))
392 node = node->next;
394 if (!view->hexedit_text) {
395 /* Hex editing */
396 unsigned int hexvalue = 0;
398 if (key >= '0' && key <= '9')
399 hexvalue = 0 + (key - '0');
400 else if (key >= 'A' && key <= 'F')
401 hexvalue = 10 + (key - 'A');
402 else if (key >= 'a' && key <= 'f')
403 hexvalue = 10 + (key - 'a');
404 else
405 return MSG_NOT_HANDLED;
407 if (node)
408 byte_val = node->value;
409 else
410 byte_val = get_byte (view, view->edit_cursor);
412 if (!view->nib_shift) {
413 byte_val = (byte_val & 0x0f) | (hexvalue << 4);
414 } else {
415 byte_val = (byte_val & 0xf0) | (hexvalue);
417 } else {
418 /* Text editing */
419 if (key < 256 && (is_printable (key) || (key == '\n')))
420 byte_val = key;
421 else
422 return MSG_NOT_HANDLED;
424 if (!node) {
425 node = (struct hexedit_change_node *)
426 g_new (struct hexedit_change_node, 1);
428 if (node) {
429 #ifndef HAVE_MMAP
430 /* alex@bcs.zaporizhzhe.ua: here we are using file copy
431 * completely loaded into memory, so we can replace bytes in
432 * view->data array to allow changes to be reflected when
433 * user switches back to text mode */
434 view->data[view->edit_cursor] = byte_val;
435 #endif /* !HAVE_MMAP */
436 node->offset = view->edit_cursor;
437 node->value = byte_val;
438 enqueue_change (&view->change_list, node);
440 } else {
441 node->value = byte_val;
443 view->dirty++;
444 view_update (view, TRUE);
445 move_right (view);
446 return MSG_HANDLED;
449 static void
450 free_change_list (WView *view)
452 struct hexedit_change_node *n = view->change_list;
454 while (n) {
455 view->change_list = n->next;
456 g_free (n);
457 n = view->change_list;
459 view->dirty++;
462 static void
463 save_edit_changes (WView *view)
465 struct hexedit_change_node *node = view->change_list;
466 int fp;
468 do {
469 fp = open (view->filename, O_WRONLY);
470 if (fp >= 0) {
471 while (node) {
472 if (lseek (fp, node->offset, SEEK_SET) == -1 ||
473 write (fp, &node->value, 1) != 1) {
474 close (fp);
475 fp = -1;
476 break;
478 node = node->next;
480 if (fp != -1)
481 close (fp);
484 if (fp == -1) {
485 fp = query_dialog (_(" Save file "),
486 _(" Cannot save file. "),
487 2, 2, _("&Retry"), _("&Cancel")) - 1;
489 } while (fp == -1);
491 free_change_list (view);
494 static int
495 view_ok_to_quit (WView *view)
497 int r;
499 if (!view->change_list)
500 return 1;
502 r = query_dialog (_("Quit"),
503 _(" File was modified, Save with exit? "), 2, 3,
504 _("&Cancel quit"), _("&Yes"), _("&No"));
506 switch (r) {
507 case 1:
508 save_edit_changes (view);
509 return 1;
510 case 2:
511 free_change_list (view);
512 return 1;
513 default:
514 return 0;
518 static char *
519 set_view_init_error (WView *view, const char *msg)
521 view->growing_buffer = 0;
522 view->reading_pipe = 0;
523 view->first = 0;
524 view->last_byte = 0;
525 if (msg) {
526 view->datasize = strlen (msg);
527 view_update_last_byte (view);
528 return g_strdup (msg);
530 return NULL;
533 /* return values: NULL for success, else points to error message */
534 static char *
535 init_growing_view (WView *view, const char *name, const char *filename)
537 const char *err_msg = NULL;
539 view->growing_buffer = 1;
540 view->block_ptr = NULL;
541 view->blocks = 0;
542 view->growbuf_lastindex = 0; /* unused */
543 view_update_last_byte (view);
545 if (name) {
546 view->reading_pipe = 1;
548 open_error_pipe ();
549 if ((view->stdfile = popen (name, "r")) == NULL) {
550 /* Avoid two messages. Message from stderr has priority. */
551 if (!close_error_pipe (view->have_frame ? -1 : 1, view->data))
552 err_msg = _(" Cannot spawn child program ");
553 return set_view_init_error (view, err_msg);
556 /* First, check if filter produced any output */
557 view_growbuf_read_until (view, 1);
558 if (view->last_byte == 0) {
559 pclose (view->stdfile);
560 view->stdfile = NULL;
561 /* Avoid two messages. Message from stderr has priority. */
562 if (!close_error_pipe (view->have_frame ? -1 : 1, view->data))
563 err_msg = _("Empty output from child filter");
564 return set_view_init_error (view, err_msg);
566 } else {
567 view->stdfile = NULL;
568 if ((view->file = mc_open (filename, O_RDONLY)) == -1)
569 return set_view_init_error (view, _(" Cannot open file "));
571 return NULL;
574 /* Load filename into core */
575 /* returns:
576 -1 on failure.
577 if (have_frame), we return success, but data points to a
578 error message instead of the file buffer (quick_view feature).
580 static char *
581 load_view_file (WView *view, int fd, const struct stat *st)
583 view->file = fd;
585 if (st->st_size == 0) {
586 /* Must be one of those nice files that grow (/proc) */
587 close_view_file (view);
588 return init_growing_view (view, 0, view->filename);
590 #ifdef HAVE_MMAP
591 if ((size_t) st->st_size == st->st_size)
592 view->data = mc_mmap (0, st->st_size, PROT_READ,
593 MAP_FILE | MAP_SHARED, view->file, 0);
594 else
595 view->data = (caddr_t) -1;
596 if ((caddr_t) view->data != (caddr_t) - 1) {
597 /* mmap worked */
598 view->first = 0;
599 view->mmappedsize = st->st_size;
600 view->datasize = st->st_size;
601 view->mmapping = 1;
602 view_update_last_byte (view);
603 return NULL;
605 #endif /* HAVE_MMAP */
607 /* For the OSes that don't provide mmap call, try to load all the
608 * file into memory (alex@bcs.zaporizhzhe.ua). Also, mmap can fail
609 * for any reason, so we use this as fallback (pavel@ucw.cz) */
611 /* Make sure view->s.st_size is not truncated when passed to g_malloc */
612 if ((gulong) st->st_size == st->st_size)
613 view->data = (unsigned char *) g_try_malloc ((gulong) st->st_size);
614 else
615 view->data = NULL;
617 if (view->data == NULL || mc_lseek (view->file, 0, SEEK_SET) != 0
618 || mc_read (view->file, view->data,
619 st->st_size) != st->st_size) {
620 g_free (view->data);
621 close_view_file (view);
622 return init_growing_view (view, 0, view->filename);
624 view->first = 0;
625 view->datasize = st->st_size;
626 view_update_last_byte (view);
627 return NULL;
630 /* Return zero on success, -1 on failure */
631 static int
632 do_view_init (WView *view, const char *_command, const char *_file,
633 int start_line)
635 char *error = NULL;
636 int i, type;
637 int fd = -1;
638 char tmp[BUF_MEDIUM];
639 struct stat st;
641 if (view->view_active)
642 view_done (view);
644 /* Set up the state */
645 view->data = NULL;
646 view->datasize = 0;
647 view->growing_buffer = 0;
648 view->reading_pipe = 0;
649 view->mmapping = 0;
650 view->blocks = 0;
651 view->block_ptr = NULL;
652 view->first = 0;
653 view->filename = g_strdup (_file);
654 view->command = 0;
655 view->last = view->first + ((LINES - 2) * view->bytes_per_line);
656 view_update_last_byte (view);
658 /* Clear the markers */
659 view->marker = 0;
660 for (i = 0; i < 10; i++)
661 view->marks[i] = 0;
663 if (!view->have_frame) {
664 view->start_col = 0;
667 if (_command && (view->viewer_magic_flag || _file[0] == '\0')) {
668 error = init_growing_view (view, _command, view->filename);
669 } else if (_file[0]) {
670 int cntlflags;
672 /* Open the file */
673 if ((fd = mc_open (_file, O_RDONLY | O_NONBLOCK)) == -1) {
674 g_snprintf (tmp, sizeof (tmp), _(" Cannot open \"%s\"\n %s "),
675 _file, unix_error_string (errno));
676 error = set_view_init_error (view, tmp);
677 goto finish;
680 /* Make sure we are working with a regular file */
681 if (mc_fstat (fd, &st) == -1) {
682 mc_close (fd);
683 g_snprintf (tmp, sizeof (tmp), _(" Cannot stat \"%s\"\n %s "),
684 _file, unix_error_string (errno));
685 error = set_view_init_error (view, tmp);
686 goto finish;
689 if (!S_ISREG (st.st_mode)) {
690 mc_close (fd);
691 g_snprintf (tmp, sizeof (tmp),
692 _(" Cannot view: not a regular file "));
693 error = set_view_init_error (view, tmp);
694 goto finish;
697 /* We don't need O_NONBLOCK after opening the file, unset it */
698 cntlflags = fcntl (fd, F_GETFL, 0);
699 if (cntlflags != -1) {
700 cntlflags &= ~O_NONBLOCK;
701 fcntl (fd, F_SETFL, cntlflags);
704 type = get_compression_type (fd);
706 if (view->viewer_magic_flag && (type != COMPRESSION_NONE)) {
707 g_free (view->filename);
708 view->filename =
709 g_strconcat (_file, decompress_extension (type), (char *) NULL);
712 error = load_view_file (view, fd, &st);
715 finish:
716 if (error) {
717 if (!view->have_frame) {
718 message (1, MSG_ERROR, "%s", error);
719 g_free (error);
720 return -1;
724 view->view_active = 1;
725 if (_command)
726 view->command = g_strdup (_command);
727 else
728 view->command = 0;
729 view->search_start = view->start_display = view->start_save =
730 view->first;
731 view->found_len = 0;
732 view->start_col = 0;
733 view->last_search = 0; /* Start a new search */
735 /* Special case: The data points to the error message */
736 if (error) {
737 view->data = error;
738 view->datasize = strlen (error);
739 view_update_last_byte (view);
740 view->file = -1;
743 if (start_line > 1 && !error) {
744 int saved_wrap_mode = view->wrap_mode;
746 view->wrap_mode = 0;
747 get_byte (view, 0);
748 view_move_forward (view, start_line - 1);
749 view->wrap_mode = saved_wrap_mode;
751 view->edit_cursor = view->first;
752 view->nib_shift = 0;
753 view->hexedit_text = 0;
754 view->change_list = NULL;
756 return 0;
759 static void
760 view_update_bytes_per_line (WView *view)
762 int cols;
764 if (view->have_frame)
765 cols = view->widget.cols - 2;
766 else
767 cols = view->widget.cols;
769 view->bottom_first = INVALID_OFFSET;
770 if (cols < 80)
771 view->bytes_per_line = ((cols - 8) / 17) * 4;
772 else
773 view->bytes_per_line = ((cols - 8) / 18) * 4;
775 if (view->bytes_per_line == 0)
776 view->bytes_per_line++; /* To avoid division by 0 */
778 view->dirty = max_dirt_limit + 1; /* To force refresh */
781 /* Both views */
782 /* Return zero on success, -1 on failure */
784 view_init (WView *view, const char *_command, const char *_file, int start_line)
786 if (!view->view_active || strcmp (_file, view->filename)
787 || altered_magic_flag)
788 return do_view_init (view, _command, _file, start_line);
789 else
790 return 0;
793 static void
794 view_percent (WView *view, int p, int w, gboolean update_gui)
796 int percent;
798 if (view->last_byte == 0 || view->last_byte == view->last)
799 percent = 100;
800 else if (p > (INT_MAX / 100))
801 percent = p / (view->last_byte / 100);
802 else
803 percent = p * 100 / view->last_byte;
805 widget_move (view, view->have_frame, w - 5);
806 printw ("%3d%%", percent);
809 static void
810 view_status (WView *view, gboolean update_gui)
812 static int i18n_adjust = 0;
813 static const char *file_label;
815 int w = view->widget.cols - (view->have_frame * 2);
816 int i;
818 attrset (SELECTED_COLOR);
819 widget_move (view, view->have_frame, view->have_frame);
820 hline (' ', w);
822 if (!i18n_adjust) {
823 file_label = _("File: %s");
824 i18n_adjust = strlen (file_label) - 2;
827 if (w < i18n_adjust + 6)
828 addstr ((char *) name_trunc (view->filename ? view->filename :
829 view->command ? view->command : "", w));
830 else {
831 i = (w > 22 ? 22 : w) - i18n_adjust;
832 printw (const_cast(char *, file_label), name_trunc (view->filename ? view->filename :
833 view->command ? view->command : "",
834 i));
835 if (w > 46) {
836 widget_move (view, view->have_frame, 24 + view->have_frame);
837 if (view->hex_mode)
838 printw (const_cast(char *, _("Offset 0x%08lx")), view->edit_cursor);
839 else
840 printw (const_cast(char *, _("Col %d")), -view->start_col);
842 if (w > 62) {
843 widget_move (view, view->have_frame, 43 + view->have_frame);
844 printw (const_cast(char *, _("%s bytes")), size_trunc (view->last_byte));
846 if (w > 70) {
847 printw (" ");
848 if (view->growing_buffer)
849 addstr (_(" [grow]"));
851 if (w > 26) {
852 view_percent (view,
853 view->hex_mode ? view->edit_cursor : view->
854 start_display,
855 view->widget.cols - view->have_frame + 1,
856 update_gui);
859 attrset (SELECTED_COLOR);
862 static inline void
863 view_display_clean (WView *view, int height, int width)
865 /* FIXME: Should I use widget_erase only and repaint the box? */
866 if (view->have_frame) {
867 int i;
869 draw_double_box (view->widget.parent, view->widget.y,
870 view->widget.x, view->widget.lines,
871 view->widget.cols);
872 for (i = 1; i < height; i++) {
873 widget_move (view, i, 1);
874 printw ("%*s", width - 1, "");
876 } else
877 widget_erase ((Widget *) view);
880 #define view_add_character(view,c) addch (c)
881 #define view_add_one_vline() one_vline()
882 #define view_add_string(view,s) addstr (s)
883 #define view_gotoyx(v,r,c) widget_move (v,r,c)
885 #define view_freeze(view)
886 #define view_thaw(view)
888 #define STATUS_LINES 1
890 typedef enum {
891 MARK_NORMAL = 0,
892 MARK_SELECTED = 1,
893 MARK_CURSOR = 2,
894 MARK_CHANGED = 3
895 } mark_t;
897 static inline int view_count_backspaces (WView *view, off_t offset)
899 int backspaces = 0;
900 while (get_byte (view, offset - 2 * backspaces) == '\b')
901 backspaces++;
902 return backspaces;
905 /* Shows the file pointed to by *start_display on view_win */
906 static offset_type
907 display (WView *view)
909 const int frame_shift = view->have_frame;
910 int col = 0 + frame_shift;
911 int row = STATUS_LINES + frame_shift;
912 int height, width;
913 offset_type from;
914 int c;
915 mark_t boldflag = MARK_NORMAL;
916 struct hexedit_change_node *curr = view->change_list;
918 height = view->widget.lines - frame_shift;
919 width = view->widget.cols - frame_shift;
920 from = view->start_display;
921 attrset (NORMAL_COLOR);
923 view_freeze (view);
924 view_display_clean (view, height, width);
926 /* Optionally, display a ruler */
927 if ((!view->hex_mode) && (ruler)) {
928 char r_buff[10];
929 int cl;
931 attrset (MARKED_COLOR);
932 for (c = frame_shift; c < width; c++) {
933 cl = c - view->start_col;
934 if (ruler == 1)
935 view_gotoyx (view, row, c);
936 else
937 view_gotoyx (view, row + height - 2, c);
938 r_buff[0] = '-';
939 if ((cl % 10) == 0)
940 r_buff[0] = '|';
941 else if ((cl % 5) == 0)
942 r_buff[0] = '*';
943 view_add_character (view, r_buff[0]);
944 if ((cl != 0) && (cl % 10) == 0) {
945 g_snprintf (r_buff, sizeof (r_buff), "%03d", cl);
946 if (ruler == 1) {
947 widget_move (view, row + 1, c - 1);
948 } else {
949 widget_move (view, row + height - 3, c - 1);
951 view_add_string (view, r_buff);
954 attrset (NORMAL_COLOR);
955 if (ruler == 1)
956 row += 2;
957 else
958 height -= 2;
961 /* Find the first displayable changed byte */
962 while (curr && (curr->offset < from)) {
963 curr = curr->next;
965 if (view->hex_mode) {
966 char hex_buff[10]; /* A temporary buffer for sprintf and mvwaddstr */
967 int bytes; /* Number of bytes already printed on the line */
969 /* Start of text column */
970 int text_start = width - view->bytes_per_line - 1 + frame_shift;
972 for (; (void) get_byte (view, from + view->bytes_per_line),
973 row < height && from < view->last_byte; row++) {
974 /* Print the hex offset */
975 attrset (MARKED_COLOR);
976 g_snprintf (hex_buff, sizeof (hex_buff), "%08X",
977 (int) (from - view->first));
978 view_gotoyx (view, row, frame_shift);
979 view_add_string (view, hex_buff);
980 attrset (NORMAL_COLOR);
982 /* Hex dump starts from column nine */
983 if (view->have_frame)
984 col = 10;
985 else
986 col = 9;
988 /* Each hex number is two digits */
989 hex_buff[2] = 0;
990 for (bytes = 0;
991 bytes < view->bytes_per_line && from < view->last_byte;
992 bytes++, from++) {
993 /* Display and mark changed bytes */
994 if (curr && from == curr->offset) {
995 c = curr->value;
996 curr = curr->next;
997 boldflag = MARK_CHANGED;
998 attrset (VIEW_UNDERLINED_COLOR);
999 } else
1000 c = (unsigned char) get_byte (view, from);
1002 if (view->found_len && from >= view->search_start
1003 && from < view->search_start + view->found_len) {
1004 boldflag = MARK_SELECTED;
1005 attrset (MARKED_COLOR);
1007 /* Display the navigation cursor */
1008 if (from == view->edit_cursor) {
1009 if (!view->hexedit_text) {
1010 view->cursor_row = row;
1011 view->cursor_col = col;
1013 boldflag = MARK_CURSOR;
1014 attrset (view->
1015 hexedit_text ? MARKED_SELECTED_COLOR :
1016 VIEW_UNDERLINED_COLOR);
1019 /* Print a hex number (sprintf is too slow) */
1020 hex_buff[0] = hex_char[(c >> 4)];
1021 hex_buff[1] = hex_char[c & 15];
1022 view_gotoyx (view, row, col);
1023 view_add_string (view, hex_buff);
1024 col += 3;
1025 /* Turn off the cursor or changed byte highlighting here */
1026 if (boldflag == MARK_CURSOR || boldflag == MARK_CHANGED)
1027 attrset (NORMAL_COLOR);
1028 if ((bytes & 3) == 3 && bytes + 1 < view->bytes_per_line) {
1029 /* Turn off the search highlighting */
1030 if (boldflag == MARK_SELECTED
1031 && from ==
1032 view->search_start + view->found_len - 1)
1033 attrset (NORMAL_COLOR);
1035 /* Hex numbers are printed in the groups of four */
1036 /* Groups are separated by a vline */
1038 view_gotoyx (view, row, col - 1);
1039 view_add_character (view, ' ');
1040 view_gotoyx (view, row, col);
1041 if ((view->have_frame && view->widget.cols < 82)
1042 || view->widget.cols < 80)
1043 col += 1;
1044 else {
1045 view_add_one_vline ();
1046 col += 2;
1049 if (boldflag != MARK_NORMAL
1050 && from ==
1051 view->search_start + view->found_len - 1)
1052 attrset (MARKED_COLOR);
1055 if (boldflag != MARK_NORMAL
1056 && from < view->search_start + view->found_len - 1
1057 && bytes != view->bytes_per_line - 1) {
1058 view_gotoyx (view, row, col);
1059 view_add_character (view, ' ');
1062 /* Print corresponding character on the text side */
1063 view_gotoyx (view, row, text_start + bytes);
1065 c = convert_to_display_c (c);
1067 if (!is_printable (c))
1068 c = '.';
1069 switch (boldflag) {
1070 case MARK_NORMAL:
1071 break;
1072 case MARK_SELECTED:
1073 attrset (MARKED_COLOR);
1074 break;
1075 case MARK_CURSOR:
1076 if (view->hexedit_text) {
1077 /* Our side is active */
1078 view->cursor_col = text_start + bytes;
1079 view->cursor_row = row;
1080 attrset (VIEW_UNDERLINED_COLOR);
1081 } else {
1082 /* Other side is active */
1083 attrset (MARKED_SELECTED_COLOR);
1085 break;
1086 case MARK_CHANGED:
1087 attrset (VIEW_UNDERLINED_COLOR);
1088 break;
1090 view_add_character (view, c);
1092 if (boldflag != MARK_NORMAL) {
1093 boldflag = MARK_NORMAL;
1094 attrset (NORMAL_COLOR);
1098 } else {
1099 /* the get_byte() call below might modify view->last_byte */
1100 for (; row < height && ((void) (c = get_byte (view, from)), from < view->last_byte); from++) {
1101 if ((c == '\n') || (col >= width && view->wrap_mode)) {
1102 col = frame_shift;
1103 row++;
1104 if (c == '\n' || row >= height)
1105 continue;
1107 if (c == '\r')
1108 continue;
1109 if (c == '\t') {
1110 col = ((col - frame_shift) / 8) * 8 + 8 + frame_shift;
1111 continue;
1113 if (view->viewer_nroff_flag && c == '\b') {
1114 int c_prev;
1115 int c_next;
1117 if (from + 1 < view->last_byte
1118 && is_printable ((c_next = get_byte (view, from + 1)))
1119 && from > view->first
1120 && is_printable ((c_prev = get_byte (view, from - 1)))
1121 && (c_prev == c_next || c_prev == '_'
1122 || (c_prev == '+' && c_next == 'o'))) {
1123 if (col <= frame_shift) {
1124 /* So it has to be wrap_mode - do not need to check for it */
1125 if (row == 1 + frame_shift) {
1126 from++;
1127 continue; /* There had to be a bold character on the rightmost position
1128 of the previous undisplayed line */
1130 row--;
1131 col = width;
1133 col--;
1134 boldflag = MARK_SELECTED;
1135 if (c_prev == '_' && (c_next != '_' || view_count_backspaces (view, from) == 1))
1136 attrset (VIEW_UNDERLINED_COLOR);
1137 else
1138 attrset (MARKED_COLOR);
1139 continue;
1142 if (view->found_len && from >= view->search_start
1143 && from < view->search_start + view->found_len) {
1144 boldflag = MARK_SELECTED;
1145 attrset (SELECTED_COLOR);
1147 if (col >= frame_shift - view->start_col
1148 && col < width - view->start_col) {
1149 view_gotoyx (view, row, col + view->start_col);
1151 c = convert_to_display_c (c);
1153 if (!is_printable (c))
1154 c = '.';
1156 view_add_character (view, c);
1158 col++;
1159 if (boldflag != MARK_NORMAL) {
1160 boldflag = MARK_NORMAL;
1161 attrset (NORMAL_COLOR);
1164 /* Very last thing */
1165 if (view->growing_buffer && from + 1 == view->last_byte)
1166 get_byte (view, from + 1);
1169 view->last = from;
1170 view_thaw (view);
1171 return from;
1174 static void
1175 view_place_cursor (WView *view)
1177 int shift;
1179 if (!view->hexedit_text && view->nib_shift)
1180 shift = 1;
1181 else
1182 shift = 0;
1184 widget_move (&view->widget, view->cursor_row,
1185 view->cursor_col + shift);
1188 static void
1189 view_update (WView *view, gboolean update_gui)
1191 static int dirt_limit = 1;
1193 if (view->dirty > dirt_limit) {
1194 /* Too many updates skipped -> force a update */
1195 display (view);
1196 view_status (view, update_gui);
1197 view->dirty = 0;
1198 /* Raise the update skipping limit */
1199 dirt_limit++;
1200 if (dirt_limit > max_dirt_limit)
1201 dirt_limit = max_dirt_limit;
1203 if (view->dirty) {
1204 if (is_idle ()) {
1205 /* We have time to update the screen properly */
1206 display (view);
1207 view_status (view, update_gui);
1208 view->dirty = 0;
1209 if (dirt_limit > 1)
1210 dirt_limit--;
1211 } else {
1212 /* We are busy -> skipping full update,
1213 only the status line is updated */
1214 view_status (view, update_gui);
1216 /* Here we had a refresh, if fast scrolling does not work
1217 restore the refresh, although this should not happen */
1221 static inline void
1222 my_define (Dlg_head *h, int idx, const char *text, void (*fn) (WView *),
1223 WView *view)
1225 define_label_data (h, idx, text, (buttonbarfn) fn, view);
1228 /* If the last parameter is nonzero, it means we want get the count of lines
1229 from current up to the the upto position inclusive */
1230 static offset_type
1231 move_forward2 (WView *view, offset_type current, int lines, offset_type upto)
1233 offset_type q, p;
1234 int line;
1235 int col = 0;
1237 if (view->hex_mode) {
1238 p = current + lines * view->bytes_per_line;
1239 p = (p >= view->last_byte) ? current : p;
1240 if (lines == 1) {
1241 q = view->edit_cursor + view->bytes_per_line;
1242 line = q / view->bytes_per_line;
1243 col = (view->last_byte - 1) / view->bytes_per_line;
1244 view->edit_cursor = (line > col) ? view->edit_cursor : q;
1245 view->edit_cursor = (view->edit_cursor < view->last_byte) ?
1246 view->edit_cursor : view->last_byte - 1;
1247 q = current + ((LINES - 2) * view->bytes_per_line);
1248 p = (view->edit_cursor < q) ? current : p;
1249 } else {
1250 view->edit_cursor = (view->edit_cursor < p) ?
1251 p : view->edit_cursor;
1253 return p;
1254 } else {
1255 if (upto) {
1256 lines = -1;
1257 q = upto;
1258 } else
1259 q = view->last_byte;
1260 if (get_byte (view, q) != '\n')
1261 q++;
1262 for (line = col = 0, p = current; p < q; p++) {
1263 int c;
1265 if (lines != -1 && line >= lines)
1266 return p;
1268 c = get_byte (view, p);
1270 if (view->wrap_mode) {
1271 if (c == '\r')
1272 continue; /* This characters is never displayed */
1273 else if (c == '\t')
1274 col =
1275 ((col - view->have_frame) / 8) * 8 + 8 +
1276 view->have_frame;
1277 else
1278 col++;
1279 if (view->viewer_nroff_flag && c == '\b') {
1280 if (p + 1 < view->last_byte
1281 && is_printable (get_byte (view, p + 1))
1282 && p > view->first
1283 && is_printable (get_byte (view, p - 1)))
1284 col -= 2;
1285 } else if (col == vwidth) {
1286 /* FIXME: the c in is_printable was a p, that is a bug,
1287 I suspect I got that fix from Jakub, same applies
1288 for d. */
1289 int d = get_byte (view, p + 2);
1291 if (p + 2 >= view->last_byte || !is_printable (c) ||
1292 !view->viewer_nroff_flag
1293 || get_byte (view, p + 1) != '\b'
1294 || !is_printable (d)) {
1295 col = 0;
1297 if (c == '\n' || get_byte (view, p + 1) != '\n')
1298 line++;
1300 } else if (c == '\n') {
1301 line++;
1302 col = 0;
1304 } else if (c == '\n')
1305 line++;
1307 if (upto)
1308 return line;
1310 return current;
1313 /* special case for text (non-hex) mode with line wrapping. */
1314 static offset_type
1315 move_backward2_textmode_wrap (WView * view, offset_type current, int lines)
1317 offset_type p, q, pm;
1318 int line;
1320 if (current == view->last_byte && get_byte (view, current - 1) != '\n')
1321 /* There is one virtual '\n' at the end,
1322 so that the last line is shown */
1323 line = 1;
1324 else
1325 line = 0;
1327 for (q = p = current - 1; p > view->first; p--) {
1328 if (get_byte (view, p) == '\n' || p == view->first) {
1329 pm = p > view->first ? p + 1 : view->first;
1330 line += move_forward2 (view, pm, 0, q);
1331 if (line >= lines) {
1332 if (line == lines)
1333 return pm;
1334 else
1335 return move_forward2 (view, pm, line - lines, 0);
1337 q = p + 1;
1340 return p > view->first ? p : view->first;
1343 /* returns the new current pointer */
1344 /* Cause even the forward routine became very complex, we in the wrap_mode
1345 just find the nearest '\n', use move_forward2(p, 0, q) to get the count
1346 of lines up to there and then use move_forward2(p, something, 0), which we
1347 return */
1348 static offset_type
1349 move_backward2 (WView *view, offset_type current, int lines)
1351 if (view->hex_mode) {
1352 if (view->edit_cursor >= lines * view->bytes_per_line) {
1353 view->edit_cursor -= lines * view->bytes_per_line;
1354 } else {
1355 view->edit_cursor %= view->bytes_per_line;
1357 if (current > view->edit_cursor) {
1358 /* cursor is out-of-view -- adjust the view */
1359 if (current >= lines * view->bytes_per_line) {
1360 current -= lines * view->bytes_per_line;
1361 } else {
1362 current %= view->bytes_per_line;
1365 return current;
1366 } else {
1367 if (current == view->first)
1368 return current;
1370 if (view->wrap_mode)
1371 return move_backward2_textmode_wrap (view, current, lines);
1373 /* There is one virtual '\n' at the end,
1374 * so that the last line is shown */
1375 if (current == view->last_byte && get_byte (view, current - 1) != '\n')
1376 lines--;
1377 while (current > view->first && get_byte(view, current - 1) != '\n')
1378 current--;
1379 while (lines > 0) {
1380 if (current > view->first)
1381 current--;
1382 lines--;
1383 while (current > view->first && get_byte(view, current - 1) != '\n')
1384 current--;
1386 return current;
1388 return current; /* unreached */
1391 static void
1392 view_move_backward (WView *view, int i)
1394 view->search_start = view->start_display =
1395 move_backward2 (view, view->start_display, i);
1396 view->found_len = 0;
1397 view->last = view->first + ((LINES - 2) * view->bytes_per_line);
1398 view->dirty++;
1401 static offset_type
1402 get_bottom_first (WView *view, int do_not_cache, int really)
1404 offset_type bottom_first;
1406 if (!have_fast_cpu && !really)
1407 return INT_MAX;
1409 if (!do_not_cache && view->bottom_first != INVALID_OFFSET)
1410 return view->bottom_first;
1412 /* Force loading */
1413 if (view->growing_buffer)
1414 view_growbuf_read_until (view, OFFSETTYPE_MAX);
1416 bottom_first = move_backward2 (view, view->last_byte, vheight - 1);
1418 if (view->hex_mode)
1419 bottom_first = (bottom_first + view->bytes_per_line - 1)
1420 / view->bytes_per_line * view->bytes_per_line;
1421 view->bottom_first = bottom_first;
1423 return view->bottom_first;
1426 static void
1427 view_move_forward (WView *view, int i)
1429 view->start_display = move_forward2 (view, view->start_display, i, 0);
1430 if (!view->reading_pipe
1431 && view->start_display > get_bottom_first (view, 0, 0))
1432 view->start_display = view->bottom_first;
1433 view->search_start = view->start_display;
1434 view->found_len = 0;
1435 view->last = view->first + ((LINES - 2) * view->bytes_per_line);
1436 view->dirty++;
1440 static void
1441 move_to_top (WView *view)
1443 view->search_start = view->start_display = view->first;
1444 view->found_len = 0;
1445 view->last = view->first + ((LINES - 2) * view->bytes_per_line);
1446 view->nib_shift = 0;
1447 view->edit_cursor = view->start_display;
1448 view->dirty++;
1451 static void
1452 move_to_bottom (WView *view)
1454 view->search_start = view->start_display =
1455 get_bottom_first (view, 0, 1);
1456 view->found_len = 0;
1457 view->last = view->first + ((LINES - 2) * view->bytes_per_line);
1458 view->edit_cursor = (view->edit_cursor < view->start_display) ?
1459 view->start_display : view->edit_cursor;
1460 view->dirty++;
1463 /* Scroll left/right the view panel functions */
1464 static void
1465 move_right (WView *view)
1467 if (view->wrap_mode && !view->hex_mode)
1468 return;
1469 if (view->hex_mode) {
1470 view->last = view->first + ((LINES - 2) * view->bytes_per_line);
1472 if (view->hex_mode && !view->hexedit_text) {
1473 view->nib_shift = !view->nib_shift;
1474 if (view->nib_shift)
1475 return;
1477 view->edit_cursor = (++view->edit_cursor < view->last_byte) ?
1478 view->edit_cursor : view->last_byte - 1;
1479 if (view->edit_cursor >= view->last) {
1480 view->edit_cursor -= view->bytes_per_line;
1481 view_move_forward (view, 1);
1483 } else if (--view->start_col > 0)
1484 view->start_col = 0;
1485 view->dirty++;
1488 static void
1489 move_left (WView *view)
1491 if (view->wrap_mode && !view->hex_mode)
1492 return;
1493 if (view->hex_mode) {
1494 if (view->hex_mode && !view->hexedit_text) {
1495 view->nib_shift = !view->nib_shift;
1496 if (!view->nib_shift)
1497 return;
1499 if (view->edit_cursor > view->first)
1500 --view->edit_cursor;
1501 if (view->edit_cursor < view->start_display) {
1502 view->edit_cursor += view->bytes_per_line;
1503 view_move_backward (view, 1);
1505 } else if (++view->start_col > 0)
1506 view->start_col = 0;
1507 view->dirty++;
1510 /* Case insensitive search of text in data */
1511 static int
1512 icase_search_p (WView *view, char *text, char *data, int nothing)
1514 const char *q;
1515 int lng;
1516 const int direction = view->direction;
1518 /* If we are searching backwards, reverse the string */
1519 if (direction == -1) {
1520 g_strreverse (text);
1521 g_strreverse (data);
1524 q = _icase_search (text, data, &lng);
1526 if (direction == -1) {
1527 g_strreverse (text);
1528 g_strreverse (data);
1531 if (q != 0) {
1532 if (direction > 0)
1533 view->search_start = q - data - lng;
1534 else
1535 view->search_start = strlen (data) - (q - data);
1536 view->found_len = lng;
1537 return 1;
1539 return 0;
1542 static char *
1543 grow_string_buffer (char *text, int *size)
1545 char *new;
1547 /* The grow steps */
1548 *size += 160;
1549 new = g_realloc (text, *size);
1550 if (!text) {
1551 *new = 0;
1553 return new;
1556 static char *
1557 get_line_at (WView *view, offset_type *p, offset_type *skipped)
1559 char *buffer = NULL;
1560 int buffer_size = 0;
1561 offset_type usable_size = 0;
1562 int ch;
1563 const int direction = view->direction;
1564 offset_type pos = *p;
1565 offset_type i = 0;
1566 int prev = 0;
1568 if (!pos && direction == -1)
1569 return 0;
1571 /* skip over all the possible zeros in the file */
1572 while ((ch = get_byte (view, pos)) == 0) {
1573 if (!pos && direction == -1)
1574 return 0;
1575 pos += direction;
1576 i++;
1578 *skipped = i;
1580 if (!i && (pos || direction == -1)) {
1581 prev = get_byte (view, pos - direction);
1582 if ((prev == -1) || (prev == '\n'))
1583 prev = 0;
1586 for (i = 1; ch != -1; ch = get_byte (view, pos)) {
1588 if (i >= usable_size) {
1589 buffer = grow_string_buffer (buffer, &buffer_size);
1590 usable_size = buffer_size - 2; /* prev & null terminator */
1593 buffer[i++] = ch;
1595 if (!pos && direction == -1)
1596 break;
1598 pos += direction;
1600 if (ch == '\n' || !ch) {
1601 i--; /* Strip newline/zero */
1602 break;
1606 if (buffer) {
1607 buffer[0] = prev;
1608 buffer[i] = 0;
1610 /* If we are searching backwards, reverse the string */
1611 if (direction < 0) {
1612 g_strreverse (buffer + 1);
1616 *p = pos;
1617 return buffer;
1620 static void
1621 search_update_steps (WView *view)
1623 if (view->last_byte != 0)
1624 view->update_steps = 40000;
1625 else /* viewing a data stream, not a file */
1626 view->update_steps = view->last_byte / 100;
1628 /* Do not update the percent display but every 20 ks */
1629 if (view->update_steps < 20000)
1630 view->update_steps = 20000;
1633 static void
1634 search (WView *view, char *text,
1635 int (*search) (WView *, char *, char *, int))
1637 const int w = view->widget.cols - view->have_frame + 1;
1639 char *s = NULL; /* The line we read from the view buffer */
1640 offset_type p, beginning, search_start;
1641 int found_len;
1642 int search_status;
1643 Dlg_head *d = 0;
1645 /* Used to keep track of where the line starts, when looking forward */
1646 /* is the index before transfering the line; the reverse case uses */
1647 /* the position returned after the line has been read */
1648 offset_type forward_line_start;
1649 offset_type reverse_line_start;
1650 offset_type t;
1651 /* Clear interrupt status */
1652 got_interrupt ();
1654 if (verbose) {
1655 d = create_message (D_NORMAL, _("Search"), _("Searching %s"), text);
1656 mc_refresh ();
1659 found_len = view->found_len;
1660 search_start = view->search_start;
1662 if (view->direction == 1) {
1663 p = found_len ? search_start + 1 : search_start;
1664 } else {
1665 p = (found_len && search_start) ? search_start - 1 : search_start;
1667 beginning = p;
1669 /* Compute the percent steps */
1670 search_update_steps (view);
1671 view->update_activate = 0;
1673 for (;; g_free (s)) {
1674 if (p >= view->update_activate) {
1675 view->update_activate += view->update_steps;
1676 if (verbose) {
1677 view_percent (view, p, w, TRUE);
1678 mc_refresh ();
1680 if (got_interrupt ())
1681 break;
1683 forward_line_start = p;
1684 disable_interrupt_key ();
1685 s = get_line_at (view, &p, &t);
1686 reverse_line_start = p;
1687 enable_interrupt_key ();
1689 if (!s)
1690 break;
1692 search_status = (*search) (view, text, s + 1, match_normal);
1693 if (search_status < 0) {
1694 g_free (s);
1695 break;
1698 if (search_status == 0)
1699 continue;
1701 /* We found the string */
1703 /* Handle ^ and $ when regexp search starts at the middle of the line */
1704 if (*s && !view->search_start && (search == regexp_view_search)) {
1705 if ((*text == '^' && view->direction == 1)
1706 || (view->direction == -1 && text[strlen (text) - 1] == '$')
1708 continue;
1711 /* Record the position used to continue the search */
1712 if (view->direction == 1)
1713 t += forward_line_start;
1714 else
1715 t = reverse_line_start ? reverse_line_start + 3 : 0;
1716 view->search_start += t;
1718 if (t != beginning) {
1719 if (t > get_bottom_first (view, 0, 0))
1720 view->start_display = view->bottom_first;
1721 else
1722 view->start_display = t;
1725 g_free (s);
1726 break;
1728 disable_interrupt_key ();
1729 if (verbose) {
1730 dlg_run_done (d);
1731 destroy_dlg (d);
1733 if (!s) {
1734 message (0, _("Search"), _(" Search string not found "));
1735 view->found_len = 0;
1739 /* Search buffer (its size is len) in the complete buffer
1740 * returns the position where the block was found or INVALID_OFFSET
1741 * if not found */
1742 static offset_type
1743 block_search (WView *view, const char *buffer, int len)
1745 const int w = view->widget.cols - view->have_frame + 1;
1746 int direction = view->direction;
1747 const char *d = buffer;
1748 char b;
1749 offset_type e;
1751 /* clear interrupt status */
1752 got_interrupt ();
1753 enable_interrupt_key ();
1754 if (direction == 1)
1755 e = view->found_len ? view->search_start + 1 : view->search_start;
1756 else
1757 e = (view->found_len
1758 && view->search_start) ? view->search_start - 1
1759 : view->search_start;
1761 search_update_steps (view);
1762 view->update_activate = 0;
1764 if (direction == -1) {
1765 for (d += len - 1;; e--) {
1766 if (e <= view->update_activate) {
1767 view->update_activate -= view->update_steps;
1768 if (verbose) {
1769 view_percent (view, e, w, TRUE);
1770 mc_refresh ();
1772 if (got_interrupt ())
1773 break;
1775 b = get_byte (view, e);
1777 if (*d == b) {
1778 if (d == buffer) {
1779 disable_interrupt_key ();
1780 return e;
1782 d--;
1783 } else {
1784 e += buffer + len - 1 - d;
1785 d = buffer + len - 1;
1787 if (e == 0)
1788 break;
1790 } else {
1791 while (e < view->last_byte) {
1792 if (e >= view->update_activate) {
1793 view->update_activate += view->update_steps;
1794 if (verbose) {
1795 view_percent (view, e, w, TRUE);
1796 mc_refresh ();
1798 if (got_interrupt ())
1799 break;
1801 b = get_byte (view, e++);
1803 if (*d == b) {
1804 d++;
1805 if (d - buffer == len) {
1806 disable_interrupt_key ();
1807 return e - len;
1809 } else {
1810 e -= d - buffer;
1811 d = buffer;
1815 disable_interrupt_key ();
1816 return INVALID_OFFSET;
1820 * Search in the hex mode. Supported input:
1821 * - numbers (oct, dec, hex). Each of them matches one byte.
1822 * - strings in double quotes. Matches exactly without quotes.
1824 static void
1825 hex_search (WView *view, const char *text)
1827 char *buffer; /* Parsed search string */
1828 char *cur; /* Current position in it */
1829 int block_len; /* Length of the search string */
1830 offset_type pos; /* Position of the string in the file */
1831 int parse_error = 0;
1833 if (!*text) {
1834 view->found_len = 0;
1835 return;
1838 /* buffer will never be longer that text */
1839 buffer = g_new (char, strlen (text));
1840 cur = buffer;
1842 /* First convert the string to a stream of bytes */
1843 while (*text) {
1844 int val;
1845 int ptr;
1847 /* Skip leading spaces */
1848 if (*text == ' ' || *text == '\t') {
1849 text++;
1850 continue;
1853 /* %i matches octal, decimal, and hexadecimal numbers */
1854 if (sscanf (text, "%i%n", &val, &ptr) > 0) {
1855 /* Allow signed and unsigned char in the user input */
1856 if (val < -128 || val > 255) {
1857 parse_error = 1;
1858 break;
1861 *cur++ = (char) val;
1862 text += ptr;
1863 continue;
1866 /* Try quoted string, strip quotes */
1867 if (*text == '"') {
1868 const char *next_quote;
1870 text++;
1871 next_quote = strchr (text, '"');
1872 if (next_quote) {
1873 memcpy (cur, text, next_quote - text);
1874 cur += next_quote - text;
1875 text = next_quote + 1;
1876 continue;
1878 /* fall through */
1881 parse_error = 1;
1882 break;
1885 block_len = cur - buffer;
1887 /* No valid bytes in the user input */
1888 if (block_len <= 0 || parse_error) {
1889 message (0, _("Search"), _("Invalid hex search expression"));
1890 g_free (buffer);
1891 view->found_len = 0;
1892 return;
1895 /* Then start the search */
1896 pos = block_search (view, buffer, block_len);
1898 g_free (buffer);
1900 if (pos == INVALID_OFFSET) {
1901 message (0, _("Search"), _(" Search string not found "));
1902 view->found_len = 0;
1903 return;
1906 view->search_start = pos;
1907 view->found_len = block_len;
1908 /* Set the edit cursor to the search position, left nibble */
1909 view->edit_cursor = view->search_start;
1910 view->nib_shift = 0;
1912 /* Adjust the file offset */
1913 view->start_display = (pos & (~(view->bytes_per_line - 1)));
1914 if (view->start_display > get_bottom_first (view, 0, 0))
1915 view->start_display = view->bottom_first;
1918 static int
1919 regexp_view_search (WView *view, char *pattern, char *string,
1920 int match_type)
1922 static regex_t r;
1923 static char *old_pattern = NULL;
1924 static int old_type;
1925 regmatch_t pmatch[1];
1926 int i, flags = REG_ICASE;
1928 if (!old_pattern || strcmp (old_pattern, pattern)
1929 || old_type != match_type) {
1930 if (old_pattern) {
1931 regfree (&r);
1932 g_free (old_pattern);
1933 old_pattern = 0;
1935 for (i = 0; pattern[i] != 0; i++) {
1936 if (isupper ((unsigned char) pattern[i])) {
1937 flags = 0;
1938 break;
1941 flags |= REG_EXTENDED;
1942 if (regcomp (&r, pattern, flags)) {
1943 message (1, MSG_ERROR, _(" Invalid regular expression "));
1944 return -1;
1946 old_pattern = g_strdup (pattern);
1947 old_type = match_type;
1949 if (regexec (&r, string, 1, pmatch, 0) != 0)
1950 return 0;
1951 view->found_len = pmatch[0].rm_eo - pmatch[0].rm_so;
1952 view->search_start = pmatch[0].rm_so;
1953 return 1;
1956 static void
1957 do_regexp_search (void *xview, char *regexp)
1959 WView *view = (WView *) xview;
1961 view->search_exp = regexp;
1962 search (view, regexp, regexp_view_search);
1963 /* Had a refresh here */
1964 view->dirty++;
1965 view_update (view, TRUE);
1968 static void
1969 do_normal_search (void *xview, char *text)
1971 WView *view = (WView *) xview;
1973 view->search_exp = text;
1974 if (view->hex_mode)
1975 hex_search (view, text);
1976 else
1977 search (view, text, icase_search_p);
1978 /* Had a refresh here */
1979 view->dirty++;
1980 view_update (view, TRUE);
1983 /* Real view only */
1984 static void
1985 view_help_cmd (void)
1987 interactive_display (NULL, "[Internal File Viewer]");
1989 view_refresh (0);
1993 /* Toggle between text edit and hex edit */
1994 static void
1995 toggle_hexedit_mode (WView *view)
1997 get_bottom_first (view, 1, 1);
1998 if (view->hexedit_mode) {
1999 view->hexedit_text = !view->hexedit_text;
2000 } else {
2001 view->hexedit_mode = !view->hexedit_mode;
2003 view_labels (view);
2004 view->dirty++;
2005 view_update (view, TRUE);
2008 /* Toggle between wrapped and unwrapped view */
2009 static void
2010 toggle_wrap_mode (WView *view)
2012 view->wrap_mode = 1 - view->wrap_mode;
2013 get_bottom_first (view, 1, 1);
2014 if (view->wrap_mode)
2015 view->start_col = 0;
2016 else {
2017 if (have_fast_cpu) {
2018 if (view->bottom_first < view->start_display)
2019 view->search_start = view->start_display =
2020 view->bottom_first;
2021 view->found_len = 0;
2024 view_labels (view);
2025 view->dirty++;
2026 view_update (view, TRUE);
2029 /* Toggle between hex view and text view */
2030 static void
2031 toggle_hex_mode (WView *view)
2033 view->hex_mode = 1 - view->hex_mode;
2035 if (view->hex_mode) {
2036 /* Shift the line start to 0x____0 on entry, restore it for text */
2037 view->start_save = view->start_display;
2038 view->start_display -= view->start_display % view->bytes_per_line;
2039 view->edit_cursor = view->start_display;
2040 view->widget.options |= W_WANT_CURSOR;
2041 } else {
2042 view->start_display = view->start_save;
2043 view->widget.options &= ~W_WANT_CURSOR;
2045 altered_hex_mode = 1;
2046 get_bottom_first (view, 1, 1);
2047 view_labels (view);
2048 view->dirty++;
2049 view_update (view, TRUE);
2052 /* Text view */
2053 static void
2054 goto_line (WView *view)
2056 char *line, prompt[BUF_SMALL];
2057 int oldline = 1;
2058 int saved_wrap_mode = view->wrap_mode;
2059 offset_type i;
2061 view->wrap_mode = 0;
2062 for (i = view->first; i < view->start_display; i++)
2063 if (get_byte (view, i) == '\n')
2064 oldline++;
2065 g_snprintf (prompt, sizeof (prompt),
2066 _(" The current line number is %d.\n"
2067 " Enter the new line number:"), oldline);
2068 line = input_dialog (_(" Goto line "), prompt, "");
2069 if (line) {
2070 if (*line) {
2071 move_to_top (view);
2072 view_move_forward (view, atol (line) - 1);
2074 g_free (line);
2076 view->dirty++;
2077 view->wrap_mode = saved_wrap_mode;
2078 view_update (view, TRUE);
2081 /* Hex view */
2082 static void
2083 goto_addr (WView *view)
2085 char *line, *error, prompt[BUF_SMALL];
2086 offset_type addr;
2088 g_snprintf (prompt, sizeof (prompt),
2089 _(" The current address is 0x%lx.\n"
2090 " Enter the new address:"), view->edit_cursor);
2091 line = input_dialog (_(" Goto Address "), prompt, "");
2092 if (line) {
2093 if (*line) {
2094 addr = strtoul (line, &error, 0);
2095 if ((*error == '\0') && (addr <= view->last_byte)) {
2096 move_to_top (view);
2097 view_move_forward (view, addr / view->bytes_per_line);
2098 view->edit_cursor = addr;
2101 g_free (line);
2103 view->dirty++;
2104 view_update (view, TRUE);
2107 /* Both views */
2108 static void
2109 regexp_search (WView *view, int direction)
2111 char *regexp = "";
2112 static char *old = 0;
2114 /* This is really an F6 key handler */
2115 if (view->hex_mode) {
2116 /* Save it without a confirmation prompt */
2117 if (view->change_list)
2118 save_edit_changes (view);
2119 return;
2122 regexp = old ? old : regexp;
2123 regexp = input_dialog (_("Search"), _(" Enter regexp:"), regexp);
2124 if ((!regexp)) {
2125 return;
2127 if ((!*regexp)) {
2128 g_free (regexp);
2129 return;
2131 g_free (old);
2132 old = regexp;
2133 #if 0
2134 /* Mhm, do we really need to load all the file in the core? */
2135 if (view->bytes_read < view->last_byte)
2136 get_byte (view, view->last_byte - 1); /* Get the whole file in to memory */
2137 #endif
2138 view->direction = direction;
2139 do_regexp_search (view, regexp);
2141 view->last_search = do_regexp_search;
2144 static void
2145 regexp_search_cmd (WView *view)
2147 regexp_search (view, 1);
2150 /* Both views */
2151 static void
2152 normal_search (WView *view, int direction)
2154 static char *old;
2155 char *exp = old ? old : "";
2157 enum {
2158 SEARCH_DLG_HEIGHT = 8,
2159 SEARCH_DLG_WIDTH = 58
2162 static int replace_backwards;
2163 int treplace_backwards = replace_backwards;
2165 static QuickWidget quick_widgets[] = {
2166 {quick_button, 6, 10, 5, SEARCH_DLG_HEIGHT, N_("&Cancel"), 0,
2167 B_CANCEL,
2168 0, 0, NULL},
2169 {quick_button, 2, 10, 5, SEARCH_DLG_HEIGHT, N_("&OK"), 0, B_ENTER,
2170 0, 0, NULL},
2171 {quick_checkbox, 3, SEARCH_DLG_WIDTH, 4, SEARCH_DLG_HEIGHT,
2172 N_("&Backwards"), 0, 0,
2173 0, 0, NULL},
2174 {quick_input, 3, SEARCH_DLG_WIDTH, 3, SEARCH_DLG_HEIGHT, "", 52, 0,
2175 0, 0, N_("Search")},
2176 {quick_label, 2, SEARCH_DLG_WIDTH, 2, SEARCH_DLG_HEIGHT,
2177 N_(" Enter search string:"), 0, 0,
2178 0, 0, 0},
2179 NULL_QuickWidget
2181 static QuickDialog Quick_input = {
2182 SEARCH_DLG_WIDTH, SEARCH_DLG_HEIGHT, -1, 0, N_("Search"),
2183 "[Input Line Keys]", quick_widgets, 0
2185 convert_to_display (old);
2187 quick_widgets[2].result = &treplace_backwards;
2188 quick_widgets[3].str_result = &exp;
2189 quick_widgets[3].text = exp;
2191 if (quick_dialog (&Quick_input) == B_CANCEL) {
2192 convert_from_input (old);
2193 return;
2195 replace_backwards = treplace_backwards;
2197 convert_from_input (old);
2199 if ((!exp)) {
2200 return;
2202 if ((!*exp)) {
2203 g_free (exp);
2204 return;
2207 g_free (old);
2208 old = exp;
2210 convert_from_input (exp);
2212 view->direction = replace_backwards ? -1 : 1;
2213 do_normal_search (view, exp);
2214 view->last_search = do_normal_search;
2217 static void
2218 normal_search_cmd (WView *view)
2220 normal_search (view, 1);
2223 static void
2224 change_viewer (WView *view)
2226 char *s;
2227 char *t;
2230 if (*view->filename) {
2231 altered_magic_flag = 1;
2232 view->viewer_magic_flag = !view->viewer_magic_flag;
2233 s = g_strdup (view->filename);
2234 if (view->command)
2235 t = g_strdup (view->command);
2236 else
2237 t = 0;
2239 view_done (view);
2240 view_init (view, t, s, 0);
2241 g_free (s);
2242 g_free (t);
2243 view_labels (view);
2244 view->dirty++;
2245 view_update (view, TRUE);
2249 static void
2250 change_nroff (WView *view)
2252 view->viewer_nroff_flag = !view->viewer_nroff_flag;
2253 altered_nroff_flag = 1;
2254 view_labels (view);
2255 view->dirty++;
2256 view_update (view, TRUE);
2259 /* Real view only */
2260 static void
2261 view_quit_cmd (WView *view)
2263 if (view_ok_to_quit (view))
2264 dlg_stop (view->widget.parent);
2267 /* Define labels and handlers for functional keys */
2268 static void
2269 view_labels (WView *view)
2271 Dlg_head *h = view->widget.parent;
2273 define_label (h, 1, _("Help"), view_help_cmd);
2275 my_define (h, 10, _("Quit"), view_quit_cmd, view);
2276 my_define (h, 4, view->hex_mode ? _("Ascii") : _("Hex"),
2277 toggle_hex_mode, view);
2278 my_define (h, 5, view->hex_mode ? _("Goto") : _("Line"),
2279 view->hex_mode ? goto_addr : goto_line, view);
2280 my_define (h, 6, view->hex_mode ? _("Save") : _("RxSrch"),
2281 regexp_search_cmd, view);
2283 if (view->hex_mode)
2284 if (view->hexedit_mode)
2285 my_define (h, 2, view->hexedit_text ? _("EdHex") : _("EdText"),
2286 toggle_hexedit_mode, view);
2287 else {
2288 if (view->growing_buffer || view->have_frame)
2289 my_define (h, 2, "", NULL, view);
2290 else
2291 my_define (h, 2, _("Edit"), toggle_hexedit_mode, view);
2292 } else
2293 my_define (h, 2, view->wrap_mode ? _("UnWrap") : _("Wrap"),
2294 toggle_wrap_mode, view);
2296 my_define (h, 7, view->hex_mode ? _("HxSrch") : _("Search"),
2297 normal_search_cmd, view);
2299 my_define (h, 8, view->viewer_magic_flag ? _("Raw") : _("Parse"),
2300 change_viewer, view);
2302 if (!view->have_frame) {
2303 my_define (h, 9,
2304 view->viewer_nroff_flag ? _("Unform") : _("Format"),
2305 change_nroff, view);
2306 my_define (h, 3, _("Quit"), view_quit_cmd, view);
2309 redraw_labels (h);
2312 /* Check for left and right arrows, possibly with modifiers */
2313 static cb_ret_t
2314 check_left_right_keys (WView *view, int c)
2316 if (c == KEY_LEFT) {
2317 move_left (view);
2318 return MSG_HANDLED;
2321 if (c == KEY_RIGHT) {
2322 move_right (view);
2323 return MSG_HANDLED;
2326 /* Ctrl with arrows moves by 10 postions in the unwrap mode */
2327 if (view->hex_mode || view->wrap_mode)
2328 return MSG_NOT_HANDLED;
2330 if (c == (KEY_M_CTRL | KEY_LEFT)) {
2331 view->start_col = view->start_col + 10;
2332 if (view->start_col > 0)
2333 view->start_col = 0;
2334 view->dirty++;
2335 return MSG_HANDLED;
2338 if (c == (KEY_M_CTRL | KEY_RIGHT)) {
2339 view->start_col = view->start_col - 10;
2340 view->dirty++;
2341 return MSG_HANDLED;
2344 return MSG_NOT_HANDLED;
2347 static void
2348 set_monitor (WView *view, int set_on)
2350 int old = view->monitor;
2352 view->monitor = set_on;
2354 if (view->monitor) {
2355 move_to_bottom (view);
2356 view->bottom_first = INVALID_OFFSET;
2357 set_idle_proc (view->widget.parent, 1);
2358 } else {
2359 if (old)
2360 set_idle_proc (view->widget.parent, 0);
2364 static void
2365 continue_search (WView *view)
2367 if (view->last_search) {
2368 (*view->last_search) (view, view->search_exp);
2369 } else {
2370 /* if not... then ask for an expression */
2371 normal_search (view, 1);
2375 /* Both views */
2376 static cb_ret_t
2377 view_handle_key (WView *view, int c)
2379 int prev_monitor = view->monitor;
2381 set_monitor (view, off);
2383 c = convert_from_input_c (c);
2385 if (view->hex_mode) {
2386 switch (c) {
2387 case 0x09: /* Tab key */
2388 view->hexedit_text = !view->hexedit_text;
2389 view->dirty++;
2390 return MSG_HANDLED;
2392 case XCTRL ('a'): /* Beginning of line */
2393 view->edit_cursor -= view->edit_cursor % view->bytes_per_line;
2394 view->dirty++;
2395 return MSG_HANDLED;
2397 case XCTRL ('b'): /* Character back */
2398 move_left (view);
2399 return MSG_HANDLED;
2401 case XCTRL ('e'): /* End of line */
2402 view_move_cursor_to_eol (view);
2403 return MSG_HANDLED;
2405 case XCTRL ('f'): /* Character forward */
2406 move_right (view);
2407 return MSG_HANDLED;
2410 if (view->hexedit_mode
2411 && view_handle_editkey (view, c) == MSG_HANDLED)
2412 return MSG_HANDLED;
2415 if (check_left_right_keys (view, c))
2416 return MSG_HANDLED;
2418 if (check_movement_keys
2419 (c, vheight, view, (movefn) view_move_backward,
2420 (movefn) view_move_forward, (movefn) move_to_top,
2421 (movefn) move_to_bottom)) {
2422 return MSG_HANDLED;
2424 switch (c) {
2426 case '?':
2427 regexp_search (view, -1);
2428 return MSG_HANDLED;
2430 case '/':
2431 regexp_search (view, 1);
2432 return MSG_HANDLED;
2434 /* Continue search */
2435 case XCTRL ('s'):
2436 case 'n':
2437 case KEY_F (17):
2438 continue_search (view);
2439 return MSG_HANDLED;
2441 case XCTRL ('r'):
2442 if (view->last_search) {
2443 (*view->last_search) (view, view->search_exp);
2444 } else {
2445 normal_search (view, -1);
2447 return MSG_HANDLED;
2449 /* toggle ruler */
2450 case ALT ('r'):
2451 switch (ruler) {
2452 case 0:
2453 ruler = 1;
2454 break;
2455 case 1:
2456 ruler = 2;
2457 break;
2458 default:
2459 ruler = 0;
2460 break;
2462 view->dirty++;
2463 return MSG_HANDLED;
2465 case 'h':
2466 move_left (view);
2467 return MSG_HANDLED;
2469 case 'j':
2470 case '\n':
2471 case 'e':
2472 view_move_forward (view, 1);
2473 return MSG_HANDLED;
2475 case 'd':
2476 view_move_forward (view, vheight / 2);
2477 return MSG_HANDLED;
2479 case 'u':
2480 view_move_backward (view, vheight / 2);
2481 return MSG_HANDLED;
2483 case 'k':
2484 case 'y':
2485 view_move_backward (view, 1);
2486 return MSG_HANDLED;
2488 case 'l':
2489 move_right (view);
2490 return MSG_HANDLED;
2492 case ' ':
2493 case 'f':
2494 view_move_forward (view, vheight - 1);
2495 return MSG_HANDLED;
2497 case XCTRL ('o'):
2498 view_other_cmd ();
2499 return MSG_HANDLED;
2501 /* Unlike Ctrl-O, run a new shell if the subshell is not running. */
2502 case '!':
2503 exec_shell ();
2504 return MSG_HANDLED;
2506 case 'F':
2507 set_monitor (view, on);
2508 return MSG_HANDLED;
2510 case 'b':
2511 view_move_backward (view, vheight - 1);
2512 return MSG_HANDLED;
2514 case KEY_IC:
2515 view_move_backward (view, 2);
2516 return MSG_HANDLED;
2518 case KEY_DC:
2519 view_move_forward (view, 2);
2520 return MSG_HANDLED;
2522 case 'm':
2523 view->marks[view->marker] = view->start_display;
2524 return MSG_HANDLED;
2526 case 'r':
2527 view->start_display = view->marks[view->marker];
2528 view->dirty++;
2529 return MSG_HANDLED;
2531 /* Use to indicate parent that we want to see the next/previous file */
2532 /* Only works on full screen mode */
2533 case XCTRL ('f'):
2534 case XCTRL ('b'):
2535 if (!view->have_frame)
2536 view->move_dir = c == XCTRL ('f') ? 1 : -1;
2537 /* fall */
2539 case 'q':
2540 case XCTRL ('g'):
2541 case ESC_CHAR:
2542 if (view_ok_to_quit (view))
2543 view->view_quit = 1;
2544 return MSG_HANDLED;
2546 #ifdef HAVE_CHARSET
2547 case XCTRL ('t'):
2548 do_select_codepage ();
2549 view->dirty++;
2550 view_update (view, TRUE);
2551 return MSG_HANDLED;
2552 #endif /* HAVE_CHARSET */
2555 if (c >= '0' && c <= '9')
2556 view->marker = c - '0';
2558 /* Restore the monitor status */
2559 set_monitor (view, prev_monitor);
2561 /* Key not used */
2562 return MSG_NOT_HANDLED;
2565 /* Both views */
2566 static int
2567 view_event (WView *view, Gpm_Event *event, int *result)
2569 *result = MOU_NORMAL;
2571 /* We are not interested in the release events */
2572 if (!(event->type & (GPM_DOWN | GPM_DRAG)))
2573 return 0;
2575 /* Wheel events */
2576 if ((event->buttons & GPM_B_UP) && (event->type & GPM_DOWN)) {
2577 view_move_backward (view, 2);
2578 return 1;
2580 if ((event->buttons & GPM_B_DOWN) && (event->type & GPM_DOWN)) {
2581 view_move_forward (view, 2);
2582 return 1;
2585 /* Scrolling left and right */
2586 if (!view->wrap_mode) {
2587 if (event->x < view->widget.cols / 4) {
2588 move_left (view);
2589 goto processed;
2591 if (event->x > 3 * vwidth / 4) {
2592 move_right (view);
2593 goto processed;
2597 /* Scrolling up and down */
2598 if (event->y < view->widget.lines / 3) {
2599 if (mouse_move_pages_viewer)
2600 view_move_backward (view, view->widget.lines / 2 - 1);
2601 else
2602 view_move_backward (view, 1);
2603 goto processed;
2604 } else if (event->y > 2 * vheight / 3) {
2605 if (mouse_move_pages_viewer)
2606 view_move_forward (view, vheight / 2 - 1);
2607 else
2608 view_move_forward (view, 1);
2609 goto processed;
2612 return 0;
2614 processed:
2615 *result = MOU_REPEAT;
2616 return 1;
2619 /* Real view only */
2620 static int
2621 real_view_event (Gpm_Event *event, void *x)
2623 int result;
2625 if (view_event ((WView *) x, event, &result))
2626 view_update ((WView *) x, TRUE);
2627 return result;
2630 static void
2631 view_adjust_size (Dlg_head *h)
2633 WView *view;
2634 WButtonBar *bar;
2636 /* Look up the viewer and the buttonbar, we assume only two widgets here */
2637 view = (WView *) find_widget_type (h, (callback_fn) view_callback);
2638 bar = find_buttonbar (h);
2639 widget_set_size (&view->widget, 0, 0, LINES - 1, COLS);
2640 widget_set_size (&bar->widget, LINES - 1, 0, 1, COLS);
2642 view_update_bytes_per_line (view);
2645 /* Callback for the view dialog */
2646 static cb_ret_t
2647 view_dialog_callback (Dlg_head *h, dlg_msg_t msg, int parm)
2649 switch (msg) {
2650 case DLG_RESIZE:
2651 view_adjust_size (h);
2652 return MSG_HANDLED;
2654 default:
2655 return default_dlg_callback (h, msg, parm);
2659 /* Real view only */
2661 view (const char *_command, const char *_file, int *move_dir_p, int start_line)
2663 int error;
2664 WView *wview;
2665 WButtonBar *bar;
2666 Dlg_head *view_dlg;
2668 /* Create dialog and widgets, put them on the dialog */
2669 view_dlg =
2670 create_dlg (0, 0, LINES, COLS, NULL, view_dialog_callback,
2671 "[Internal File Viewer]", NULL, DLG_WANT_TAB);
2673 wview = view_new (0, 0, COLS, LINES - 1, 0);
2675 bar = buttonbar_new (1);
2677 add_widget (view_dlg, bar);
2678 add_widget (view_dlg, wview);
2680 error = view_init (wview, _command, _file, start_line);
2681 if (move_dir_p)
2682 *move_dir_p = 0;
2684 /* Please note that if you add another widget,
2685 * you have to modify view_adjust_size to
2686 * be aware of it
2688 if (!error) {
2689 run_dlg (view_dlg);
2690 if (move_dir_p)
2691 *move_dir_p = wview->move_dir;
2693 destroy_dlg (view_dlg);
2695 return !error;
2698 static void
2699 view_hook (void *v)
2701 WView *view = (WView *) v;
2702 WPanel *panel;
2704 /* If the user is busy typing, wait until he finishes to update the
2705 screen */
2706 if (!is_idle ()) {
2707 if (!hook_present (idle_hook, view_hook))
2708 add_hook (&idle_hook, view_hook, v);
2709 return;
2712 delete_hook (&idle_hook, view_hook);
2714 if (get_current_type () == view_listing)
2715 panel = current_panel;
2716 else if (get_other_type () == view_listing)
2717 panel = other_panel;
2718 else
2719 return;
2721 view_init (view, 0, panel->dir.list[panel->selected].fname, 0);
2722 display (view);
2723 view_status (view, TRUE);
2726 static cb_ret_t
2727 view_callback (WView *view, widget_msg_t msg, int parm)
2729 cb_ret_t i;
2730 Dlg_head *h = view->widget.parent;
2732 view_update_bytes_per_line (view);
2734 switch (msg) {
2735 case WIDGET_INIT:
2736 view_update_bytes_per_line (view);
2737 if (view->have_frame)
2738 add_hook (&select_file_hook, view_hook, view);
2739 else
2740 view_labels (view);
2741 return MSG_HANDLED;
2743 case WIDGET_DRAW:
2744 display (view);
2745 view_status (view, TRUE);
2746 return MSG_HANDLED;
2748 case WIDGET_CURSOR:
2749 if (view->hex_mode)
2750 view_place_cursor (view);
2751 return MSG_HANDLED;
2753 case WIDGET_KEY:
2754 i = view_handle_key ((WView *) view, parm);
2755 if (view->view_quit && !view->have_frame)
2756 dlg_stop (h);
2757 else {
2758 view_update (view, TRUE);
2760 return i;
2762 case WIDGET_IDLE:
2763 /* This event is generated when the user is using the 'F' flag */
2764 view->bottom_first = INVALID_OFFSET;
2765 move_to_bottom (view);
2766 display (view);
2767 view_status (view, TRUE);
2768 sleep (1);
2769 return MSG_HANDLED;
2771 case WIDGET_FOCUS:
2772 view_labels (view);
2773 return MSG_HANDLED;
2775 case WIDGET_DESTROY:
2776 view_done (view);
2777 if (view->have_frame)
2778 delete_hook (&select_file_hook, view_hook);
2779 return MSG_HANDLED;
2781 case WIDGET_RESIZED:
2782 view_update_bytes_per_line (view);
2783 /* FALLTROUGH */
2785 default:
2786 return default_proc (msg, parm);
2790 WView *
2791 view_new (int y, int x, int cols, int lines, int is_panel)
2793 WView *view = g_new0 (WView, 1);
2795 init_widget (&view->widget, y, x, lines, cols,
2796 (callback_fn) view_callback,
2797 (mouse_h) real_view_event);
2799 view->hex_mode = default_hex_mode;
2800 view->hexedit_mode = default_hexedit_mode;
2801 view->viewer_magic_flag = default_magic_flag;
2802 view->viewer_nroff_flag = default_nroff_flag;
2803 view->have_frame = is_panel;
2804 view->wrap_mode = global_wrap_mode;
2806 widget_want_cursor (view->widget, 0);
2808 return view;