Back to index

tetex-bin  3.0
display.c
Go to the documentation of this file.
00001 /* display.c -- How to display Info windows.
00002    $Id: display.c,v 1.7 2004/04/11 17:56:45 karl Exp $
00003 
00004    Copyright (C) 1993, 1997, 2003, 2004 Free Software Foundation, Inc.
00005 
00006    This program is free software; you can redistribute it and/or modify
00007    it under the terms of the GNU General Public License as published by
00008    the Free Software Foundation; either version 2, or (at your option)
00009    any later version.
00010 
00011    This program is distributed in the hope that it will be useful,
00012    but WITHOUT ANY WARRANTY; without even the implied warranty of
00013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014    GNU General Public License for more details.
00015 
00016    You should have received a copy of the GNU General Public License
00017    along with this program; if not, write to the Free Software
00018    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00019 
00020    Originally written by Brian Fox (bfox@ai.mit.edu). */
00021 
00022 #include "info.h"
00023 #include "display.h"
00024 
00025 extern int info_any_buffered_input_p (void); /* Found in session.c. */
00026 
00027 static void free_display (DISPLAY_LINE **display);
00028 static DISPLAY_LINE **make_display (int width, int height);
00029 
00030 void handle_tag (char *tag);
00031 void handle_tag_start (char *tag);
00032 void handle_tag_end (char *tag);
00033 
00034 /* An array of display lines which tell us what is currently visible on
00035    the display.  */
00036 DISPLAY_LINE **the_display = (DISPLAY_LINE **)NULL;
00037 
00038 /* Non-zero means do no output. */
00039 int display_inhibited = 0;
00040 
00041 /* Initialize THE_DISPLAY to WIDTH and HEIGHT, with nothing in it. */
00042 void
00043 display_initialize_display (int width, int height)
00044 {
00045   free_display (the_display);
00046   the_display = make_display (width, height);
00047   display_clear_display (the_display);
00048 }
00049 
00050 /* Clear all of the lines in DISPLAY making the screen blank. */
00051 void
00052 display_clear_display (DISPLAY_LINE **display)
00053 {
00054   register int i;
00055 
00056   for (i = 0; display[i]; i++)
00057     {
00058       display[i]->text[0] = '\0';
00059       display[i]->textlen = 0;
00060       display[i]->inverse = 0;
00061     }
00062 }
00063 
00064 /* Non-zero if we didn't completely redisplay a window. */
00065 int display_was_interrupted_p = 0;
00066 
00067 /* Update the windows pointed to by WINDOW in the_display.  This actually
00068    writes the text on the screen. */
00069 void
00070 display_update_display (WINDOW *window)
00071 {
00072   register WINDOW *win;
00073 
00074   display_was_interrupted_p = 0;
00075 
00076   /* For every window in the list, check contents against the display. */
00077   for (win = window; win; win = win->next)
00078     {
00079       /* Only re-display visible windows which need updating. */
00080       if (((win->flags & W_WindowVisible) == 0) ||
00081           ((win->flags & W_UpdateWindow) == 0) ||
00082           (win->height == 0))
00083         continue;
00084 
00085       display_update_one_window (win);
00086       if (display_was_interrupted_p)
00087         break;
00088     }
00089 
00090   /* Always update the echo area. */
00091   display_update_one_window (the_echo_area);
00092 }
00093 
00094 void
00095 handle_tag_start (char *tag)
00096 {
00097   /* TODO really handle this tag.  */
00098   return;
00099 }
00100 
00101 void
00102 handle_tag_end (char *tag)
00103 {
00104   /* TODO really handle this tag.  */
00105   return;
00106 }
00107 
00108 void
00109 handle_tag (char *tag)
00110 {
00111     if (tag[0] == '/')
00112       {
00113        tag++;
00114        handle_tag_end (tag);
00115       }
00116     else
00117       handle_tag_start (tag);
00118 }
00119 
00120 /* Display WIN on the_display.  Unlike display_update_display (), this
00121    function only does one window. */
00122 void
00123 display_update_one_window (WINDOW *win)
00124 {
00125   register char *nodetext;      /* Current character to display. */
00126   register char *last_node_char; /* Position of the last character in node. */
00127   register int i;               /* General use index. */
00128   char *printed_line;           /* Buffer for a printed line. */
00129   int pl_index = 0;             /* Index into PRINTED_LINE. */
00130   int line_index = 0;           /* Number of lines done so far. */
00131   int pl_ignore = 0;        /* How many chars use zero width on screen. */
00132   int allocated_win_width;
00133   DISPLAY_LINE **display = the_display;
00134 
00135   /* If display is inhibited, that counts as an interrupted display. */
00136   if (display_inhibited)
00137     display_was_interrupted_p = 1;
00138 
00139   /* If the window has no height, or display is inhibited, quit now. */
00140   if (!win->height || display_inhibited)
00141     return;
00142 
00143   /* If the window's first row doesn't appear in the_screen, then it
00144      cannot be displayed.  This can happen when the_echo_area is the
00145      window to be displayed, and the screen has shrunk to less than one
00146      line. */
00147   if ((win->first_row < 0) || (win->first_row > the_screen->height))
00148     return;
00149 
00150   /* Print each line in the window into our local buffer, and then
00151      check the contents of that buffer against the display.  If they
00152      differ, update the display. */
00153   allocated_win_width = win->width + 1;
00154   printed_line = (char *)xmalloc (allocated_win_width);
00155 
00156   if (!win->node || !win->line_starts)
00157     goto done_with_node_display;
00158 
00159   nodetext = win->line_starts[win->pagetop];
00160   last_node_char = win->node->contents + win->node->nodelen;
00161 
00162   for (; nodetext < last_node_char; nodetext++)
00163     {
00164       char *rep = NULL, *rep_carried_over, rep_temp[2];
00165       int replen;
00166 
00167       if (isprint (*nodetext))
00168         {
00169           rep_temp[0] = *nodetext;
00170           replen = 1;
00171           rep_temp[1] = '\0';
00172           rep = rep_temp;
00173         }
00174       else
00175         {
00176           if (*nodetext == '\r' || *nodetext == '\n')
00177             {
00178               replen = win->width - pl_index + pl_ignore;
00179             }
00180          else if (*nodetext == '\0'
00181                  && (nodetext + 2) < last_node_char
00182                  && *(nodetext + 1) == '\b'
00183                  && *(nodetext + 2) == '[')
00184            {
00185              /* Found new style tag/cookie \0\b[
00186                Read until the closing tag \0\b] */
00187              int element_len = 0;
00188              char *element;
00189 
00190              /* Skip the escapes.  */
00191              nodetext += 3;
00192 
00193              while (!(*nodetext == '\0'
00194                   && *(nodetext + 1) == '\b'
00195                   && *(nodetext + 2) == ']'))
00196               {
00197                 nodetext++;
00198                 element_len++;
00199               }
00200 
00201              element = (char *) malloc (element_len + 1);
00202              strncpy (element, nodetext - element_len, element_len);
00203 
00204              /* Skip the escapes.  */
00205              nodetext += 2;
00206              pl_ignore += element_len + 5;
00207              /* Append string terminator.  */
00208              element[element_len] = '\0';
00209 
00210              handle_tag (element);
00211 
00212              /* Over and out */
00213              free (element);
00214 
00215              continue;
00216            }
00217           else
00218             {
00219               rep = printed_representation (*nodetext, pl_index);
00220               replen = strlen (rep);
00221             }
00222         }
00223 
00224       /* Support ANSI escape sequences under -R.  */
00225       if (raw_escapes_p
00226          && *nodetext == '\033'
00227          && nodetext[1] == '['
00228          && isdigit (nodetext[2]))
00229        {
00230          if (nodetext[3] == 'm')
00231            pl_ignore += 4;
00232          else if (isdigit (nodetext[3]) && nodetext[4] == 'm')
00233            pl_ignore += 5;
00234        }
00235       while (pl_index + 2 >= allocated_win_width - 1)
00236        {
00237          allocated_win_width *= 2;
00238          printed_line = (char *)xrealloc (printed_line, allocated_win_width);
00239        }
00240 
00241       /* If this character can be printed without passing the width of
00242          the line, then stuff it into the line. */
00243       if (replen + pl_index < win->width + pl_ignore)
00244         {
00245           /* Optimize if possible. */
00246           if (replen == 1)
00247             {
00248               printed_line[pl_index++] = *rep;
00249             }
00250           else
00251             {
00252               for (i = 0; i < replen; i++)
00253                 printed_line[pl_index++] = rep[i];
00254             }
00255         }
00256       else
00257         {
00258           DISPLAY_LINE *entry;
00259 
00260           /* If this character cannot be printed in this line, we have
00261              found the end of this line as it would appear on the screen.
00262              Carefully print the end of the line, and then compare. */
00263           if (*nodetext == '\n' || *nodetext == '\r' || *nodetext == '\t')
00264             {
00265               printed_line[pl_index] = '\0';
00266               rep_carried_over = (char *)NULL;
00267             }
00268           else
00269             {
00270               /* The printed representation of this character extends into
00271                  the next line.  Remember the offset of the last character
00272                  printed out of REP so that we can carry the character over
00273                  to the next line. */
00274               for (i = 0; pl_index < (win->width + pl_ignore - 1);)
00275                 printed_line[pl_index++] = rep[i++];
00276 
00277               rep_carried_over = rep + i;
00278 
00279               /* If printing the last character in this window couldn't
00280                  possibly cause the screen to scroll, place a backslash
00281                  in the rightmost column. */
00282               if (1 + line_index + win->first_row < the_screen->height)
00283                 {
00284                   if (win->flags & W_NoWrap)
00285                     printed_line[pl_index++] = '$';
00286                   else
00287                     printed_line[pl_index++] = '\\';
00288                 }
00289               printed_line[pl_index] = '\0';
00290             }
00291 
00292           /* We have the exact line as it should appear on the screen.
00293              Check to see if this line matches the one already appearing
00294              on the screen. */
00295           entry = display[line_index + win->first_row];
00296 
00297           /* If the screen line is inversed, then we have to clear
00298              the line from the screen first.  Why, I don't know.
00299              (But don't do this if we have no visible entries, as can
00300              happen if the window is shrunk very small.)  */
00301           if ((entry && entry->inverse)
00302              /* Need to erase the line if it has escape sequences.  */
00303              || (raw_escapes_p && strchr (entry->text, '\033') != 0))
00304             {
00305               terminal_goto_xy (0, line_index + win->first_row);
00306               terminal_clear_to_eol ();
00307               entry->inverse = 0;
00308               entry->text[0] = '\0';
00309               entry->textlen = 0;
00310             }
00311 
00312           /* Find the offset where these lines differ. */
00313           for (i = 0; i < pl_index; i++)
00314             if (printed_line[i] != entry->text[i])
00315               break;
00316 
00317           /* If the lines are not the same length, or if they differed
00318              at all, we must do some redrawing. */
00319           if ((i != pl_index) || (pl_index != entry->textlen))
00320             {
00321               /* Move to the proper point on the terminal. */
00322               terminal_goto_xy (i, line_index + win->first_row);
00323 
00324               /* If there is any text to print, print it. */
00325               if (i != pl_index)
00326                 terminal_put_text (printed_line + i);
00327 
00328               /* If the printed text didn't extend all the way to the edge
00329                  of the window, and text was appearing between here and the
00330                  edge of the window, clear from here to the end of the line. */
00331               if ((pl_index < win->width + pl_ignore
00332                  && pl_index < entry->textlen)
00333                 || (entry->inverse))
00334                 terminal_clear_to_eol ();
00335 
00336               fflush (stdout);
00337 
00338               /* Update the display text buffer. */
00339              if (strlen (printed_line) > (unsigned int) screenwidth)
00340               /* printed_line[] can include more than screenwidth
00341                  characters if we are under -R and there are escape
00342                  sequences in it.  However, entry->text was
00343                  allocated (in display_initialize_display) for
00344                  screenwidth characters only.  */
00345               entry->text = xrealloc (entry->text, strlen (printed_line)+1);
00346               strcpy (entry->text + i, printed_line + i);
00347               entry->textlen = pl_index;
00348 
00349               /* Lines showing node text are not in inverse.  Only modelines
00350                  have that distinction. */
00351               entry->inverse = 0;
00352             }
00353 
00354           /* We have done at least one line.  Increment our screen line
00355              index, and check against the bottom of the window. */
00356           if (++line_index == win->height)
00357             break;
00358 
00359           /* A line has been displayed, and the screen reflects that state.
00360              If there is typeahead pending, then let that typeahead be read
00361              now, instead of continuing with the display. */
00362           if (info_any_buffered_input_p ())
00363             {
00364               free (printed_line);
00365               display_was_interrupted_p = 1;
00366               return;
00367             }
00368 
00369           /* Reset PL_INDEX to the start of the line. */
00370           pl_index = 0;
00371          pl_ignore = 0;     /* this is computed per line */
00372 
00373           /* If there are characters from REP left to print, stuff them
00374              into the buffer now. */
00375           if (rep_carried_over)
00376             for (; rep[pl_index]; pl_index++)
00377               printed_line[pl_index] = rep[pl_index];
00378 
00379           /* If this window has chosen not to wrap lines, skip to the end
00380              of the physical line in the buffer, and start a new line here. */
00381           if (pl_index && (win->flags & W_NoWrap))
00382             {
00383               char *begin;
00384 
00385               pl_index = 0;
00386               printed_line[0] = '\0';
00387 
00388               begin = nodetext;
00389 
00390               while ((nodetext < last_node_char) && (*nodetext != '\n'))
00391                 nodetext++;
00392             }
00393         }
00394     }
00395 
00396  done_with_node_display:
00397   /* We have reached the end of the node or the end of the window.  If it
00398      is the end of the node, then clear the lines of the window from here
00399      to the end of the window. */
00400   for (; line_index < win->height; line_index++)
00401     {
00402       DISPLAY_LINE *entry = display[line_index + win->first_row];
00403 
00404       /* If this line has text on it then make it go away. */
00405       if (entry && entry->textlen)
00406         {
00407           entry->textlen = 0;
00408           entry->text[0] = '\0';
00409 
00410           terminal_goto_xy (0, line_index + win->first_row);
00411           terminal_clear_to_eol ();
00412         }
00413     }
00414 
00415   /* Finally, if this window has a modeline it might need to be redisplayed.
00416      Check the window's modeline against the one in the display, and update
00417      if necessary. */
00418   if ((win->flags & W_InhibitMode) == 0)
00419     {
00420       window_make_modeline (win);
00421       line_index = win->first_row + win->height;
00422 
00423       /* This display line must both be in inverse, and have the same
00424          contents. */
00425       if ((!display[line_index]->inverse) ||
00426           (strcmp (display[line_index]->text, win->modeline) != 0))
00427         {
00428           terminal_goto_xy (0, line_index);
00429           terminal_begin_inverse ();
00430           terminal_put_text (win->modeline);
00431           terminal_end_inverse ();
00432           strcpy (display[line_index]->text, win->modeline);
00433           display[line_index]->inverse = 1;
00434           display[line_index]->textlen = strlen (win->modeline);
00435           fflush (stdout);
00436         }
00437     }
00438 
00439   /* Okay, this window doesn't need updating anymore. */
00440   win->flags &= ~W_UpdateWindow;
00441   free (printed_line);
00442   fflush (stdout);
00443 }
00444 
00445 /* Scroll the region of the_display starting at START, ending at END, and
00446    moving the lines AMOUNT lines.  If AMOUNT is less than zero, the lines
00447    are moved up in the screen, otherwise down.  Actually, it is possible
00448    for no scrolling to take place in the case that the terminal doesn't
00449    support it.  This doesn't matter to us. */
00450 void
00451 display_scroll_display (int start, int end, int amount)
00452 {
00453   register int i, last;
00454   DISPLAY_LINE *temp;
00455 
00456   /* If this terminal cannot do scrolling, give up now. */
00457   if (!terminal_can_scroll)
00458     return;
00459 
00460   /* If there isn't anything displayed on the screen because it is too
00461      small, quit now. */
00462   if (!the_display[0])
00463     return;
00464 
00465   /* If there is typeahead pending, then don't actually do any scrolling. */
00466   if (info_any_buffered_input_p ())
00467     return;
00468 
00469   /* Do it on the screen. */
00470   terminal_scroll_terminal (start, end, amount);
00471 
00472   /* Now do it in the display buffer so our contents match the screen. */
00473   if (amount > 0)
00474     {
00475       last = end + amount;
00476 
00477       /* Shift the lines to scroll right into place. */
00478       for (i = 0; i < (end - start); i++)
00479         {
00480           temp = the_display[last - i];
00481           the_display[last - i] = the_display[end - i];
00482           the_display[end - i] = temp;
00483         }
00484 
00485       /* The lines have been shifted down in the buffer.  Clear all of the
00486          lines that were vacated. */
00487       for (i = start; i != (start + amount); i++)
00488         {
00489           the_display[i]->text[0] = '\0';
00490           the_display[i]->textlen = 0;
00491           the_display[i]->inverse = 0;
00492         }
00493     }
00494 
00495   if (amount < 0)
00496     {
00497       last = start + amount;
00498       for (i = 0; i < (end - start); i++)
00499         {
00500           temp = the_display[last + i];
00501           the_display[last + i] = the_display[start + i];
00502           the_display[start + i] = temp;
00503         }
00504 
00505       /* The lines have been shifted up in the buffer.  Clear all of the
00506          lines that are left over. */
00507       for (i = end + amount; i != end; i++)
00508         {
00509           the_display[i]->text[0] = '\0';
00510           the_display[i]->textlen = 0;
00511           the_display[i]->inverse = 0;
00512         }
00513     }
00514 }
00515 
00516 /* Try to scroll lines in WINDOW.  OLD_PAGETOP is the pagetop of WINDOW before
00517    having had its line starts recalculated.  OLD_STARTS is the list of line
00518    starts that used to appear in this window.  OLD_COUNT is the number of lines
00519    that appear in the OLD_STARTS array. */
00520 void
00521 display_scroll_line_starts (WINDOW *window, int old_pagetop,
00522     char **old_starts, int old_count)
00523 {
00524   register int i, old, new;     /* Indices into the line starts arrays. */
00525   int last_new, last_old;       /* Index of the last visible line. */
00526   int old_first, new_first;     /* Index of the first changed line. */
00527   int unchanged_at_top = 0;
00528   int already_scrolled = 0;
00529 
00530   /* Locate the first line which was displayed on the old window. */
00531   old_first = old_pagetop;
00532   new_first = window->pagetop;
00533 
00534   /* Find the last line currently visible in this window. */
00535   last_new = window->pagetop + (window->height - 1);
00536   if (last_new > window->line_count)
00537     last_new = window->line_count - 1;
00538 
00539   /* Find the last line which used to be currently visible in this window. */
00540   last_old = old_pagetop + (window->height - 1);
00541   if (last_old > old_count)
00542     last_old = old_count - 1;
00543 
00544   for (old = old_first, new = new_first;
00545        old < last_old && new < last_new;
00546        old++, new++)
00547     if (old_starts[old] != window->line_starts[new])
00548       break;
00549     else
00550       unchanged_at_top++;
00551 
00552   /* Loop through the old lines looking for a match in the new lines. */
00553   for (old = old_first + unchanged_at_top; old < last_old; old++)
00554     {
00555       for (new = new_first; new < last_new; new++)
00556         if (old_starts[old] == window->line_starts[new])
00557           {
00558             /* Find the extent of the matching lines. */
00559             for (i = 0; (old + i) < last_old; i++)
00560               if (old_starts[old + i] != window->line_starts[new + i])
00561                 break;
00562 
00563             /* Scroll these lines if there are enough of them. */
00564             {
00565               int start, end, amount;
00566 
00567               start = (window->first_row
00568                        + ((old + already_scrolled) - old_pagetop));
00569               amount = new - (old + already_scrolled);
00570               end = window->first_row + window->height;
00571 
00572               /* If we are shifting the block of lines down, then the last
00573                  AMOUNT lines will become invisible.  Thus, don't bother
00574                  scrolling them. */
00575               if (amount > 0)
00576                 end -= amount;
00577 
00578               if ((end - start) > 0)
00579                 {
00580                   display_scroll_display (start, end, amount);
00581 
00582                   /* Some lines have been scrolled.  Simulate the scrolling
00583                      by offsetting the value of the old index. */
00584                   old += i;
00585                   already_scrolled += amount;
00586                 }
00587             }
00588           }
00589     }
00590 }
00591 
00592 /* Move the screen cursor to directly over the current character in WINDOW. */
00593 void
00594 display_cursor_at_point (WINDOW *window)
00595 {
00596   int vpos, hpos;
00597 
00598   vpos = window_line_of_point (window) - window->pagetop + window->first_row;
00599   hpos = window_get_cursor_column (window);
00600   terminal_goto_xy (hpos, vpos);
00601   fflush (stdout);
00602 }
00603 
00604 /* **************************************************************** */
00605 /*                                                                  */
00606 /*                   Functions Static to this File                  */
00607 /*                                                                  */
00608 /* **************************************************************** */
00609 
00610 /* Make a DISPLAY_LINE ** with width and height. */
00611 static DISPLAY_LINE **
00612 make_display (int width, int height)
00613 {
00614   register int i;
00615   DISPLAY_LINE **display;
00616 
00617   display = (DISPLAY_LINE **)xmalloc ((1 + height) * sizeof (DISPLAY_LINE *));
00618 
00619   for (i = 0; i < height; i++)
00620     {
00621       display[i] = (DISPLAY_LINE *)xmalloc (sizeof (DISPLAY_LINE));
00622       display[i]->text = (char *)xmalloc (1 + width);
00623       display[i]->textlen = 0;
00624       display[i]->inverse = 0;
00625     }
00626   display[i] = (DISPLAY_LINE *)NULL;
00627   return (display);
00628 }
00629 
00630 /* Free the storage allocated to DISPLAY. */
00631 static void
00632 free_display (DISPLAY_LINE **display)
00633 {
00634   register int i;
00635   register DISPLAY_LINE *display_line;
00636 
00637   if (!display)
00638     return;
00639 
00640   for (i = 0; (display_line = display[i]); i++)
00641     {
00642       free (display_line->text);
00643       free (display_line);
00644     }
00645   free (display);
00646 }