OSDN Git Service

* flow.c (mark_used_reg): Hack around rs6000 eliminable pic reg.
[pf3gnuchains/gcc-fork.git] / texinfo / info / display.c
1 /* display.c -- How to display Info windows.
2    $Id: display.c,v 1.1.1.2 1998/03/22 20:42:23 law Exp $
3
4    Copyright (C) 1993, 97 Free Software Foundation, Inc.
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2, or (at your option)
9    any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19
20    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 (); /* Found in session.c. */
26
27 static void free_display ();
28 static DISPLAY_LINE **make_display ();
29
30 /* An array of display lines which tell us what is currently visible on
31    the display.  */
32 DISPLAY_LINE **the_display = (DISPLAY_LINE **)NULL;
33
34 /* Non-zero means do no output. */
35 int display_inhibited = 0;
36
37 /* Initialize THE_DISPLAY to WIDTH and HEIGHT, with nothing in it. */
38 void
39 display_initialize_display (width, height)
40      int width, height;
41 {
42   free_display (the_display);
43   the_display = make_display (width, height);
44   display_clear_display (the_display);
45 }
46
47 /* Clear all of the lines in DISPLAY making the screen blank. */
48 void
49 display_clear_display (display)
50      DISPLAY_LINE **display;
51 {
52   register int i;
53   register DISPLAY_LINE *display_line;
54
55   for (i = 0; (display_line = display[i]); i++)
56     {
57       display[i]->text[0] = '\0';
58       display[i]->textlen = 0;
59       display[i]->inverse = 0;
60     }
61 }
62
63 /* Non-zero if we didn't completely redisplay a window. */
64 int display_was_interrupted_p = 0;
65
66 /* Update the windows pointed to by WINDOW in the_display.  This actually
67    writes the text on the screen. */
68 void
69 display_update_display (window)
70      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 /* Display WIN on the_display.  Unlike display_update_display (), this
95    function only does one window. */
96 void
97 display_update_one_window (win)
98      WINDOW *win;
99 {
100   register char *nodetext;      /* Current character to display. */
101   register char *last_node_char; /* Position of the last character in node. */
102   register int i;               /* General use index. */
103   char *printed_line;           /* Buffer for a printed line. */
104   int pl_index = 0;             /* Index into PRINTED_LINE. */
105   int line_index = 0;           /* Number of lines done so far. */
106   DISPLAY_LINE **display = the_display;
107
108   /* If display is inhibited, that counts as an interrupted display. */
109   if (display_inhibited)
110     display_was_interrupted_p = 1;
111
112   /* If the window has no height, or display is inhibited, quit now. */
113   if (!win->height || display_inhibited)
114     return;
115
116   /* If the window's first row doesn't appear in the_screen, then it
117      cannot be displayed.  This can happen when the_echo_area is the
118      window to be displayed, and the screen has shrunk to less than one
119      line. */
120   if ((win->first_row < 0) || (win->first_row > the_screen->height))
121     return;
122
123   /* Print each line in the window into our local buffer, and then
124      check the contents of that buffer against the display.  If they
125      differ, update the display. */
126   printed_line = (char *)xmalloc (1 + win->width);
127
128   if (!win->node || !win->line_starts)
129     goto done_with_node_display;
130
131   nodetext = win->line_starts[win->pagetop];
132   last_node_char = win->node->contents + win->node->nodelen;
133
134   for (; nodetext < last_node_char; nodetext++)
135     {
136       char *rep, *rep_carried_over, rep_temp[2];
137       int replen;
138
139       if (isprint (*nodetext))
140         {
141           rep_temp[0] = *nodetext;
142           replen = 1;
143           rep_temp[1] = '\0';
144           rep = rep_temp;
145         }
146       else
147         {
148           if (*nodetext == '\r' || *nodetext == '\n')
149             {
150               replen = win->width - pl_index;
151             }
152           else
153             {
154               rep = printed_representation (*nodetext, pl_index);
155               replen = strlen (rep);
156             }
157         }
158
159       /* If this character can be printed without passing the width of
160          the line, then stuff it into the line. */
161       if (replen + pl_index < win->width)
162         {
163           /* Optimize if possible. */
164           if (replen == 1)
165             {
166               printed_line[pl_index++] = *rep;
167             }
168           else
169             {
170               for (i = 0; i < replen; i++)
171                 printed_line[pl_index++] = rep[i];
172             }
173         }
174       else
175         {
176           DISPLAY_LINE *entry;
177
178           /* If this character cannot be printed in this line, we have
179              found the end of this line as it would appear on the screen.
180              Carefully print the end of the line, and then compare. */
181           if (*nodetext == '\n' || *nodetext == '\r' || *nodetext == '\t')
182             {
183               printed_line[pl_index] = '\0';
184               rep_carried_over = (char *)NULL;
185             }
186           else
187             {
188               /* The printed representation of this character extends into
189                  the next line.  Remember the offset of the last character
190                  printed out of REP so that we can carry the character over
191                  to the next line. */
192               for (i = 0; pl_index < (win->width - 1);)
193                 printed_line[pl_index++] = rep[i++];
194               
195               rep_carried_over = rep + i;
196
197               /* If printing the last character in this window couldn't
198                  possibly cause the screen to scroll, place a backslash
199                  in the rightmost column. */
200               if (1 + line_index + win->first_row < the_screen->height)
201                 {
202                   if (win->flags & W_NoWrap)
203                     printed_line[pl_index++] = '$';
204                   else
205                     printed_line[pl_index++] = '\\';
206                 }
207               printed_line[pl_index] = '\0';
208             }
209
210           /* We have the exact line as it should appear on the screen.
211              Check to see if this line matches the one already appearing
212              on the screen. */
213           entry = display[line_index + win->first_row];
214
215           /* If the screen line is inversed, then we have to clear
216              the line from the screen first.  Why, I don't know. */
217           if (entry->inverse)
218             {
219               terminal_goto_xy (0, line_index + win->first_row);
220               terminal_clear_to_eol ();
221               entry->inverse = 0;
222               entry->text[0] = '\0';
223               entry->textlen = 0;
224             }
225
226           /* Find the offset where these lines differ. */
227           for (i = 0; i < pl_index; i++)
228             if (printed_line[i] != entry->text[i])
229               break;
230
231           /* If the lines are not the same length, or if they differed
232              at all, we must do some redrawing. */
233           if ((i != pl_index) || (pl_index != entry->textlen))
234             {
235               /* Move to the proper point on the terminal. */
236               terminal_goto_xy (i, line_index + win->first_row);
237
238               /* If there is any text to print, print it. */
239               if (i != pl_index)
240                 terminal_put_text (printed_line + i);
241
242               /* If the printed text didn't extend all the way to the edge
243                  of the window, and text was appearing between here and the
244                  edge of the window, clear from here to the end of the line. */
245               if ((pl_index < win->width && pl_index < entry->textlen) ||
246                   (entry->inverse))
247                 terminal_clear_to_eol ();
248
249               fflush (stdout);
250
251               /* Update the display text buffer. */
252               strcpy (entry->text + i, printed_line + i);
253               entry->textlen = pl_index;
254
255               /* Lines showing node text are not in inverse.  Only modelines
256                  have that distinction. */
257               entry->inverse = 0;
258             }
259
260           /* We have done at least one line.  Increment our screen line
261              index, and check against the bottom of the window. */
262           if (++line_index == win->height)
263             break;
264
265           /* A line has been displayed, and the screen reflects that state.
266              If there is typeahead pending, then let that typeahead be read
267              now, instead of continuing with the display. */
268           if (info_any_buffered_input_p ())
269             {
270               free (printed_line);
271               display_was_interrupted_p = 1;
272               return;
273             }
274
275           /* Reset PL_INDEX to the start of the line. */
276           pl_index = 0;
277
278           /* If there are characters from REP left to print, stuff them
279              into the buffer now. */
280           if (rep_carried_over)
281             for (; rep[pl_index]; pl_index++)
282               printed_line[pl_index] = rep[pl_index];
283
284           /* If this window has chosen not to wrap lines, skip to the end
285              of the physical line in the buffer, and start a new line here. */
286           if (pl_index && (win->flags & W_NoWrap))
287             {
288               char *begin;
289
290               pl_index = 0;
291               printed_line[0] = '\0';
292
293               begin = nodetext;
294               
295               while ((nodetext < last_node_char) && (*nodetext != '\n'))
296                 nodetext++;
297             }
298         }
299     }
300
301  done_with_node_display:
302   /* We have reached the end of the node or the end of the window.  If it
303      is the end of the node, then clear the lines of the window from here
304      to the end of the window. */
305   for (; line_index < win->height; line_index++)
306     {
307       DISPLAY_LINE *entry = display[line_index + win->first_row];
308
309       /* If this line has text on it then make it go away. */
310       if (entry && entry->textlen)
311         {
312           entry->textlen = 0;
313           entry->text[0] = '\0';
314
315           terminal_goto_xy (0, line_index + win->first_row);
316           terminal_clear_to_eol ();
317         }
318     }
319
320   /* Finally, if this window has a modeline it might need to be redisplayed.
321      Check the window's modeline against the one in the display, and update
322      if necessary. */
323   if ((win->flags & W_InhibitMode) == 0)
324     {
325       window_make_modeline (win);
326       line_index = win->first_row + win->height;
327
328       /* This display line must both be in inverse, and have the same
329          contents. */
330       if ((!display[line_index]->inverse) ||
331           (strcmp (display[line_index]->text, win->modeline) != 0))
332         {
333           terminal_goto_xy (0, line_index);
334           terminal_begin_inverse ();
335           terminal_put_text (win->modeline);
336           terminal_end_inverse ();
337           strcpy (display[line_index]->text, win->modeline);
338           display[line_index]->inverse = 1;
339           display[line_index]->textlen = strlen (win->modeline);
340           fflush (stdout);
341         }
342     }
343
344   /* Okay, this window doesn't need updating anymore. */
345   win->flags &= ~W_UpdateWindow;
346   free (printed_line);
347   fflush (stdout);
348 }
349
350 /* Scroll the region of the_display starting at START, ending at END, and
351    moving the lines AMOUNT lines.  If AMOUNT is less than zero, the lines
352    are moved up in the screen, otherwise down.  Actually, it is possible
353    for no scrolling to take place in the case that the terminal doesn't
354    support it.  This doesn't matter to us. */
355 void
356 display_scroll_display (start, end, amount)
357      int start, end, amount;
358 {
359   register int i, last;
360   DISPLAY_LINE *temp;
361
362   /* If this terminal cannot do scrolling, give up now. */
363   if (!terminal_can_scroll)
364     return;
365
366   /* If there isn't anything displayed on the screen because it is too
367      small, quit now. */
368   if (!the_display[0])
369     return;
370
371   /* If there is typeahead pending, then don't actually do any scrolling. */
372   if (info_any_buffered_input_p ())
373     return;
374
375   /* Do it on the screen. */
376   terminal_scroll_terminal (start, end, amount);
377
378   /* Now do it in the display buffer so our contents match the screen. */
379   if (amount > 0)
380     {
381       last = end + amount;
382
383       /* Shift the lines to scroll right into place. */
384       for (i = 0; i < (end - start); i++)
385         {
386           temp = the_display[last - i];
387           the_display[last - i] = the_display[end - i];
388           the_display[end - i] = temp;
389         }
390
391       /* The lines have been shifted down in the buffer.  Clear all of the
392          lines that were vacated. */
393       for (i = start; i != (start + amount); i++)
394         {
395           the_display[i]->text[0] = '\0';
396           the_display[i]->textlen = 0;
397           the_display[i]->inverse = 0;
398         }
399     }
400
401   if (amount < 0)
402     {
403       last = start + amount;
404       for (i = 0; i < (end - start); i++)
405         {
406           temp = the_display[last + i];
407           the_display[last + i] = the_display[start + i];
408           the_display[start + i] = temp;
409         }
410
411       /* The lines have been shifted up in the buffer.  Clear all of the
412          lines that are left over. */
413       for (i = end + amount; i != end; i++)
414         {
415           the_display[i]->text[0] = '\0';
416           the_display[i]->textlen = 0;
417           the_display[i]->inverse = 0;
418         }
419     }
420 }
421
422 /* Try to scroll lines in WINDOW.  OLD_PAGETOP is the pagetop of WINDOW before
423    having had its line starts recalculated.  OLD_STARTS is the list of line
424    starts that used to appear in this window.  OLD_COUNT is the number of lines
425    that appear in the OLD_STARTS array. */
426 void
427 display_scroll_line_starts (window, old_pagetop, old_starts, old_count)
428      WINDOW *window;
429      int old_pagetop, old_count;
430      char **old_starts;
431 {
432   register int i, old, new;     /* Indices into the line starts arrays. */
433   int last_new, last_old;       /* Index of the last visible line. */
434   int old_first, new_first;     /* Index of the first changed line. */
435   int unchanged_at_top = 0;
436   int already_scrolled = 0;
437
438   /* Locate the first line which was displayed on the old window. */
439   old_first = old_pagetop;
440   new_first = window->pagetop;
441
442   /* Find the last line currently visible in this window. */
443   last_new = window->pagetop + (window->height - 1);
444   if (last_new > window->line_count)
445     last_new = window->line_count - 1;
446
447   /* Find the last line which used to be currently visible in this window. */
448   last_old = old_pagetop + (window->height - 1);
449   if (last_old > old_count)
450     last_old = old_count - 1;
451
452   for (old = old_first, new = new_first;
453        old < last_old && new < last_new;
454        old++, new++)
455     if (old_starts[old] != window->line_starts[new])
456       break;
457     else
458       unchanged_at_top++;
459
460   /* Loop through the old lines looking for a match in the new lines. */
461   for (old = old_first + unchanged_at_top; old < last_old; old++)
462     {
463       for (new = new_first; new < last_new; new++)
464         if (old_starts[old] == window->line_starts[new])
465           {
466             /* Find the extent of the matching lines. */
467             for (i = 0; (old + i) < last_old; i++)
468               if (old_starts[old + i] != window->line_starts[new + i])
469                 break;
470
471             /* Scroll these lines if there are enough of them. */
472             {
473               int start, end, amount;
474
475               start = (window->first_row
476                        + ((old + already_scrolled) - old_pagetop));
477               amount = new - (old + already_scrolled);
478               end = window->first_row + window->height;
479
480               /* If we are shifting the block of lines down, then the last
481                  AMOUNT lines will become invisible.  Thus, don't bother
482                  scrolling them. */
483               if (amount > 0)
484                 end -= amount;
485
486               if ((end - start) > 0)
487                 {
488                   display_scroll_display (start, end, amount);
489
490                   /* Some lines have been scrolled.  Simulate the scrolling
491                      by offsetting the value of the old index. */
492                   old += i;
493                   already_scrolled += amount;
494                 }
495             }
496           }
497     }
498 }
499
500 /* Move the screen cursor to directly over the current character in WINDOW. */
501 void
502 display_cursor_at_point (window)
503      WINDOW *window;
504 {
505   int vpos, hpos;
506
507   vpos = window_line_of_point (window) - window->pagetop + window->first_row;
508   hpos = window_get_cursor_column (window);
509   terminal_goto_xy (hpos, vpos);
510   fflush (stdout);
511 }
512 \f
513 /* **************************************************************** */
514 /*                                                                  */
515 /*                   Functions Static to this File                  */
516 /*                                                                  */
517 /* **************************************************************** */
518
519 /* Make a DISPLAY_LINE ** with width and height. */
520 static DISPLAY_LINE **
521 make_display (width, height)
522      int width, height;
523 {
524   register int i;
525   DISPLAY_LINE **display;
526
527   display = (DISPLAY_LINE **)xmalloc ((1 + height) * sizeof (DISPLAY_LINE *));
528
529   for (i = 0; i < height; i++)
530     {
531       display[i] = (DISPLAY_LINE *)xmalloc (sizeof (DISPLAY_LINE));
532       display[i]->text = (char *)xmalloc (1 + width);
533       display[i]->textlen = 0;
534       display[i]->inverse = 0;
535     }
536   display[i] = (DISPLAY_LINE *)NULL;
537   return (display);
538 }
539
540 /* Free the storage allocated to DISPLAY. */
541 static void
542 free_display (display)
543      DISPLAY_LINE **display;
544 {
545   register int i;
546   register DISPLAY_LINE *display_line;
547
548   if (!display)
549     return;
550
551   for (i = 0; (display_line = display[i]); i++)
552     {
553       free (display_line->text);
554       free (display_line);
555     }
556   free (display);
557 }