Initial import of binutils 2.22 on the new vendor branch
[dragonfly.git] / contrib / texinfo / info / display.c
1 /* display.c -- How to display Info windows.
2    $Id: display.c,v 1.16 2008/06/11 09:55:41 gray Exp $
3
4    Copyright (C) 1993, 1997, 2003, 2004, 2006, 2007, 2008
5    Free Software Foundation, Inc.
6
7    This program is free software: you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation, either version 3 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19
20    Originally written by Brian Fox (bfox@ai.mit.edu). */
21
22 #include "info.h"
23 #include "display.h"
24
25 extern int info_any_buffered_input_p (void); /* Found in session.c. */
26
27 static void free_display (DISPLAY_LINE **display);
28 static DISPLAY_LINE **make_display (int width, int height);
29
30 void handle_tag (char *tag);
31 void handle_tag_start (char *tag);
32 void handle_tag_end (char *tag);
33
34 /* An array of display lines which tell us what is currently visible on
35    the display.  */
36 DISPLAY_LINE **the_display = NULL;
37
38 /* Non-zero means do no output. */
39 int display_inhibited = 0;
40
41 /* Initialize THE_DISPLAY to WIDTH and HEIGHT, with nothing in it. */
42 void
43 display_initialize_display (int width, int height)
44 {
45   free_display (the_display);
46   the_display = make_display (width, height);
47   display_clear_display (the_display);
48 }
49
50 /* Clear all of the lines in DISPLAY making the screen blank. */
51 void
52 display_clear_display (DISPLAY_LINE **display)
53 {
54   register int i;
55
56   for (i = 0; display[i]; i++)
57     {
58       display[i]->text[0] = '\0';
59       display[i]->textlen = 0;
60       display[i]->inverse = 0;
61     }
62 }
63
64 /* Non-zero if we didn't completely redisplay a window. */
65 int display_was_interrupted_p = 0;
66
67 /* Update the windows pointed to by WINDOW in the_display.  This actually
68    writes the text on the screen. */
69 void
70 display_update_display (WINDOW *window)
71 {
72   register WINDOW *win;
73
74   display_was_interrupted_p = 0;
75
76   /* For every window in the list, check contents against the display. */
77   for (win = window; win; win = win->next)
78     {
79       /* Only re-display visible windows which need updating. */
80       if (((win->flags & W_WindowVisible) == 0) ||
81           ((win->flags & W_UpdateWindow) == 0) ||
82           (win->height == 0))
83         continue;
84
85       display_update_one_window (win);
86       if (display_was_interrupted_p)
87         break;
88     }
89
90   /* Always update the echo area. */
91   display_update_one_window (the_echo_area);
92 }
93
94 void
95 handle_tag_start (char *tag)
96 {
97   /* TODO really handle this tag.  */
98   return;
99 }
100
101 void
102 handle_tag_end (char *tag)
103 {
104   /* TODO really handle this tag.  */
105   return;
106 }
107
108 void
109 handle_tag (char *tag)
110 {
111     if (tag[0] == '/')
112       {
113         tag++;
114         handle_tag_end (tag);
115       }
116     else
117       handle_tag_start (tag);
118 }
119
120 \f
121 struct display_node_closure {
122   WINDOW *win;
123   DISPLAY_LINE **display;
124 };
125
126 static int
127 find_diff (const char *a, size_t alen, const char *b, size_t blen, int *ppos)
128 {
129   mbi_iterator_t itra, itrb;
130   int i = 0;
131   int pos = 0;
132   
133   for (i = 0, mbi_init (itra, a, alen), mbi_init (itrb, b, blen);
134        mbi_avail (itra) && mbi_avail (itrb);
135        mbi_advance (itra), mbi_advance (itrb))
136     {
137       if (mb_cmp (mbi_cur (itra), mbi_cur (itrb)))
138         break;
139       pos += mb_len (mbi_cur (itra));
140     }
141   *ppos = pos;
142   return i;
143 }
144
145 int
146 display_node_text(void *closure, size_t line_index,
147                   const char *src_line,
148                   char *printed_line, size_t pl_index, size_t pl_count)
149 {
150   struct display_node_closure *dn = closure;
151   WINDOW *win = dn->win;
152   DISPLAY_LINE **display = dn->display;
153   DISPLAY_LINE *entry = display[win->first_row + line_index];
154
155   /* We have the exact line as it should appear on the screen.
156      Check to see if this line matches the one already appearing
157      on the screen. */
158
159   /* If the window is very small, entry might be NULL. */
160   if (entry)
161     {
162       int i, off;
163               
164       /* If the screen line is inversed, then we have to clear
165          the line from the screen first.  Why, I don't know.
166          (But don't do this if we have no visible entries, as can
167          happen if the window is shrunk very small.)  */
168       if (entry->inverse
169           /* Need to erase the line if it has escape sequences.  */
170           || (raw_escapes_p && mbschr (entry->text, '\033') != 0))
171         {
172           terminal_goto_xy (0, win->first_row + line_index);
173           terminal_clear_to_eol ();
174           entry->inverse = 0;
175           entry->text[0] = '\0';
176           entry->textlen = 0;
177         }
178
179       i = find_diff (printed_line, pl_index,
180                      entry->text, strlen (entry->text), &off);
181
182       /* If the lines are not the same length, or if they differed
183          at all, we must do some redrawing. */
184       if (i != pl_count || pl_count != entry->textlen)
185         {
186           /* Move to the proper point on the terminal. */
187           terminal_goto_xy (i, win->first_row + line_index);
188           /* If there is any text to print, print it. */
189           if (i != pl_count)
190             terminal_put_text (printed_line + i);
191           
192           /* If the printed text didn't extend all the way to the edge
193              of the window, and text was appearing between here and the
194              edge of the window, clear from here to the end of the
195              line. */
196           if ((pl_count < win->width && pl_count < entry->textlen)
197               || entry->inverse)
198             terminal_clear_to_eol ();
199           
200           fflush (stdout);
201           
202           /* Update the display text buffer. */
203           if (strlen (printed_line) > (unsigned int) screenwidth)
204             /* printed_line[] can include more than screenwidth
205                characters, e.g. if multibyte encoding is used or
206                if we are under -R and there are escape sequences
207                in it.  However, entry->text was allocated (in
208                display_initialize_display) for screenwidth
209                bytes only.  */
210             entry->text = xrealloc (entry->text, strlen (printed_line) + 1);
211           strcpy (entry->text + off, printed_line + off);
212           entry->textlen = pl_count;
213           
214           /* Lines showing node text are not in inverse.  Only modelines
215              have that distinction. */
216           entry->inverse = 0;
217         }
218     }
219
220   /* A line has been displayed, and the screen reflects that state.
221      If there is typeahead pending, then let that typeahead be read
222      now, instead of continuing with the display. */
223   if (info_any_buffered_input_p ())
224     {
225       display_was_interrupted_p = 1;
226       return 1;
227     }
228
229   if (line_index + 1 == win->height)
230     return 1;
231
232   return 0;
233 }
234
235 void
236 display_update_one_window (WINDOW *win)
237 {
238   size_t line_index = 0;
239   DISPLAY_LINE **display = the_display;
240
241   /* If display is inhibited, that counts as an interrupted display. */
242   if (display_inhibited)
243     display_was_interrupted_p = 1;
244
245   /* If the window has no height, or display is inhibited, quit now.
246      Strictly speaking, it should only be necessary to test if the
247      values are equal to zero, since window_new_screen_size should
248      ensure that the window height/width never becomes negative, but
249      since historically this has often been the culprit for crashes, do
250      our best to be doubly safe.  */
251   if (win->height <= 0 || win->width <= 0 || display_inhibited)
252     return;
253
254   /* If the window's first row doesn't appear in the_screen, then it
255      cannot be displayed.  This can happen when the_echo_area is the
256      window to be displayed, and the screen has shrunk to less than one
257      line. */
258   if ((win->first_row < 0) || (win->first_row > the_screen->height))
259     return;
260
261   if (win->node && win->line_starts)
262     {
263       struct display_node_closure dnc;
264
265       dnc.win = win;
266       dnc.display = the_display;
267       
268       line_index = process_node_text (win, win->line_starts[win->pagetop],
269                                       1,
270                                       display_node_text,
271                                       &dnc);
272       if (display_was_interrupted_p)
273         return;
274     }
275   
276   /* We have reached the end of the node or the end of the window.  If it
277      is the end of the node, then clear the lines of the window from here
278      to the end of the window. */
279   for (; line_index < win->height; line_index++)
280     {
281       DISPLAY_LINE *entry = display[win->first_row + line_index];
282
283       /* If this line has text on it then make it go away. */
284       if (entry && entry->textlen)
285         {
286           entry->textlen = 0;
287           entry->text[0] = '\0';
288
289           terminal_goto_xy (0, win->first_row + line_index);
290           terminal_clear_to_eol ();
291         }
292     }
293
294   /* Finally, if this window has a modeline it might need to be redisplayed.
295      Check the window's modeline against the one in the display, and update
296      if necessary. */
297   if (!(win->flags & W_InhibitMode))
298     {
299       window_make_modeline (win);
300       line_index = win->first_row + win->height;
301
302       /* This display line must both be in inverse, and have the same
303          contents. */
304       if ((!display[line_index]->inverse) ||
305           (strcmp (display[line_index]->text, win->modeline) != 0))
306         {
307           terminal_goto_xy (0, line_index);
308           terminal_begin_inverse ();
309           terminal_put_text (win->modeline);
310           terminal_end_inverse ();
311           strcpy (display[line_index]->text, win->modeline);
312           display[line_index]->inverse = 1;
313           display[line_index]->textlen = strlen (win->modeline);
314           fflush (stdout);
315         }
316     }
317
318   fflush (stdout);
319
320   /* Okay, this window doesn't need updating anymore. */
321   win->flags &= ~W_UpdateWindow;
322 }
323
324 /* Scroll the region of the_display starting at START, ending at END, and
325    moving the lines AMOUNT lines.  If AMOUNT is less than zero, the lines
326    are moved up in the screen, otherwise down.  Actually, it is possible
327    for no scrolling to take place in the case that the terminal doesn't
328    support it.  This doesn't matter to us. */
329 void
330 display_scroll_display (int start, int end, int amount)
331 {
332   register int i, last;
333   DISPLAY_LINE *temp;
334
335   /* If this terminal cannot do scrolling, give up now. */
336   if (!terminal_can_scroll)
337     return;
338
339   /* If there isn't anything displayed on the screen because it is too
340      small, quit now. */
341   if (!the_display[0])
342     return;
343
344   /* If there is typeahead pending, then don't actually do any scrolling. */
345   if (info_any_buffered_input_p ())
346     return;
347
348   /* Do it on the screen. */
349   terminal_scroll_terminal (start, end, amount);
350
351   /* Now do it in the display buffer so our contents match the screen. */
352   if (amount > 0)
353     {
354       last = end + amount;
355
356       /* Shift the lines to scroll right into place. */
357       for (i = 0; i < (end - start); i++)
358         {
359           temp = the_display[last - i];
360           the_display[last - i] = the_display[end - i];
361           the_display[end - i] = temp;
362         }
363
364       /* The lines have been shifted down in the buffer.  Clear all of the
365          lines that were vacated. */
366       for (i = start; i != (start + amount); i++)
367         {
368           the_display[i]->text[0] = '\0';
369           the_display[i]->textlen = 0;
370           the_display[i]->inverse = 0;
371         }
372     }
373
374   if (amount < 0)
375     {
376       last = start + amount;
377       for (i = 0; i < (end - start); i++)
378         {
379           temp = the_display[last + i];
380           the_display[last + i] = the_display[start + i];
381           the_display[start + i] = temp;
382         }
383
384       /* The lines have been shifted up in the buffer.  Clear all of the
385          lines that are left over. */
386       for (i = end + amount; i != end; i++)
387         {
388           the_display[i]->text[0] = '\0';
389           the_display[i]->textlen = 0;
390           the_display[i]->inverse = 0;
391         }
392     }
393 }
394
395 /* Try to scroll lines in WINDOW.  OLD_PAGETOP is the pagetop of WINDOW before
396    having had its line starts recalculated.  OLD_STARTS is the list of line
397    starts that used to appear in this window.  OLD_COUNT is the number of lines
398    that appear in the OLD_STARTS array. */
399 void
400 display_scroll_line_starts (WINDOW *window, int old_pagetop,
401     char **old_starts, int old_count)
402 {
403   register int i, old, new;     /* Indices into the line starts arrays. */
404   int last_new, last_old;       /* Index of the last visible line. */
405   int old_first, new_first;     /* Index of the first changed line. */
406   int unchanged_at_top = 0;
407   int already_scrolled = 0;
408
409   /* Locate the first line which was displayed on the old window. */
410   old_first = old_pagetop;
411   new_first = window->pagetop;
412
413   /* Find the last line currently visible in this window. */
414   last_new = window->pagetop + (window->height - 1);
415   if (last_new > window->line_count)
416     last_new = window->line_count - 1;
417
418   /* Find the last line which used to be currently visible in this window. */
419   last_old = old_pagetop + (window->height - 1);
420   if (last_old > old_count)
421     last_old = old_count - 1;
422
423   for (old = old_first, new = new_first;
424        old < last_old && new < last_new;
425        old++, new++)
426     if (old_starts[old] != window->line_starts[new])
427       break;
428     else
429       unchanged_at_top++;
430
431   /* Loop through the old lines looking for a match in the new lines. */
432   for (old = old_first + unchanged_at_top; old < last_old; old++)
433     {
434       for (new = new_first; new < last_new; new++)
435         if (old_starts[old] == window->line_starts[new])
436           {
437             /* Find the extent of the matching lines. */
438             for (i = 0; (old + i) < last_old; i++)
439               if (old_starts[old + i] != window->line_starts[new + i])
440                 break;
441
442             /* Scroll these lines if there are enough of them. */
443             {
444               int start, end, amount;
445
446               start = (window->first_row
447                        + ((old + already_scrolled) - old_pagetop));
448               amount = new - (old + already_scrolled);
449               end = window->first_row + window->height;
450
451               /* If we are shifting the block of lines down, then the last
452                  AMOUNT lines will become invisible.  Thus, don't bother
453                  scrolling them. */
454               if (amount > 0)
455                 end -= amount;
456
457               if ((end - start) > 0)
458                 {
459                   display_scroll_display (start, end, amount);
460
461                   /* Some lines have been scrolled.  Simulate the scrolling
462                      by offsetting the value of the old index. */
463                   old += i;
464                   already_scrolled += amount;
465                 }
466             }
467           }
468     }
469 }
470
471 /* Move the screen cursor to directly over the current character in WINDOW. */
472 void
473 display_cursor_at_point (WINDOW *window)
474 {
475   int vpos, hpos;
476
477   vpos = window_line_of_point (window) - window->pagetop + window->first_row;
478   hpos = window_get_cursor_column (window);
479   terminal_goto_xy (hpos, vpos);
480   fflush (stdout);
481 }
482 \f
483 /* **************************************************************** */
484 /*                                                                  */
485 /*                   Functions Static to this File                  */
486 /*                                                                  */
487 /* **************************************************************** */
488
489 /* Make a DISPLAY_LINE ** with width and height. */
490 static DISPLAY_LINE **
491 make_display (int width, int height)
492 {
493   register int i;
494   DISPLAY_LINE **display;
495
496   display = xmalloc ((1 + height) * sizeof (DISPLAY_LINE *));
497
498   for (i = 0; i < height; i++)
499     {
500       display[i] = xmalloc (sizeof (DISPLAY_LINE));
501       display[i]->text = xmalloc (1 + width);
502       display[i]->textlen = 0;
503       display[i]->inverse = 0;
504     }
505   display[i] = NULL;
506   return display;
507 }
508
509 /* Free the storage allocated to DISPLAY. */
510 static void
511 free_display (DISPLAY_LINE **display)
512 {
513   register int i;
514   register DISPLAY_LINE *display_line;
515
516   if (!display)
517     return;
518
519   for (i = 0; (display_line = display[i]); i++)
520     {
521       free (display_line->text);
522       free (display_line);
523     }
524   free (display);
525 }
526