Just a little correction at the it.po file.
[midnight-commander.git] / src / view.c
blob9c48a6dacc20af759e5a563a9309cdbd25bb68f2
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
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
24 #include <config.h>
25 #include <stdio.h>
26 #include <sys/types.h>
27 #ifdef HAVE_UNISTD_H
28 # include <unistd.h>
29 #endif
30 #include <string.h>
31 #include <sys/stat.h>
32 #ifdef HAVE_MMAP
33 # include <sys/mman.h>
34 #endif
35 #include <ctype.h> /* For toupper() */
36 #include <errno.h>
37 #include <limits.h>
39 #include "global.h"
40 #include "tty.h"
41 #include "cmd.h" /* For view_other_cmd */
42 #include "dlg.h" /* Needed by widget.h */
43 #include "widget.h" /* Needed for buttonbar_new */
44 #include "color.h"
45 #include "dialog.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 "../vfs/vfs.h"
53 #include "dir.h"
54 #include "panel.h" /* Needed for current_panel and other_panel */
55 #include "win.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 #ifdef IS_AIX
72 # define IFAIX(x) case (x):
73 #else
74 # define IFAIX(x)
75 #endif
77 #define vwidth (view->widget.cols - (view->have_frame ? 2 : 0))
78 #define vheight (view->widget.lines - (view->have_frame ? 2 : 0))
80 /* The growing buffers data types */
81 typedef struct block_ptr_t {
82 unsigned char *data;
83 } block_ptr_t;
85 /* A node for building a change list on change_list */
86 struct hexedit_change_node {
87 struct hexedit_change_node *next;
88 long offset;
89 unsigned char value;
92 enum ViewSide {
93 view_side_left,
94 view_side_right
97 struct WView {
98 Widget widget;
100 char *filename; /* Name of the file */
101 char *command; /* Command used to pipe data in */
102 char *localcopy;
103 int view_active;
104 int have_frame;
106 unsigned char *data; /* Memory area for the file to be viewed */
108 /* File information */
109 int file; /* File descriptor (for mmap and munmap) */
110 FILE *stdfile; /* Stdio struct for reading file in parts */
111 int reading_pipe; /* Flag: Reading from pipe(use popen/pclose) */
112 unsigned long bytes_read; /* How much of file is read */
113 int mmapping; /* Did we use mmap on the file? */
115 /* Display information */
116 unsigned long last; /* Last byte shown */
117 unsigned long last_byte; /* Last byte of file */
118 long first; /* First byte in file */
119 long bottom_first; /* First byte shown when very last page is displayed */
120 /* For the case of WINCH we should reset it to -1 */
121 unsigned long start_display;/* First char displayed */
122 int start_col; /* First displayed column, negative */
123 unsigned long edit_cursor; /* HexEdit cursor position in file */
124 char hexedit_mode; /* Hexidecimal editing mode flag */
125 char nib_shift; /* A flag for inserting nibbles into bytes */
126 enum ViewSide view_side; /* A flag for the active editing panel */
127 int file_dirty; /* Number of changes */
128 int start_save; /* Line start shift between Ascii and Hex */
129 int cursor_col; /* Cursor column */
130 int cursor_row; /* Cursor row */
131 struct hexedit_change_node *change_list; /* Linked list of changes */
133 int dirty; /* Number of skipped updates */
134 int wrap_mode; /* wrap_mode */
136 /* Mode variables */
137 int hex_mode; /* Hexadecimal mode flag */
138 int bytes_per_line; /* Number of bytes per line in hex mode */
139 int viewer_magic_flag; /* Selected viewer */
140 int viewer_nroff_flag; /* Do we do nroff style highlighting? */
142 /* Growing buffers information */
143 int growing_buffer; /* Use the growing buffers? */
144 struct block_ptr_t *block_ptr; /* Pointer to the block pointers */
145 int blocks; /* The number of blocks in *block_ptr */
148 /* Search variables */
149 int search_start; /* First character to start searching from */
150 int found_len; /* Length of found string or 0 if none was found */
151 char *search_exp; /* The search expression */
152 int direction; /* 1= forward; -1 backward */
153 void (*last_search)(void *, char *);
154 /* Pointer to the last search command */
155 int view_quit; /* Quit flag */
157 int monitor; /* Monitor file growth (like tail -f) */
158 /* Markers */
159 int marker; /* mark to use */
160 int marks [10]; /* 10 marks: 0..9 */
163 int move_dir; /* return value from widget:
164 * 0 do nothing
165 * -1 view previous file
166 * 1 view next file
168 struct stat s; /* stat for file */
172 /* Maxlimit for skipping updates */
173 int max_dirt_limit =
174 #ifdef NATIVE_WIN32
176 #else
178 #endif
180 extern Hook *idle_hook;
182 /* If set, show a ruler */
183 static int ruler = 0;
185 /* Scrolling is done in pages or line increments */
186 int mouse_move_pages_viewer = 1;
188 /* Used to compute the bottom first variable */
189 int have_fast_cpu = 0;
191 /* wrap mode default */
192 int global_wrap_mode = 1;
194 int default_hex_mode = 0;
195 static int default_hexedit_mode = 0;
196 int default_magic_flag = 1;
197 int default_nroff_flag = 1;
198 int altered_hex_mode = 0;
199 int altered_magic_flag = 0;
200 int altered_nroff_flag = 0;
202 static const char hex_char[] = "0123456789ABCDEF";
204 /* Our callback */
205 static int view_callback (WView *view, int msg, int par);
207 static int regexp_view_search (WView * view, char *pattern, char *string,
208 int match_type);
209 static void view_move_forward (WView * view, int i);
210 static void view_labels (WView * view);
211 static void set_monitor (WView * view, int set_on);
212 static void view_update (WView * view, gboolean update_gui);
215 static void
216 close_view_file (WView *view)
218 if (view->file != -1) {
219 mc_close (view->file);
220 view->file = -1;
224 static void
225 free_file (WView *view)
227 int i;
229 #ifdef HAVE_MMAP
230 if (view->mmapping) {
231 mc_munmap (view->data, view->s.st_size);
232 close_view_file (view);
233 } else
234 #endif /* HAVE_MMAP */
236 if (view->reading_pipe) {
237 /* Check error messages */
238 if (!view->have_frame)
239 check_error_pipe ();
241 /* Close pipe */
242 pclose (view->stdfile);
243 view->stdfile = NULL;
245 /* Ignore errors because we don't want to hear about broken pipe */
246 close_error_pipe (-1, NULL);
247 } else
248 close_view_file (view);
250 /* Block_ptr may be zero if the file was a file with 0 bytes */
251 if (view->growing_buffer && view->block_ptr) {
252 for (i = 0; i < view->blocks; i++) {
253 g_free (view->block_ptr[i].data);
255 g_free (view->block_ptr);
259 /* Valid parameters for second parameter to set_monitor */
260 enum { off, on };
262 /* Both views */
263 static void
264 view_done (WView *view)
266 set_monitor (view, off);
268 /* alex: release core, used to replace mmap */
269 if (!view->mmapping && !view->growing_buffer && view->data != NULL) {
270 g_free (view->data);
271 view->data = NULL;
274 if (view->view_active) {
275 if (view->localcopy)
276 mc_ungetlocalcopy (view->filename, view->localcopy, 0);
277 free_file (view);
278 g_free (view->filename);
279 if (view->command)
280 g_free (view->command);
282 view->view_active = 0;
283 default_hex_mode = view->hex_mode;
284 default_nroff_flag = view->viewer_nroff_flag;
285 default_magic_flag = view->viewer_magic_flag;
286 global_wrap_mode = view->wrap_mode;
289 static void view_hook (void *);
291 static void
292 view_destroy (WView *view)
294 view_done (view);
295 if (view->have_frame)
296 delete_hook (&select_file_hook, view_hook);
299 static int
300 get_byte (WView *view, unsigned int byte_index)
302 int page = byte_index / VIEW_PAGE_SIZE + 1;
303 int offset = byte_index % VIEW_PAGE_SIZE;
304 int i, n;
306 if (view->growing_buffer) {
307 if (page > view->blocks) {
308 view->block_ptr = g_realloc (view->block_ptr,
309 sizeof (block_ptr_t) * page);
310 for (i = view->blocks; i < page; i++) {
311 char *p = g_malloc (VIEW_PAGE_SIZE);
312 view->block_ptr[i].data = p;
313 if (!p)
314 return '\n';
315 if (view->stdfile != NULL)
316 n = fread (p, 1, VIEW_PAGE_SIZE, view->stdfile);
317 else
318 n = mc_read (view->file, p, VIEW_PAGE_SIZE);
320 * FIXME: Errors are ignored at this point
321 * Also should report preliminary EOF
323 if (n != -1)
324 view->bytes_read += n;
325 if (view->s.st_size < view->bytes_read) {
326 view->bottom_first = -1; /* Invalidate cache */
327 view->s.st_size = view->bytes_read;
328 view->last_byte = view->bytes_read;
329 if (view->reading_pipe)
330 view->last_byte = view->first + view->bytes_read;
332 /* To force loading the next page */
333 if (n == VIEW_PAGE_SIZE && view->reading_pipe) {
334 view->last_byte++;
337 view->blocks = page;
339 if (byte_index > view->bytes_read) {
340 return -1;
341 } else
342 return view->block_ptr[page - 1].data[offset];
343 } else {
344 if (byte_index >= view->last_byte)
345 return -1;
346 else
347 return view->data[byte_index];
351 static void
352 enqueue_change (struct hexedit_change_node **head,
353 struct hexedit_change_node *node)
355 struct hexedit_change_node *curr = *head;
357 while (curr) {
358 if (node->offset < curr->offset) {
359 *head = node;
360 node->next = curr;
361 return;
363 head = (struct hexedit_change_node **) curr;
364 curr = curr->next;
366 *head = node;
367 node->next = curr;
370 static void move_right (WView *);
372 static void
373 put_editkey (WView *view, unsigned char key)
375 struct hexedit_change_node *node;
376 unsigned char byte_val;
378 if (!view->hexedit_mode || view->growing_buffer != 0)
379 return;
381 /* Has there been a change at this position ? */
382 node = view->change_list;
383 while (node && (node->offset != view->edit_cursor)) {
384 node = node->next;
387 if (view->view_side == view_side_left) {
388 /* Hex editing */
390 if (key >= '0' && key <= '9')
391 key -= '0';
392 else if (key >= 'A' && key <= 'F')
393 key -= '7';
394 else if (key >= 'a' && key <= 'f')
395 key -= 'W';
396 else
397 return;
399 if (node)
400 byte_val = node->value;
401 else
402 byte_val = get_byte (view, view->edit_cursor);
404 if (view->nib_shift == 0) {
405 byte_val = (byte_val & 0x0f) | (key << 4);
406 } else {
407 byte_val = (byte_val & 0xf0) | (key);
409 } else {
410 /* Text editing */
411 byte_val = key;
413 if (!node) {
414 node = (struct hexedit_change_node *)
415 g_new (struct hexedit_change_node, 1);
417 if (node) {
418 #ifndef HAVE_MMAP
419 /* alex@bcs.zaporizhzhe.ua: here we are using file copy
420 * completely loaded into memory, so we can replace bytes in
421 * view->data array to allow changes to be reflected when
422 * user switches back to ascii mode */
423 view->data[view->edit_cursor] = byte_val;
424 #endif /* !HAVE_MMAP */
425 node->offset = view->edit_cursor;
426 node->value = byte_val;
427 enqueue_change (&view->change_list, node);
429 } else {
430 node->value = byte_val;
432 view->dirty++;
433 view_update (view, TRUE);
434 move_right (view);
437 static void
438 free_change_list (WView *view)
440 struct hexedit_change_node *n = view->change_list;
442 while (n) {
443 view->change_list = n->next;
444 g_free (n);
445 n = view->change_list;
447 view->file_dirty = 0;
448 view->dirty++;
451 static void
452 save_edit_changes (WView *view)
454 struct hexedit_change_node *node = view->change_list;
455 int fp;
457 do {
458 fp = open (view->filename, O_WRONLY);
459 if (fp >= 0) {
460 while (node) {
461 if (lseek (fp, node->offset, SEEK_SET) == -1 ||
462 write (fp, &node->value, 1) != 1) {
463 close (fp);
464 fp = -1;
465 break;
467 node = node->next;
469 if (fp != -1)
470 close (fp);
473 if (fp == -1) {
474 fp = query_dialog (_(" Save file "),
475 _(" Cannot save file. "),
476 2, 2, _("&Retry"), _("&Cancel")) - 1;
478 } while (fp == -1);
480 free_change_list (view);
483 static int
484 view_ok_to_quit (WView *view)
486 int r;
488 if (!view->change_list)
489 return 1;
491 r = query_dialog (_("Quit"),
492 _(" File was modified, Save with exit? "), 2, 3,
493 _("Cancel quit"), _("&Yes"), _("&No"));
495 switch (r) {
496 case 1:
497 save_edit_changes (view);
498 return 1;
499 case 2:
500 free_change_list (view);
501 return 1;
502 default:
503 return 0;
507 static char *
508 set_view_init_error (WView *view, const char *msg)
510 view->growing_buffer = 0;
511 view->reading_pipe = 0;
512 view->first = 0;
513 view->last_byte = 0;
514 if (msg) {
515 view->bytes_read = strlen (msg);
516 return g_strdup (msg);
518 return 0;
521 /* return values: NULL for success, else points to error message */
522 static char *
523 init_growing_view (WView *view, char *name, char *filename)
525 char *err_msg = NULL;
527 view->growing_buffer = 1;
529 if (name) {
530 view->reading_pipe = 1;
531 view->s.st_size = 0;
533 open_error_pipe ();
534 if ((view->stdfile = popen (name, "r")) == NULL) {
535 /* Avoid two messages. Message from stderr has priority. */
536 if (!close_error_pipe (view->have_frame ? -1 : 1, view->data))
537 err_msg = _(" Cannot spawn child program ");
538 return set_view_init_error (view, err_msg);
541 /* First, check if filter produced any output */
542 get_byte (view, 0);
543 if (view->bytes_read <= 0) {
544 pclose (view->stdfile);
545 view->stdfile = NULL;
546 /* Avoid two messages. Message from stderr has priority. */
547 if (!close_error_pipe (view->have_frame ? -1 : 1, view->data))
548 err_msg = _("Empty output from child filter");
549 return set_view_init_error (view, err_msg);
551 } else {
552 view->stdfile = NULL;
553 if ((view->file = mc_open (filename, O_RDONLY)) == -1)
554 return set_view_init_error (view, _(" Cannot open file "));
556 return NULL;
559 /* Load filename into core */
560 /* returns:
561 -1 on failure.
562 if (have_frame), we return success, but data points to a
563 error message instead of the file buffer (quick_view feature).
565 static char *
566 load_view_file (WView *view, int fd)
568 view->file = fd;
570 if (view->s.st_size == 0) {
571 /* Must be one of those nice files that grow (/proc) */
572 close_view_file (view);
573 return init_growing_view (view, 0, view->filename);
575 #ifdef HAVE_MMAP
576 view->data =
577 mc_mmap (0, view->s.st_size, PROT_READ, MAP_FILE | MAP_SHARED,
578 view->file, 0);
579 if ((caddr_t) view->data != (caddr_t) - 1) {
580 /* mmap worked */
581 view->first = 0;
582 view->bytes_read = view->s.st_size;
583 view->mmapping = 1;
584 return NULL;
586 #endif /* HAVE_MMAP */
588 /* For the OSes that don't provide mmap call, try to load all the
589 * file into memory (alex@bcs.zaporizhzhe.ua). Also, mmap can fail
590 * for any reason, so we use this as fallback (pavel@ucw.cz) */
592 /* Make sure view->s.st_size is not truncated when passed to g_malloc */
593 if ((gulong) view->s.st_size == view->s.st_size)
594 view->data = (unsigned char *) g_malloc ((gulong) view->s.st_size);
595 else
596 view->data = NULL;
598 if (view->data == NULL || mc_lseek (view->file, 0, SEEK_SET) != 0
599 || mc_read (view->file, view->data,
600 view->s.st_size) != view->s.st_size) {
601 g_free (view->data);
602 close_view_file (view);
603 return init_growing_view (view, 0, view->filename);
605 view->first = 0;
606 view->bytes_read = view->s.st_size;
607 return NULL;
610 /* Return zero on success, -1 on failure */
611 static int
612 do_view_init (WView *view, char *_command, const char *_file,
613 int start_line)
615 char *error = 0;
616 int i, type;
617 int fd = -1;
618 char tmp[BUF_MEDIUM];
620 if (view->view_active)
621 view_done (view);
623 /* Set up the state */
624 view->block_ptr = 0;
625 view->data = NULL;
626 view->growing_buffer = 0;
627 view->reading_pipe = 0;
628 view->mmapping = 0;
629 view->blocks = 0;
630 view->block_ptr = 0;
631 view->first = view->bytes_read = 0;
632 view->last_byte = 0;
633 view->filename = g_strdup (_file);
634 view->localcopy = 0;
635 view->command = 0;
636 view->last = view->first + ((LINES - 2) * view->bytes_per_line);
638 /* Clear the markers */
639 view->marker = 0;
640 for (i = 0; i < 10; i++)
641 view->marks[i] = 0;
643 if (!view->have_frame) {
644 view->start_col = 0;
647 if (_command && (view->viewer_magic_flag || _file[0] == '\0')) {
648 error = init_growing_view (view, _command, view->filename);
649 } else if (_file[0]) {
650 int cntlflags;
652 /* Open the file */
653 if ((fd = mc_open (_file, O_RDONLY | O_NONBLOCK)) == -1) {
654 g_snprintf (tmp, sizeof (tmp), _(" Cannot open \"%s\"\n %s "),
655 _file, unix_error_string (errno));
656 error = set_view_init_error (view, tmp);
657 goto finish;
660 /* Make sure we are working with a regular file */
661 if (mc_fstat (fd, &view->s) == -1) {
662 mc_close (fd);
663 g_snprintf (tmp, sizeof (tmp), _(" Cannot stat \"%s\"\n %s "),
664 _file, unix_error_string (errno));
665 error = set_view_init_error (view, tmp);
666 goto finish;
669 if (!S_ISREG (view->s.st_mode)) {
670 mc_close (fd);
671 g_snprintf (tmp, sizeof (tmp),
672 _(" Cannot view: not a regular file "));
673 error = set_view_init_error (view, tmp);
674 goto finish;
677 /* We don't need O_NONBLOCK after opening the file, unset it */
678 cntlflags = fcntl (fd, F_GETFL, 0);
679 if (cntlflags != -1) {
680 cntlflags &= ~O_NONBLOCK;
681 fcntl (fd, F_SETFL, cntlflags);
684 type = get_compression_type (fd);
686 if (view->viewer_magic_flag && (type != COMPRESSION_NONE)) {
687 g_free (view->filename);
688 view->filename =
689 g_strconcat (_file, decompress_extension (type), NULL);
692 error = load_view_file (view, fd);
695 finish:
696 if (error) {
697 if (!view->have_frame) {
698 message (1, MSG_ERROR, "%s", error);
699 g_free (error);
700 return -1;
704 view->view_active = 1;
705 if (_command)
706 view->command = g_strdup (_command);
707 else
708 view->command = 0;
709 view->search_start = view->start_display = view->start_save =
710 view->first;
711 view->found_len = 0;
712 view->start_col = 0;
713 view->last_search = 0; /* Start a new search */
715 /* Special case: The data points to the error message */
716 if (error) {
717 view->data = error;
718 view->file = -1;
719 view->s.st_size = view->bytes_read = strlen (view->data);
721 view->last_byte = view->first + view->s.st_size;
723 if (start_line > 1 && !error) {
724 int saved_wrap_mode = view->wrap_mode;
726 view->wrap_mode = 0;
727 get_byte (view, 0);
728 view_move_forward (view, start_line - 1);
729 view->wrap_mode = saved_wrap_mode;
731 view->edit_cursor = view->first;
732 view->file_dirty = 0;
733 view->nib_shift = 0;
734 view->view_side = view_side_left;
735 view->change_list = NULL;
737 return 0;
740 void
741 view_update_bytes_per_line (WView *view)
743 int cols;
745 if (view->have_frame)
746 cols = view->widget.cols - 2;
747 else
748 cols = view->widget.cols;
750 view->bottom_first = -1;
751 if (cols < 80)
752 view->bytes_per_line = ((cols - 8) / 17) * 4;
753 else
754 view->bytes_per_line = ((cols - 8) / 18) * 4;
756 if (view->bytes_per_line == 0)
757 view->bytes_per_line++; /* To avoid division by 0 */
759 view->dirty = max_dirt_limit + 1; /* To force refresh */
762 /* Both views */
763 /* Return zero on success, -1 on failure */
765 view_init (WView *view, char *_command, const char *_file, int start_line)
767 if (!view->view_active || strcmp (_file, view->filename)
768 || altered_magic_flag)
769 return do_view_init (view, _command, _file, start_line);
770 else
771 return 0;
774 static void
775 view_percent (WView *view, int p, int w, gboolean update_gui)
777 int percent;
779 percent = (view->s.st_size == 0
780 || view->last_byte == view->last) ? 100 : (p >
781 (INT_MAX /
782 100) ? p /
783 (view->s.
784 st_size /
785 100) : p * 100 /
786 view->s.st_size);
788 #if 0
789 percent = view->s.st_size == 0 ? 100 :
790 (view->last_byte == view->last ? 100 :
791 (p) * 100 / view->s.st_size);
792 #endif
794 widget_move (view, view->have_frame, w - 5);
795 printw ("%3d%%", percent);
798 static void
799 view_status (WView *view, gboolean update_gui)
801 static int i18n_adjust = 0;
802 static char *file_label;
804 int w = view->widget.cols - (view->have_frame * 2);
805 int i;
807 attrset (SELECTED_COLOR);
808 widget_move (view, view->have_frame, view->have_frame);
809 hline (' ', w);
811 if (!i18n_adjust) {
812 file_label = _("File: %s");
813 i18n_adjust = strlen (file_label) - 2;
816 if (w < i18n_adjust + 6)
817 addstr (name_trunc (view->filename ? view->filename :
818 view->command ? view->command : "", w));
819 else {
820 i = (w > 22 ? 22 : w) - i18n_adjust;
821 printw (file_label, name_trunc (view->filename ? view->filename :
822 view->command ? view->command : "",
823 i));
824 if (w > 46) {
825 widget_move (view, view->have_frame, 24 + view->have_frame);
826 if (view->hex_mode)
827 printw (_("Offset 0x%08x"), view->edit_cursor);
828 else
829 printw (_("Col %d"), -view->start_col);
831 if (w > 62) {
832 widget_move (view, view->have_frame, 43 + view->have_frame);
833 printw (_("%s bytes"), size_trunc (view->s.st_size));
835 if (w > 70) {
836 printw (" ");
837 if (view->growing_buffer)
838 addstr (_(" [grow]"));
840 if (w > 26) {
841 view_percent (view,
842 view->hex_mode ? view->edit_cursor : view->
843 start_display,
844 view->widget.cols - view->have_frame + 1,
845 update_gui);
848 attrset (SELECTED_COLOR);
851 static inline void
852 view_display_clean (WView *view, int height, int width)
854 /* FIXME: Should I use widget_erase only and repaint the box? */
855 if (view->have_frame) {
856 int i;
858 draw_double_box (view->widget.parent, view->widget.y,
859 view->widget.x, view->widget.lines,
860 view->widget.cols);
861 for (i = 1; i < height; i++) {
862 widget_move (view, i, 1);
863 printw ("%*s", width - 1, "");
865 } else
866 widget_erase ((Widget *) view);
869 #define view_add_character(view,c) addch (c)
870 #define view_add_one_vline() one_vline()
871 #define view_add_string(view,s) addstr (s)
872 #define view_gotoyx(v,r,c) widget_move (v,r,c)
874 #define view_freeze(view)
875 #define view_thaw(view)
877 #define STATUS_LINES 1
879 typedef enum {
880 MARK_NORMAL = 0,
881 MARK_SELECTED = 1,
882 MARK_CURSOR = 2,
883 MARK_CHANGED = 3
884 } mark_t;
886 /* Shows the file pointed to by *start_display on view_win */
887 static long
888 display (WView *view)
890 const int frame_shift = view->have_frame;
891 int col = 0 + frame_shift;
892 int row = STATUS_LINES + frame_shift;
893 int height, width;
894 unsigned long from;
895 int c;
896 mark_t boldflag = MARK_NORMAL;
897 struct hexedit_change_node *curr = view->change_list;
899 height = view->widget.lines - frame_shift;
900 width = view->widget.cols - frame_shift;
901 from = view->start_display;
902 attrset (NORMAL_COLOR);
904 view_freeze (view);
905 view_display_clean (view, height, width);
907 /* Optionally, display a ruler */
908 if ((!view->hex_mode) && (ruler)) {
909 char r_buff[10];
910 int cl;
912 attrset (MARKED_COLOR);
913 for (c = frame_shift; c < width; c++) {
914 cl = c - view->start_col;
915 if (ruler == 1)
916 view_gotoyx (view, row, c);
917 else
918 view_gotoyx (view, row + height - 2, c);
919 r_buff[0] = '-';
920 if ((cl % 10) == 0)
921 r_buff[0] = '|';
922 else if ((cl % 5) == 0)
923 r_buff[0] = '*';
924 view_add_character (view, r_buff[0]);
925 if ((cl != 0) && (cl % 10) == 0) {
926 g_snprintf (r_buff, sizeof (r_buff), "%03d", cl);
927 if (ruler == 1) {
928 widget_move (view, row + 1, c - 1);
929 } else {
930 widget_move (view, row + height - 3, c - 1);
932 view_add_string (view, r_buff);
935 attrset (NORMAL_COLOR);
936 if (ruler == 1)
937 row += 2;
938 else
939 height -= 2;
942 /* Find the first displayable changed byte */
943 while (curr && (curr->offset < from)) {
944 curr = curr->next;
946 if (view->hex_mode) {
947 char hex_buff[10]; /* A temporary buffer for sprintf and mvwaddstr */
948 int bytes; /* Number of bytes already printed on the line */
950 /* Start of text column */
951 int text_start = width - view->bytes_per_line - 1 + frame_shift;
953 for (; row < height && from < view->last_byte; row++) {
954 /* Print the hex offset */
955 attrset (MARKED_COLOR);
956 g_snprintf (hex_buff, sizeof (hex_buff), "%08X",
957 (int) (from - view->first));
958 view_gotoyx (view, row, frame_shift);
959 view_add_string (view, hex_buff);
960 attrset (NORMAL_COLOR);
962 /* Hex dump starts from column nine */
963 if (view->have_frame)
964 col = 10;
965 else
966 col = 9;
968 /* Each hex number is two digits */
969 hex_buff[2] = 0;
970 for (bytes = 0;
971 bytes < view->bytes_per_line && from < view->last_byte;
972 bytes++, from++) {
973 /* Display and mark changed bytes */
974 if (curr && from == curr->offset) {
975 c = curr->value;
976 curr = curr->next;
977 boldflag = MARK_CHANGED;
978 attrset (VIEW_UNDERLINED_COLOR);
979 } else
980 c = (unsigned char) get_byte (view, from);
982 if (view->found_len && from >= view->search_start
983 && from < view->search_start + view->found_len) {
984 boldflag = MARK_SELECTED;
985 attrset (MARKED_COLOR);
987 /* Display the navigation cursor */
988 if (from == view->edit_cursor) {
989 if (view->view_side == view_side_left) {
990 view->cursor_row = row;
991 view->cursor_col = col;
993 boldflag = MARK_CURSOR;
994 attrset (view->view_side ==
995 view_side_left ? VIEW_UNDERLINED_COLOR :
996 MARKED_SELECTED_COLOR);
999 /* Print a hex number (sprintf is too slow) */
1000 hex_buff[0] = hex_char[(c >> 4)];
1001 hex_buff[1] = hex_char[c & 15];
1002 view_gotoyx (view, row, col);
1003 view_add_string (view, hex_buff);
1004 col += 3;
1005 /* Turn off the cursor or changed byte highlighting here */
1006 if (boldflag == MARK_CURSOR || boldflag == MARK_CHANGED)
1007 attrset (NORMAL_COLOR);
1008 if ((bytes & 3) == 3 && bytes + 1 < view->bytes_per_line) {
1009 /* Turn off the search highlighting */
1010 if (boldflag == MARK_SELECTED
1011 && from ==
1012 view->search_start + view->found_len - 1)
1013 attrset (NORMAL_COLOR);
1015 /* Hex numbers are printed in the groups of four */
1016 /* Groups are separated by a vline */
1018 view_gotoyx (view, row, col - 1);
1019 view_add_character (view, ' ');
1020 view_gotoyx (view, row, col);
1021 if ((view->have_frame && view->widget.cols < 82) ||
1022 view->widget.cols < 80)
1023 col += 1;
1024 else {
1025 view_add_one_vline ();
1026 col += 2;
1029 if (boldflag != MARK_NORMAL
1030 && from ==
1031 view->search_start + view->found_len - 1)
1032 attrset (MARKED_COLOR);
1035 if (boldflag != MARK_NORMAL
1036 && from < view->search_start + view->found_len - 1
1037 && bytes != view->bytes_per_line - 1) {
1038 view_gotoyx (view, row, col);
1039 view_add_character (view, ' ');
1042 /* Print the corresponding ascii character */
1043 view_gotoyx (view, row, text_start + bytes);
1045 c = convert_to_display_c (c);
1047 if (!is_printable (c))
1048 c = '.';
1049 switch (boldflag) {
1050 case MARK_NORMAL:
1051 break;
1052 case MARK_SELECTED:
1053 attrset (MARKED_COLOR);
1054 break;
1055 case MARK_CURSOR:
1056 if (view->view_side == view_side_right) {
1057 /* Our side is active */
1058 view->cursor_col = text_start + bytes;
1059 view->cursor_row = row;
1060 attrset (VIEW_UNDERLINED_COLOR);
1061 } else {
1062 /* Other side is active */
1063 attrset (MARKED_SELECTED_COLOR);
1065 break;
1066 case MARK_CHANGED:
1067 attrset (VIEW_UNDERLINED_COLOR);
1068 break;
1070 view_add_character (view, c);
1072 if (boldflag != MARK_NORMAL) {
1073 boldflag = MARK_NORMAL;
1074 attrset (NORMAL_COLOR);
1078 } else {
1079 if (view->growing_buffer && from == view->last_byte)
1080 get_byte (view, from);
1081 for (; row < height && from < view->last_byte; from++) {
1082 c = get_byte (view, from);
1083 if ((c == '\n') || (col >= width && view->wrap_mode)) {
1084 col = frame_shift;
1085 row++;
1086 if (c == '\n' || row >= height)
1087 continue;
1089 if (c == '\r')
1090 continue;
1091 if (c == '\t') {
1092 col = ((col - frame_shift) / 8) * 8 + 8 + frame_shift;
1093 continue;
1095 if (view->viewer_nroff_flag && c == '\b') {
1096 int c_prev;
1097 int c_next;
1099 if (from + 1 < view->last_byte
1100 && is_printable ((c_next = get_byte (view, from + 1)))
1101 && from > view->first
1102 && is_printable ((c_prev = get_byte (view, from - 1)))
1103 && (c_prev == c_next || c_prev == '_')) {
1104 if (col <= frame_shift) {
1105 /* So it has to be wrap_mode - do not need to check for it */
1106 if (row == 1 + frame_shift) {
1107 from++;
1108 continue; /* There had to be a bold character on the rightmost position
1109 of the previous undisplayed line */
1111 row--;
1112 col = width;
1114 col--;
1115 boldflag = MARK_SELECTED;
1116 if (c_prev == '_' && c_next != '_')
1117 attrset (VIEW_UNDERLINED_COLOR);
1118 else
1119 attrset (MARKED_COLOR);
1120 continue;
1123 if (view->found_len && from >= view->search_start
1124 && from < view->search_start + view->found_len) {
1125 boldflag = MARK_SELECTED;
1126 attrset (SELECTED_COLOR);
1128 if (col >= frame_shift - view->start_col
1129 && col < width - view->start_col) {
1130 view_gotoyx (view, row, col + view->start_col);
1132 c = convert_to_display_c (c);
1134 if (!is_printable (c))
1135 c = '.';
1137 view_add_character (view, c);
1139 col++;
1140 if (boldflag != MARK_NORMAL) {
1141 boldflag = MARK_NORMAL;
1142 attrset (NORMAL_COLOR);
1145 /* Very last thing */
1146 if (view->growing_buffer && from + 1 == view->last_byte)
1147 get_byte (view, from + 1);
1150 view->last = from;
1151 view_thaw (view);
1152 return from;
1155 static void
1156 view_place_cursor (WView *view)
1158 int shift;
1160 if (view->view_side == view_side_left)
1161 shift = view->nib_shift;
1162 else
1163 shift = 0;
1165 widget_move (&view->widget, view->cursor_row,
1166 view->cursor_col + shift);
1169 static void
1170 view_update (WView *view, gboolean update_gui)
1172 static int dirt_limit = 1;
1174 if (view->dirty > dirt_limit) {
1175 /* Too many updates skipped -> force a update */
1176 display (view);
1177 view_status (view, update_gui);
1178 view->dirty = 0;
1179 /* Raise the update skipping limit */
1180 dirt_limit++;
1181 if (dirt_limit > max_dirt_limit)
1182 dirt_limit = max_dirt_limit;
1184 if (view->dirty) {
1185 if (is_idle ()) {
1186 /* We have time to update the screen properly */
1187 display (view);
1188 view_status (view, update_gui);
1189 view->dirty = 0;
1190 if (dirt_limit > 1)
1191 dirt_limit--;
1192 } else {
1193 /* We are busy -> skipping full update,
1194 only the status line is updated */
1195 view_status (view, update_gui);
1197 /* Here we had a refresh, if fast scrolling does not work
1198 restore the refresh, although this should not happen */
1202 static inline void
1203 my_define (Dlg_head *h, int idx, char *text, void (*fn) (WView *),
1204 WView *view)
1206 define_label_data (h, idx, text, (buttonbarfn) fn, view);
1209 /* If the last parameter is nonzero, it means we want get the count of lines
1210 from current up to the the upto position inclusive */
1211 static long
1212 move_forward2 (WView *view, long current, int lines, long upto)
1214 unsigned long q, p;
1215 int line;
1216 int col = 0;
1218 if (view->hex_mode) {
1219 p = current + lines * view->bytes_per_line;
1220 p = (p >= view->last_byte) ? current : p;
1221 if (lines == 1) {
1222 q = view->edit_cursor + view->bytes_per_line;
1223 line = q / view->bytes_per_line;
1224 col = (view->last_byte - 1) / view->bytes_per_line;
1225 view->edit_cursor = (line > col) ? view->edit_cursor : q;
1226 view->edit_cursor = (view->edit_cursor < view->last_byte) ?
1227 view->edit_cursor : view->last_byte - 1;
1228 q = current + ((LINES - 2) * view->bytes_per_line);
1229 p = (view->edit_cursor < q) ? current : p;
1230 } else {
1231 view->edit_cursor = (view->edit_cursor < p) ?
1232 p : view->edit_cursor;
1234 return p;
1235 } else {
1236 if (upto) {
1237 lines = -1;
1238 q = upto;
1239 } else
1240 q = view->last_byte;
1241 if (get_byte (view, q) != '\n')
1242 q++;
1243 for (line = col = 0, p = current; p < q; p++) {
1244 int c;
1246 if (lines != -1 && line >= lines)
1247 return p;
1249 c = get_byte (view, p);
1251 if (view->wrap_mode) {
1252 if (c == '\r')
1253 continue; /* This characters is never displayed */
1254 else if (c == '\t')
1255 col =
1256 ((col - view->have_frame) / 8) * 8 + 8 +
1257 view->have_frame;
1258 else
1259 col++;
1260 if (view->viewer_nroff_flag && c == '\b') {
1261 if (p + 1 < view->last_byte
1262 && is_printable (get_byte (view, p + 1))
1263 && p > view->first
1264 && is_printable (get_byte (view, p - 1)))
1265 col -= 2;
1266 } else if (col == vwidth) {
1267 /* FIXME: the c in is_printable was a p, that is a bug,
1268 I suspect I got that fix from Jakub, same applies
1269 for d. */
1270 int d = get_byte (view, p + 2);
1272 if (p + 2 >= view->last_byte || !is_printable (c) ||
1273 !view->viewer_nroff_flag
1274 || get_byte (view, p + 1) != '\b'
1275 || !is_printable (d)) {
1276 col = 0;
1278 if (c == '\n' || get_byte (view, p + 1) != '\n')
1279 line++;
1281 } else if (c == '\n') {
1282 line++;
1283 col = 0;
1285 } else if (c == '\n')
1286 line++;
1288 if (upto)
1289 return line;
1291 return current;
1294 /* returns the new current pointer */
1295 /* Cause even the forward routine became very complex, we in the wrap_mode
1296 just find the nearest '\n', use move_forward2(p, 0, q) to get the count
1297 of lines up to there and then use move_forward2(p, something, 0), which we
1298 return */
1299 static long
1300 move_backward2 (WView *view, unsigned long current, int lines)
1302 long p, q, pm;
1303 int line;
1305 if (!view->hex_mode && current == view->first)
1306 return current;
1308 if (view->hex_mode) {
1309 p = current - lines * view->bytes_per_line;
1310 p = (p < view->first) ? view->first : p;
1311 if (lines == 1) {
1312 q = view->edit_cursor - view->bytes_per_line;
1313 view->edit_cursor = (q < view->first) ? view->edit_cursor : q;
1314 p = (view->edit_cursor >= current) ? current : p;
1315 } else {
1316 q = p + ((LINES - 2) * view->bytes_per_line);
1317 view->edit_cursor = (view->edit_cursor >= q) ?
1318 p : view->edit_cursor;
1320 return p;
1321 } else {
1322 if (current == view->last_byte
1323 && get_byte (view, current - 1) != '\n')
1324 /* There is one virtual '\n' at the end,
1325 so that the last line is shown */
1326 line = 1;
1327 else
1328 line = 0;
1329 for (q = p = current - 1; p >= view->first; p--)
1330 if (get_byte (view, p) == '\n' || p == view->first) {
1331 pm = p > view->first ? p + 1 : view->first;
1332 if (!view->wrap_mode) {
1333 if (line == lines)
1334 return pm;
1335 line++;
1336 } else {
1337 line += move_forward2 (view, pm, 0, q);
1338 if (line >= lines) {
1339 if (line == lines)
1340 return pm;
1341 else
1342 return move_forward2 (view, pm, line - lines,
1345 q = p + 1;
1349 return p > view->first ? p : view->first;
1352 static void
1353 view_move_backward (WView *view, int i)
1355 view->search_start = view->start_display =
1356 move_backward2 (view, view->start_display, i);
1357 view->found_len = 0;
1358 view->last = view->first + ((LINES - 2) * view->bytes_per_line);
1359 view->dirty++;
1362 static long
1363 get_bottom_first (WView *view, int do_not_cache, int really)
1365 int bottom_first;
1367 if (!have_fast_cpu && !really)
1368 return INT_MAX;
1370 if (!do_not_cache && view->bottom_first != -1)
1371 return view->bottom_first;
1373 /* Force loading */
1374 if (view->growing_buffer) {
1375 int old_last_byte;
1377 old_last_byte = -1;
1378 while (old_last_byte != view->last_byte) {
1379 old_last_byte = view->last_byte;
1380 get_byte (view, view->last_byte + VIEW_PAGE_SIZE);
1384 bottom_first = move_backward2 (view, view->last_byte, vheight - 1);
1386 if (view->hex_mode)
1387 bottom_first = (bottom_first + view->bytes_per_line - 1)
1388 / view->bytes_per_line * view->bytes_per_line;
1389 view->bottom_first = bottom_first;
1391 return view->bottom_first;
1394 static void
1395 view_move_forward (WView *view, int i)
1397 view->start_display = move_forward2 (view, view->start_display, i, 0);
1398 if (!view->reading_pipe
1399 && view->start_display > get_bottom_first (view, 0, 0))
1400 view->start_display = view->bottom_first;
1401 view->search_start = view->start_display;
1402 view->found_len = 0;
1403 view->last = view->first + ((LINES - 2) * view->bytes_per_line);
1404 view->dirty++;
1408 static void
1409 move_to_top (WView *view)
1411 view->search_start = view->start_display = view->first;
1412 view->found_len = 0;
1413 view->last = view->first + ((LINES - 2) * view->bytes_per_line);
1414 view->nib_shift = 0;
1415 view->edit_cursor = view->start_display;
1416 view->dirty++;
1419 static void
1420 move_to_bottom (WView *view)
1422 view->search_start = view->start_display =
1423 get_bottom_first (view, 0, 1);
1424 view->found_len = 0;
1425 view->last = view->first + ((LINES - 2) * view->bytes_per_line);
1426 view->edit_cursor = (view->edit_cursor < view->start_display) ?
1427 view->start_display : view->edit_cursor;
1428 view->dirty++;
1431 /* Scroll left/right the view panel functions */
1432 static void
1433 move_right (WView *view)
1435 if (view->wrap_mode && !view->hex_mode)
1436 return;
1437 if (view->hex_mode) {
1438 view->last = view->first + ((LINES - 2) * view->bytes_per_line);
1440 if (view->hex_mode && view->view_side == view_side_left) {
1441 view->nib_shift = 1 - view->nib_shift;
1442 if (view->nib_shift == 1)
1443 return;
1445 view->edit_cursor = (++view->edit_cursor < view->last_byte) ?
1446 view->edit_cursor : view->last_byte - 1;
1447 if (view->edit_cursor >= view->last) {
1448 view->edit_cursor -= view->bytes_per_line;
1449 view_move_forward (view, 1);
1451 } else if (--view->start_col > 0)
1452 view->start_col = 0;
1453 view->dirty++;
1456 static void
1457 move_left (WView *view)
1459 if (view->wrap_mode && !view->hex_mode)
1460 return;
1461 if (view->hex_mode) {
1462 if (view->hex_mode && view->view_side == view_side_left) {
1463 view->nib_shift = 1 - view->nib_shift;
1464 if (view->nib_shift == 0)
1465 return;
1467 if (view->edit_cursor > view->first)
1468 --view->edit_cursor;
1469 if (view->edit_cursor < view->start_display) {
1470 view->edit_cursor += view->bytes_per_line;
1471 view_move_backward (view, 1);
1473 } else if (++view->start_col > 0)
1474 view->start_col = 0;
1475 view->dirty++;
1478 /* Case insensitive search of text in data */
1479 static int
1480 icase_search_p (WView *view, char *text, char *data, int nothing)
1482 char *q;
1483 int lng;
1484 int direction = view->direction;
1486 /* If we are searching backwards, reverse the string */
1487 if (direction == -1) {
1488 g_strreverse (text);
1489 g_strreverse (data);
1492 q = _icase_search (text, data, &lng);
1494 if (direction == -1) {
1495 g_strreverse (text);
1496 g_strreverse (data);
1499 if (q != 0) {
1500 if (direction > 0)
1501 view->search_start = q - data - lng;
1502 else
1503 view->search_start = strlen (data) - (q - data);
1504 view->found_len = lng;
1505 return 1;
1507 return 0;
1510 static char *
1511 grow_string_buffer (char *text, int *size)
1513 char *new;
1515 /* The grow steps */
1516 *size += 160;
1517 new = g_realloc (text, *size);
1518 if (!text) {
1519 *new = 0;
1521 return new;
1524 static char *
1525 get_line_at (WView *view, unsigned long *p, unsigned long *skipped)
1527 char *buffer = 0;
1528 int buffer_size = 0;
1529 int usable_size = 0;
1530 int ch;
1531 int direction = view->direction;
1532 unsigned long pos = *p;
1533 long i = 0;
1534 int prev = 0;
1536 if (!pos && direction == -1)
1537 return 0;
1539 /* skip over all the possible zeros in the file */
1540 while ((ch = get_byte (view, pos)) == 0) {
1541 if (!pos && direction == -1)
1542 return 0;
1543 pos += direction;
1544 i++;
1546 *skipped = i;
1548 if (!i && (pos || direction == -1)) {
1549 prev = get_byte (view, pos - direction);
1550 if ((prev == -1) || (prev == '\n'))
1551 prev = 0;
1554 for (i = 1; ch != -1; ch = get_byte (view, pos)) {
1556 if (i >= usable_size) {
1557 buffer = grow_string_buffer (buffer, &buffer_size);
1558 usable_size = buffer_size - 2; /* prev & null terminator */
1561 buffer[i++] = ch;
1563 if (!pos && direction == -1)
1564 break;
1566 pos += direction;
1568 if (ch == '\n' || !ch) {
1569 i--; /* Strip newline/zero */
1570 break;
1574 if (buffer) {
1575 buffer[0] = prev;
1576 buffer[i] = 0;
1578 /* If we are searching backwards, reverse the string */
1579 if (direction < 0) {
1580 g_strreverse (buffer + 1);
1584 *p = pos;
1585 return buffer;
1588 /** Search status optmizations **/
1590 /* The number of bytes between percent increments */
1591 static int update_steps;
1593 /* Last point where we updated the status */
1594 static long update_activate;
1596 static void
1597 search_update_steps (WView *view)
1599 if (view->s.st_size)
1600 update_steps = 40000;
1601 else
1602 update_steps = view->last_byte / 100;
1604 /* Do not update the percent display but every 20 ks */
1605 if (update_steps < 20000)
1606 update_steps = 20000;
1609 static void
1610 search (WView *view, char *text,
1611 int (*search) (WView *, char *, char *, int))
1613 int w = view->widget.cols - view->have_frame + 1;
1615 char *s = NULL; /* The line we read from the view buffer */
1616 long p, beginning;
1617 int found_len, search_start;
1618 int search_status;
1619 Dlg_head *d = 0;
1621 /* Used to keep track of where the line starts, when looking forward */
1622 /* is the index before transfering the line; the reverse case uses */
1623 /* the position returned after the line has been read */
1624 long forward_line_start;
1625 long reverse_line_start;
1626 long t;
1627 /* Clear interrupt status */
1628 got_interrupt ();
1630 if (verbose) {
1631 d = message (D_INSERT, _("Search"), _("Searching %s"), text);
1632 mc_refresh ();
1635 found_len = view->found_len;
1636 search_start = view->search_start;
1638 if (view->direction == 1) {
1639 p = found_len ? search_start + 1 : search_start;
1640 } else {
1641 p = (found_len && search_start) ? search_start - 1 : search_start;
1643 beginning = p;
1645 /* Compute the percent steps */
1646 search_update_steps (view);
1647 update_activate = 0;
1649 for (;; g_free (s)) {
1650 if (p >= update_activate) {
1651 update_activate += update_steps;
1652 if (verbose) {
1653 view_percent (view, p, w, TRUE);
1654 mc_refresh ();
1656 if (got_interrupt ())
1657 break;
1659 forward_line_start = p;
1660 disable_interrupt_key ();
1661 s = get_line_at (view, &p, &t);
1662 reverse_line_start = p;
1663 enable_interrupt_key ();
1665 if (!s)
1666 break;
1668 search_status = (*search) (view, text, s + 1, match_normal);
1669 if (search_status < 0) {
1670 g_free (s);
1671 break;
1674 if (search_status == 0)
1675 continue;
1677 /* We found the string */
1679 /* Handle ^ and $ when regexp search starts at the middle of the line */
1680 if (*s && !view->search_start && (search == regexp_view_search)) {
1681 if ((*text == '^' && view->direction == 1)
1682 || (view->direction == -1 && text[strlen (text) - 1] == '$')
1684 continue;
1687 /* Record the position used to continue the search */
1688 if (view->direction == 1)
1689 t += forward_line_start;
1690 else
1691 t = reverse_line_start ? reverse_line_start + 3 : 0;
1692 view->search_start += t;
1694 if (t != beginning) {
1695 if (t > get_bottom_first (view, 0, 0))
1696 view->start_display = view->bottom_first;
1697 else
1698 view->start_display = t;
1701 g_free (s);
1702 break;
1704 disable_interrupt_key ();
1705 if (verbose) {
1706 dlg_run_done (d);
1707 destroy_dlg (d);
1709 if (!s) {
1710 message (0, _("Search"), _(" Search string not found "));
1711 view->found_len = 0;
1715 /* Search buffer (it's size is len) in the complete buffer */
1716 /* returns the position where the block was found or -1 if not found */
1717 static long
1718 block_search (WView *view, char *buffer, int len)
1720 int w = view->widget.cols - view->have_frame + 1;
1721 int direction = view->direction;
1722 char *d = buffer, b;
1723 unsigned long e;
1725 /* clear interrupt status */
1726 got_interrupt ();
1727 enable_interrupt_key ();
1728 if (direction == 1)
1729 e = view->found_len ? view->search_start + 1 : view->search_start;
1730 else
1731 e = (view->found_len
1732 && view->search_start) ? view->search_start - 1
1733 : view->search_start;
1735 search_update_steps (view);
1736 update_activate = 0;
1738 if (direction == -1) {
1739 for (d += len - 1;; e--) {
1740 if (e <= update_activate) {
1741 update_activate -= update_steps;
1742 if (verbose) {
1743 view_percent (view, e, w, TRUE);
1744 mc_refresh ();
1746 if (got_interrupt ())
1747 break;
1749 b = get_byte (view, e);
1751 if (*d == b) {
1752 if (d == buffer) {
1753 disable_interrupt_key ();
1754 return e;
1756 d--;
1757 } else {
1758 e += buffer + len - 1 - d;
1759 d = buffer + len - 1;
1761 if (e == 0)
1762 break;
1764 } else {
1765 while (e < view->last_byte) {
1766 if (e >= update_activate) {
1767 update_activate += 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 d++;
1779 if (d - buffer == len) {
1780 disable_interrupt_key ();
1781 return e - len;
1783 } else {
1784 e -= d - buffer;
1785 d = buffer;
1789 disable_interrupt_key ();
1790 return -1;
1794 * Search in the hex mode. Supported input:
1795 * - numbers (oct, dec, hex). Each of them matches one byte.
1796 * - strings in double quotes. Matches exactly without quotes.
1798 static void
1799 hex_search (WView *view, char *text)
1801 char *buffer; /* Parsed search string */
1802 char *cur; /* Current position in it */
1803 int block_len; /* Length of the search string */
1804 long pos; /* Position of the string in the file */
1805 int parse_error = 0;
1807 if (!*text) {
1808 view->found_len = 0;
1809 return;
1812 /* buffer will never be longer that text */
1813 buffer = g_new (char, strlen (text));
1814 cur = buffer;
1816 /* First convert the string to a stream of bytes */
1817 while (*text) {
1818 int val;
1819 int ptr;
1821 /* Skip leading spaces */
1822 if (*text == ' ' || *text == '\t') {
1823 text++;
1824 continue;
1827 /* %i matches octal, decimal, and hexadecimal numbers */
1828 if (sscanf (text, "%i%n", &val, &ptr) > 0) {
1829 /* Allow signed and unsigned char in the user input */
1830 if (val < -128 || val > 255) {
1831 parse_error = 1;
1832 break;
1835 *cur++ = (char) val;
1836 text += ptr;
1837 continue;
1840 /* Try quoted string, strip quotes */
1841 if (*text == '"') {
1842 char *next_quote;
1844 text++;
1845 next_quote = strchr (text, '"');
1846 if (next_quote) {
1847 memcpy (cur, text, next_quote - text);
1848 cur += next_quote - text;
1849 text = next_quote + 1;
1850 continue;
1852 /* fall through */
1855 parse_error = 1;
1856 break;
1859 block_len = cur - buffer;
1861 /* No valid bytes in the user input */
1862 if (block_len <= 0 || parse_error) {
1863 message (0, _("Search"), _("Invalid hex search expression"));
1864 g_free (buffer);
1865 view->found_len = 0;
1866 return;
1869 /* Then start the search */
1870 pos = block_search (view, buffer, block_len);
1872 g_free (buffer);
1874 if (pos == -1) {
1875 message (0, _("Search"), _(" Search string not found "));
1876 view->found_len = 0;
1877 return;
1880 view->search_start = pos;
1881 view->found_len = block_len;
1882 /* Set the edit cursor to the search position, left nibble */
1883 view->edit_cursor = view->search_start;
1884 view->nib_shift = 0;
1886 /* Adjust the file offset */
1887 view->start_display = (pos & (~(view->bytes_per_line - 1)));
1888 if (view->start_display > get_bottom_first (view, 0, 0))
1889 view->start_display = view->bottom_first;
1892 static int
1893 regexp_view_search (WView *view, char *pattern, char *string,
1894 int match_type)
1896 static regex_t r;
1897 static char *old_pattern = NULL;
1898 static int old_type;
1899 regmatch_t pmatch[1];
1900 int i, flags = REG_ICASE;
1902 if (!old_pattern || strcmp (old_pattern, pattern)
1903 || old_type != match_type) {
1904 if (old_pattern) {
1905 regfree (&r);
1906 g_free (old_pattern);
1907 old_pattern = 0;
1909 for (i = 0; pattern[i] != 0; i++) {
1910 if (isupper ((unsigned char) pattern[i])) {
1911 flags = 0;
1912 break;
1915 flags |= REG_EXTENDED;
1916 if (regcomp (&r, pattern, flags)) {
1917 message (1, MSG_ERROR, _(" Invalid regular expression "));
1918 return -1;
1920 old_pattern = g_strdup (pattern);
1921 old_type = match_type;
1923 if (regexec (&r, string, 1, pmatch, 0) != 0)
1924 return 0;
1925 view->found_len = pmatch[0].rm_eo - pmatch[0].rm_so;
1926 view->search_start = pmatch[0].rm_so;
1927 return 1;
1930 static void
1931 do_regexp_search (void *xview, char *regexp)
1933 WView *view = (WView *) xview;
1935 view->search_exp = regexp;
1936 search (view, regexp, regexp_view_search);
1937 /* Had a refresh here */
1938 view->dirty++;
1939 view_update (view, TRUE);
1942 static void
1943 do_normal_search (void *xview, char *text)
1945 WView *view = (WView *) xview;
1947 view->search_exp = text;
1948 if (view->hex_mode)
1949 hex_search (view, text);
1950 else
1951 search (view, text, icase_search_p);
1952 /* Had a refresh here */
1953 view->dirty++;
1954 view_update (view, TRUE);
1957 /* Real view only */
1958 static void
1959 view_help_cmd (void)
1961 interactive_display (NULL, "[Internal File Viewer]");
1963 view_refresh (0);
1967 /* Both views */
1968 static void
1969 toggle_wrap_mode (WView *view)
1971 if (view->hex_mode) {
1972 if (view->growing_buffer != 0) {
1973 return;
1975 get_bottom_first (view, 1, 1);
1976 if (view->hexedit_mode) {
1977 view->view_side = 1 - view->view_side;
1978 } else {
1979 view->hexedit_mode = 1 - view->hexedit_mode;
1981 view_labels (view);
1982 view->dirty++;
1983 view_update (view, TRUE);
1984 return;
1986 view->wrap_mode = 1 - view->wrap_mode;
1987 get_bottom_first (view, 1, 1);
1988 if (view->wrap_mode)
1989 view->start_col = 0;
1990 else {
1991 if (have_fast_cpu) {
1992 if (view->bottom_first < view->start_display)
1993 view->search_start = view->start_display =
1994 view->bottom_first;
1995 view->found_len = 0;
1998 view_labels (view);
1999 view->dirty++;
2000 view_update (view, TRUE);
2003 /* Both views */
2004 static void
2005 toggle_hex_mode (WView *view)
2007 view->hex_mode = 1 - view->hex_mode;
2009 if (view->hex_mode) {
2010 /* Shift the line start to 0x____0 on entry, restore it for Ascii */
2011 view->start_save = view->start_display;
2012 view->start_display -= view->start_display % view->bytes_per_line;
2013 view->edit_cursor = view->start_display;
2014 view->widget.options |= W_WANT_CURSOR;
2015 view->widget.parent->flags |= DLG_WANT_TAB;
2016 } else {
2017 view->start_display = view->start_save;
2018 view->widget.parent->flags &= ~DLG_WANT_TAB;
2019 view->widget.options &= ~W_WANT_CURSOR;
2021 altered_hex_mode = 1;
2022 get_bottom_first (view, 1, 1);
2023 view_labels (view);
2024 view->dirty++;
2025 view_update (view, TRUE);
2028 /* Ascii view */
2029 static void
2030 goto_line (WView *view)
2032 char *line, prompt[BUF_SMALL];
2033 int oldline = 1;
2034 int saved_wrap_mode = view->wrap_mode;
2035 unsigned long i;
2037 view->wrap_mode = 0;
2038 for (i = view->first; i < view->start_display; i++)
2039 if (get_byte (view, i) == '\n')
2040 oldline++;
2041 g_snprintf (prompt, sizeof (prompt),
2042 _(" The current line number is %d.\n"
2043 " Enter the new line number:"), oldline);
2044 line = input_dialog (_(" Goto line "), prompt, "");
2045 if (line) {
2046 if (*line) {
2047 move_to_top (view);
2048 view_move_forward (view, atol (line) - 1);
2050 g_free (line);
2052 view->dirty++;
2053 view->wrap_mode = saved_wrap_mode;
2054 view_update (view, TRUE);
2057 /* Hex view */
2058 static void
2059 goto_addr (WView *view)
2061 char *line, *error, prompt[BUF_SMALL];
2062 unsigned long addr;
2064 g_snprintf (prompt, sizeof (prompt),
2065 _(" The current address is 0x%lx.\n"
2066 " Enter the new address:"), view->edit_cursor);
2067 line = input_dialog (_(" Goto Address "), prompt, "");
2068 if (line) {
2069 if (*line) {
2070 addr = strtol (line, &error, 0);
2071 if ((*error == '\0') && (addr <= view->last_byte)) {
2072 move_to_top (view);
2073 view_move_forward (view, addr / view->bytes_per_line);
2074 view->edit_cursor = addr;
2077 g_free (line);
2079 view->dirty++;
2080 view_update (view, TRUE);
2083 /* Both views */
2084 static void
2085 regexp_search (WView *view, int direction)
2087 char *regexp = "";
2088 static char *old = 0;
2090 /* This is really an F6 key handler */
2091 if (view->hex_mode) {
2092 /* Save it without a confirmation prompt */
2093 if (view->change_list)
2094 save_edit_changes (view);
2095 return;
2098 regexp = old ? old : regexp;
2099 regexp = input_dialog (_("Search"), _(" Enter regexp:"), regexp);
2100 if ((!regexp)) {
2101 return;
2103 if ((!*regexp)) {
2104 g_free (regexp);
2105 return;
2107 if (old)
2108 g_free (old);
2109 old = regexp;
2110 #if 0
2111 /* Mhm, do we really need to load all the file in the core? */
2112 if (view->bytes_read < view->last_byte)
2113 get_byte (view, view->last_byte - 1); /* Get the whole file in to memory */
2114 #endif
2115 view->direction = direction;
2116 do_regexp_search (view, regexp);
2118 view->last_search = do_regexp_search;
2121 static void
2122 regexp_search_cmd (WView *view)
2124 regexp_search (view, 1);
2127 /* Both views */
2128 static void
2129 normal_search (WView *view, int direction)
2131 static char *old;
2132 char *exp = old ? old : "";
2134 enum {
2135 SEARCH_DLG_HEIGHT = 8,
2136 SEARCH_DLG_WIDTH = 58
2139 static int replace_backwards;
2140 int treplace_backwards = replace_backwards;
2142 static QuickWidget quick_widgets[] = {
2143 {quick_button, 6, 10, 5, SEARCH_DLG_HEIGHT, N_("&Cancel"), 0,
2144 B_CANCEL,
2145 0, 0, NULL},
2146 {quick_button, 2, 10, 5, SEARCH_DLG_HEIGHT, N_("&OK"), 0, B_ENTER,
2147 0, 0, NULL},
2148 {quick_checkbox, 3, SEARCH_DLG_WIDTH, 4, SEARCH_DLG_HEIGHT,
2149 N_("&Backwards"), 0, 0,
2150 0, 0, NULL},
2151 {quick_input, 3, SEARCH_DLG_WIDTH, 3, SEARCH_DLG_HEIGHT, "", 52, 0,
2152 0, 0, N_("Search")},
2153 {quick_label, 2, SEARCH_DLG_WIDTH, 2, SEARCH_DLG_HEIGHT,
2154 N_(" Enter search string:"), 0, 0,
2155 0, 0, 0},
2158 static QuickDialog Quick_input = {
2159 SEARCH_DLG_WIDTH, SEARCH_DLG_HEIGHT, -1, 0, N_("Search"),
2160 "[Input Line Keys]", quick_widgets, 0
2162 convert_to_display (old);
2164 quick_widgets[2].result = &treplace_backwards;
2165 quick_widgets[3].str_result = &exp;
2166 quick_widgets[3].text = exp;
2168 if (quick_dialog (&Quick_input) == B_CANCEL) {
2169 convert_from_input (old);
2170 return;
2172 replace_backwards = treplace_backwards;
2174 convert_from_input (old);
2176 if ((!exp)) {
2177 return;
2179 if ((!*exp)) {
2180 g_free (exp);
2181 return;
2184 if (old)
2185 g_free (old);
2186 old = exp;
2188 convert_from_input (exp);
2190 view->direction = replace_backwards ? -1 : 1;
2191 do_normal_search (view, exp);
2192 view->last_search = do_normal_search;
2195 static void
2196 normal_search_cmd (WView *view)
2198 normal_search (view, 1);
2201 static void
2202 change_viewer (WView *view)
2204 char *s;
2205 char *t;
2208 if (*view->filename) {
2209 altered_magic_flag = 1;
2210 view->viewer_magic_flag = !view->viewer_magic_flag;
2211 s = g_strdup (view->filename);
2212 if (view->command)
2213 t = g_strdup (view->command);
2214 else
2215 t = 0;
2217 view_done (view);
2218 view_init (view, t, s, 0);
2219 g_free (s);
2220 if (t)
2221 g_free (t);
2222 view_labels (view);
2223 view->dirty++;
2224 view_update (view, TRUE);
2228 static void
2229 change_nroff (WView *view)
2231 view->viewer_nroff_flag = !view->viewer_nroff_flag;
2232 altered_nroff_flag = 1;
2233 view_labels (view);
2234 view->dirty++;
2235 view_update (view, TRUE);
2238 /* Real view only */
2239 static void
2240 view_quit_cmd (WView *view)
2242 if (view_ok_to_quit (view))
2243 dlg_stop (view->widget.parent);
2246 /* Both views */
2247 static void
2248 view_labels (WView *view)
2250 Dlg_head *h = view->widget.parent;
2252 define_label (h, 1, _("Help"), view_help_cmd);
2254 my_define (h, 10, _("Quit"), view_quit_cmd, view);
2255 my_define (h, 4, view->hex_mode ? _("Ascii") : _("Hex"),
2256 toggle_hex_mode, view);
2257 my_define (h, 5, view->hex_mode ? _("Goto") : _("Line"),
2258 view->hex_mode ? goto_addr : goto_line, view);
2259 my_define (h, 6, view->hex_mode ? _("Save") : _("RxSrch"),
2260 regexp_search_cmd, view);
2262 my_define (h, 2, view->hex_mode ? view->hexedit_mode ?
2263 view->view_side ==
2264 view_side_left ? _("EdText") : _("EdHex") : view->
2265 growing_buffer ? "" : _("Edit") : view->
2266 wrap_mode ? _("UnWrap") : _("Wrap"), toggle_wrap_mode,
2267 view);
2269 my_define (h, 7, view->hex_mode ? _("HxSrch") : _("Search"),
2270 normal_search_cmd, view);
2272 my_define (h, 8, view->viewer_magic_flag ? _("Raw") : _("Parse"),
2273 change_viewer, view);
2275 if (!view->have_frame) {
2276 my_define (h, 9,
2277 view->viewer_nroff_flag ? _("Unform") : _("Format"),
2278 change_nroff, view);
2279 my_define (h, 3, _("Quit"), view_quit_cmd, view);
2282 redraw_labels (h);
2285 /* Both views */
2286 static int
2287 check_left_right_keys (WView *view, int c)
2289 if (c == KEY_LEFT)
2290 move_left (view);
2291 else if (c == KEY_RIGHT)
2292 move_right (view);
2293 else
2294 return 0;
2296 return 1;
2299 static void
2300 set_monitor (WView *view, int set_on)
2302 int old = view->monitor;
2304 view->monitor = set_on;
2306 if (view->monitor) {
2307 move_to_bottom (view);
2308 view->bottom_first = -1;
2309 set_idle_proc (view->widget.parent, 1);
2310 } else {
2311 if (old)
2312 set_idle_proc (view->widget.parent, 0);
2316 static void
2317 continue_search (WView *view)
2319 if (view->last_search) {
2320 (*view->last_search) (view, view->search_exp);
2321 } else {
2322 /* if not... then ask for an expression */
2323 normal_search (view, 1);
2327 /* Both views */
2328 static int
2329 view_handle_key (WView *view, int c)
2331 int prev_monitor = view->monitor;
2333 set_monitor (view, off);
2335 c = convert_from_input_c (c);
2337 if (view->hex_mode) {
2338 switch (c) {
2339 case 0x09: /* Tab key */
2340 view->view_side = 1 - view->view_side;
2341 view->dirty++;
2342 return 1;
2344 case XCTRL ('a'): /* Beginning of line */
2345 view->edit_cursor -= view->edit_cursor % view->bytes_per_line;
2346 view->dirty++;
2347 return 1;
2349 case XCTRL ('b'): /* Character back */
2350 move_left (view);
2351 return 1;
2353 case XCTRL ('e'): /* End of line */
2354 view->edit_cursor -= view->edit_cursor % view->bytes_per_line;
2355 view->edit_cursor += view->bytes_per_line - 1;
2356 view->dirty++;
2357 return 1;
2359 case XCTRL ('f'): /* Character forward */
2360 move_right (view);
2361 return 1;
2364 /* Trap 0-9,A-F,a-f for left side data entry (hex editing) */
2365 if (view->view_side == view_side_left) {
2366 if ((c >= '0' && c <= '9') ||
2367 (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f')) {
2369 put_editkey (view, c);
2370 return 1;
2374 /* Trap all printable characters for right side data entry */
2375 /* Also enter the value of the Enter key */
2376 if (view->view_side == view_side_right) {
2377 if (c < 256 && (is_printable (c) || (c == '\n'))) {
2378 put_editkey (view, c);
2379 return 1;
2384 if (check_left_right_keys (view, c))
2385 return 1;
2387 if (check_movement_keys
2388 (c, 1, vheight, view, (movefn) view_move_backward,
2389 (movefn) view_move_forward, (movefn) move_to_top,
2390 (movefn) move_to_bottom)) {
2391 return 1;
2393 switch (c) {
2395 case '?':
2396 regexp_search (view, -1);
2397 return 1;
2399 case '/':
2400 regexp_search (view, 1);
2401 return 1;
2403 /* Continue search */
2404 case XCTRL ('s'):
2405 case 'n':
2406 case KEY_F (17):
2407 continue_search (view);
2408 return 1;
2410 case XCTRL ('r'):
2411 if (view->last_search) {
2412 (*view->last_search) (view, view->search_exp);
2413 } else {
2414 normal_search (view, -1);
2416 return 1;
2418 /* toggle ruler */
2419 case ALT ('r'):
2420 switch (ruler) {
2421 case 0:
2422 ruler = 1;
2423 break;
2424 case 1:
2425 ruler = 2;
2426 break;
2427 default:
2428 ruler = 0;
2429 break;
2431 view->dirty++;
2432 return 1;
2434 case 'h':
2435 move_left (view);
2436 return 1;
2438 case 'j':
2439 case '\n':
2440 case 'e':
2441 view_move_forward (view, 1);
2442 return 1;
2444 case 'd':
2445 view_move_forward (view, vheight / 2);
2446 return 1;
2448 case 'u':
2449 view_move_backward (view, vheight / 2);
2450 return 1;
2452 case 'k':
2453 case 'y':
2454 view_move_backward (view, 1);
2455 return 1;
2457 case 'l':
2458 move_right (view);
2459 return 1;
2461 case ' ':
2462 case 'f':
2463 view_move_forward (view, vheight - 1);
2464 return 1;
2466 case XCTRL ('o'):
2467 view_other_cmd ();
2468 return 1;
2470 /* Unlike Ctrl-O, run a new shell if the subshell is not running. */
2471 case '!':
2472 exec_shell ();
2473 return 1;
2475 case 'F':
2476 set_monitor (view, on);
2477 return 1;
2479 case 'b':
2480 view_move_backward (view, vheight - 1);
2481 return 1;
2483 case KEY_IC:
2484 view_move_backward (view, 2);
2485 return 1;
2487 case KEY_DC:
2488 view_move_forward (view, 2);
2489 return 1;
2491 case 'm':
2492 view->marks[view->marker] = view->start_display;
2493 return 1;
2495 case 'r':
2496 view->start_display = view->marks[view->marker];
2497 view->dirty++;
2498 return 1;
2500 /* Use to indicate parent that we want to see the next/previous file */
2501 /* Only works on full screen mode */
2502 case XCTRL ('f'):
2503 case XCTRL ('b'):
2504 if (!view->have_frame)
2505 view->move_dir = c == XCTRL ('f') ? 1 : -1;
2506 /* fall */
2508 case 'q':
2509 case XCTRL ('g'):
2510 case ESC_CHAR:
2511 if (view_ok_to_quit (view))
2512 view->view_quit = 1;
2513 return 1;
2515 #ifdef HAVE_CHARSET
2516 case XCTRL ('t'):
2517 do_select_codepage ();
2518 view->dirty++;
2519 view_update (view, TRUE);
2520 return 1;
2521 #endif /* HAVE_CHARSET */
2524 if (c >= '0' && c <= '9')
2525 view->marker = c - '0';
2527 /* Restore the monitor status */
2528 set_monitor (view, prev_monitor);
2530 /* Key not used */
2531 return 0;
2534 /* Both views */
2535 static int
2536 view_event (WView *view, Gpm_Event *event, int *result)
2538 *result = MOU_NORMAL;
2540 /* We are not interested in the release events */
2541 if (!(event->type & (GPM_DOWN | GPM_DRAG)))
2542 return 0;
2544 /* Wheel events */
2545 if ((event->buttons & GPM_B_UP) && (event->type & GPM_DOWN)) {
2546 view_move_backward (view, 2);
2547 return 1;
2549 if ((event->buttons & GPM_B_DOWN) && (event->type & GPM_DOWN)) {
2550 view_move_forward (view, 2);
2551 return 1;
2554 /* Scrolling left and right */
2555 if (!view->wrap_mode) {
2556 if (event->x < view->widget.cols / 4) {
2557 move_left (view);
2558 goto processed;
2560 if (event->x > 3 * vwidth / 4) {
2561 move_right (view);
2562 goto processed;
2566 /* Scrolling up and down */
2567 if (event->y < view->widget.lines / 3) {
2568 if (mouse_move_pages_viewer)
2569 view_move_backward (view, view->widget.lines / 2 - 1);
2570 else
2571 view_move_backward (view, 1);
2572 goto processed;
2573 } else if (event->y > 2 * vheight / 3) {
2574 if (mouse_move_pages_viewer)
2575 view_move_forward (view, vheight / 2 - 1);
2576 else
2577 view_move_forward (view, 1);
2578 goto processed;
2581 return 0;
2583 processed:
2584 *result = MOU_REPEAT;
2585 return 1;
2588 /* Real view only */
2589 static int
2590 real_view_event (Gpm_Event *event, void *x)
2592 int result;
2594 if (view_event ((WView *) x, event, &result))
2595 view_update ((WView *) x, TRUE);
2596 return result;
2599 static void
2600 view_adjust_size (Dlg_head *h)
2602 WView *view;
2603 WButtonBar *bar;
2605 /* Look up the viewer and the buttonbar, we assume only two widgets here */
2606 view = (WView *) find_widget_type (h, (callback_fn) view_callback);
2607 bar = find_buttonbar (h);
2608 widget_set_size (&view->widget, 0, 0, LINES - 1, COLS);
2609 widget_set_size (&bar->widget, LINES - 1, 0, 1, COLS);
2611 view_update_bytes_per_line (view);
2614 /* Callback for the view dialog */
2615 static int
2616 view_dialog_callback (Dlg_head *h, int id, int msg)
2618 switch (msg) {
2619 case DLG_RESIZE:
2620 view_adjust_size (h);
2621 return MSG_HANDLED;
2623 return default_dlg_callback (h, id, msg);
2626 /* Real view only */
2628 view (char *_command, const char *_file, int *move_dir_p, int start_line)
2630 int error;
2631 WView *wview;
2632 WButtonBar *bar;
2633 Dlg_head *view_dlg;
2635 /* Create dialog and widgets, put them on the dialog */
2636 view_dlg =
2637 create_dlg (0, 0, LINES, COLS, NULL, view_dialog_callback,
2638 "[Internal File Viewer]", NULL, DLG_NONE);
2640 wview = view_new (0, 0, COLS, LINES - 1, 0);
2642 bar = buttonbar_new (1);
2644 add_widget (view_dlg, wview);
2645 add_widget (view_dlg, bar);
2647 error = view_init (wview, _command, _file, start_line);
2648 if (move_dir_p)
2649 *move_dir_p = 0;
2651 /* Please note that if you add another widget,
2652 * you have to modify view_adjust_size to
2653 * be aware of it
2655 if (!error) {
2656 run_dlg (view_dlg);
2657 if (move_dir_p)
2658 *move_dir_p = wview->move_dir;
2660 destroy_dlg (view_dlg);
2662 return !error;
2665 static void
2666 view_hook (void *v)
2668 WView *view = (WView *) v;
2669 WPanel *panel;
2671 /* If the user is busy typing, wait until he finishes to update the
2672 screen */
2673 if (!is_idle ()) {
2674 if (!hook_present (idle_hook, view_hook))
2675 add_hook (&idle_hook, view_hook, v);
2676 return;
2679 delete_hook (&idle_hook, view_hook);
2681 if (get_current_type () == view_listing)
2682 panel = cpanel;
2683 else if (get_other_type () == view_listing)
2684 panel = other_panel;
2685 else
2686 return;
2688 view_init (view, 0, panel->dir.list[panel->selected].fname, 0);
2689 display (view);
2690 view_status (view, TRUE);
2693 static int
2694 view_callback (WView *view, int msg, int par)
2696 int i;
2697 Dlg_head *h = view->widget.parent;
2699 switch (msg) {
2700 case WIDGET_INIT:
2701 view_update_bytes_per_line (view);
2702 if (view->have_frame)
2703 add_hook (&select_file_hook, view_hook, view);
2704 else
2705 view_labels (view);
2706 break;
2708 case WIDGET_DRAW:
2709 display (view);
2710 view_status (view, TRUE);
2711 break;
2713 case WIDGET_CURSOR:
2714 if (view->hex_mode)
2715 view_place_cursor (view);
2716 break;
2718 case WIDGET_KEY:
2719 i = view_handle_key ((WView *) view, par);
2720 if (view->view_quit)
2721 dlg_stop (h);
2722 else {
2723 view_update (view, TRUE);
2725 return i;
2727 case WIDGET_IDLE:
2728 /* This event is generated when the user is using the 'F' flag */
2729 view->bottom_first = -1;
2730 move_to_bottom (view);
2731 display (view);
2732 view_status (view, TRUE);
2733 sleep (1);
2734 return 1;
2736 case WIDGET_FOCUS:
2737 view_labels (view);
2738 return 1;
2741 return default_proc (msg, par);
2744 WView *
2745 view_new (int y, int x, int cols, int lines, int is_panel)
2747 WView *view = g_new0 (WView, 1);
2749 init_widget (&view->widget, y, x, lines, cols,
2750 (callback_fn) view_callback,
2751 (destroy_fn) view_destroy,
2752 (mouse_h) real_view_event, NULL);
2754 view->hex_mode = default_hex_mode;
2755 view->hexedit_mode = default_hexedit_mode;
2756 view->viewer_magic_flag = default_magic_flag;
2757 view->viewer_nroff_flag = default_nroff_flag;
2758 view->have_frame = is_panel;
2759 view->last_byte = -1;
2760 view->wrap_mode = global_wrap_mode;
2762 widget_want_cursor (view->widget, 0);
2764 return view;