1 /* window.c -- windows in Info.
2 
3    Copyright 1993-2019 Free Software Foundation, Inc.
4 
5    This program is free software: you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation, either version 3 of the License, or
8    (at your option) any later version.
9 
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14 
15    You should have received a copy of the GNU General Public License
16    along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 
18    Originally written by Brian Fox.  */
19 
20 #include "info.h"
21 #include "session.h"
22 #include "display.h"
23 #include "info-utils.h"
24 #include "doc.h"
25 #include "tag.h"
26 #include "variables.h"
27 
28 /* The window which describes the screen. */
29 WINDOW *the_screen = NULL;
30 
31 /* The window which describes the echo area. */
32 WINDOW *the_echo_area = NULL;
33 
34 /* The list of windows in Info. */
35 WINDOW *windows = NULL;
36 
37 /* Pointer to the active window in WINDOW_LIST. */
38 WINDOW *active_window = NULL;
39 
40 /* The size of the echo area in Info.  It never changes, irregardless of the
41    size of the screen. */
42 #define ECHO_AREA_HEIGHT 1
43 
44 /* Show malformed multibyte sequences */
45 int show_malformed_multibyte_p = 0;
46 
47 /* Initalize the window system by creating THE_SCREEN and THE_ECHO_AREA.
48    Create the first window ever.
49    You pass the dimensions of the total screen size. */
50 void
window_initialize_windows(int width,int height)51 window_initialize_windows (int width, int height)
52 {
53   the_screen = xzalloc (sizeof (WINDOW));
54   the_echo_area = xzalloc (sizeof (WINDOW));
55   windows = xzalloc (sizeof (WINDOW));
56   active_window = windows;
57 
58   /* The active and echo_area windows are visible.
59      The echo_area is permanent.
60      The screen is permanent. */
61   active_window->flags = W_WindowVisible;
62   the_echo_area->flags = W_WindowIsPerm | W_InhibitMode | W_WindowVisible;
63   the_screen->flags    = W_WindowIsPerm;
64 
65   /* The height of the echo area never changes.  It is statically set right
66      here, and it must be at least 1 line for display.  The size of the
67      initial window cannot be the same size as the screen, since the screen
68      includes the echo area.  So, we make the height of the initial window
69      equal to the screen's displayable region minus the height of the echo
70      area. */
71   the_echo_area->height = ECHO_AREA_HEIGHT;
72   active_window->height = the_screen->height - 1 - the_echo_area->height;
73   window_new_screen_size (width, height);
74 }
75 
76 /* Given that the size of the screen has changed to WIDTH and HEIGHT
77    from whatever it was before (found in the_screen->height, ->width),
78    change the size (and possibly location) of each window in the screen.
79    If a window would become too small, call the function DELETER on it,
80    after deleting the window from our chain of windows.  If DELETER is NULL,
81    nothing extra is done.  The last window can never be deleted, but it can
82    become invisible. */
83 void
window_new_screen_size(int width,int height)84 window_new_screen_size (int width, int height)
85 {
86   register WINDOW *win, *first_win;
87   int delta_height, delta_each, delta_leftover;
88   int numwins;
89 
90   /* If no change, do nothing. */
91   if (width == the_screen->width && height == the_screen->height)
92     return;
93 
94   /* The screen has changed height and width. */
95   delta_height = height - the_screen->height;
96   the_screen->height = height;
97   the_screen->width = width;
98 
99   /* Set the start of the echo area. */
100   the_echo_area->first_row = height - the_echo_area->height;
101   the_echo_area->width = width;
102 
103   /* Count number of windows. */
104   numwins = 0;
105   for (win = windows; win; win = win->next)
106     numwins++;
107 
108   if (numwins == 0)
109     return; /* There is nothing to do. */
110 
111   /* Divide the change in height among the available windows. */
112   delta_each = delta_height / numwins;
113   delta_leftover = delta_height - (delta_each * numwins);
114 
115   /* See if some windows will need to be deleted.  This is the case if
116      the screen is getting smaller, and the available space divided by
117      the number of windows is less than WINDOW_MIN_SIZE.  In that case,
118      delete some windows and try again until there is either enough
119      space to divy up among the windows, or until there is only one
120      window left. */
121   while (height - 1 <= WINDOW_MIN_SIZE * numwins)
122     {
123       /* If only one window left, give up. */
124       if (!windows->next)
125         {
126           /* Keep track of the height so that when the screen gets bigger
127              again, it can be resized properly.  The -2 is for the window
128              information bar and the echo area. */
129           windows->height = height - 2;
130           windows->width = width;
131           free (windows->modeline);
132           windows->modeline = xmalloc (1 + width);
133           return;
134         }
135 
136       /* If we have some temporary windows, delete one of them. */
137       for (win = windows; win; win = win->next)
138         if (win->flags & W_TempWindow)
139           break;
140 
141       /* Otherwise, delete the first window, and try again. */
142       if (!win)
143         win = windows;
144 
145       forget_window_and_nodes (win);
146       window_delete_window (win);
147       numwins--;
148     }
149 
150   /* Alternate which window we start resizing at, to resize all
151      windows evenly. */
152     {
153       int first_win_num = the_screen->height % numwins;
154       int i;
155       first_win = windows;
156       for (i = 0; i < first_win_num; i++)
157         first_win = first_win->next;
158     }
159 
160   /* Change the height of each window in the chain by delta_each.  Change
161      the height of the last window in the chain by delta_each and by the
162      leftover amount of change.  Change the width of each window to be
163      WIDTH. */
164   win = first_win;
165   do
166     {
167       if ((win->width != width) && ((win->flags & W_InhibitMode) == 0))
168         {
169           win->width = width;
170           free (win->modeline);
171           win->modeline = xmalloc (1 + width);
172         }
173 
174       /* Don't resize a window to be smaller than one line. */
175       if (win->height + delta_each >= 1)
176         win->height += delta_each;
177       else
178         delta_leftover += delta_each;
179 
180       /* Try to use up the extra space. */
181       if (delta_leftover != 0 && win->height + delta_leftover >= 1)
182         {
183           win->height += delta_leftover;
184           delta_leftover = 0;
185         }
186       /* Go to next window, wrapping round to the start. */
187       win = win->next;
188       if (!win)
189         win = windows;
190     }
191   while (win != first_win);
192 
193   for (win = windows; win; win = win->next)
194     {
195       /* If this is not the first window in the chain, set the
196          first row of it by adding one to the location of the
197          previous window's modeline. */
198       if (win->prev)
199         win->first_row = (win->prev->first_row + win->prev->height) + 1;
200 
201       if (win->node)
202         {
203           free (win->line_starts);
204           free (win->log_line_no);
205           calculate_line_starts (win);
206         }
207 
208       win->flags |= W_UpdateWindow;
209     }
210 
211   /* If the screen got smaller, check over the windows just shrunk to
212      keep them within bounds.  Some of the windows may have gotten smaller
213      than WINDOW_MIN_HEIGHT in which case some of the other windows are
214      larger than the available display space in the screen.  Because of our
215      intial test above, we know that there is enough space for all of the
216      windows. */
217   if ((delta_each < 0) && ((windows->height != 0) && windows->next))
218     {
219       int avail;
220 
221       avail = the_screen->height - (numwins + the_echo_area->height);
222       win = windows;
223 
224       while (win)
225         {
226           if ((win->height < WINDOW_MIN_HEIGHT) ||
227               (win->height > avail))
228             {
229               WINDOW *lastwin = NULL;
230 
231               /* Split the space among the available windows. */
232               delta_each = avail / numwins;
233               delta_leftover = avail - (delta_each * numwins);
234 
235               for (win = windows; win; win = win->next)
236                 {
237                   lastwin = win;
238                   if (win->prev)
239                     win->first_row =
240                       (win->prev->first_row + win->prev->height) + 1;
241                   win->height = delta_each;
242                 }
243 
244               /* Give the leftover space (if any) to the last window. */
245               lastwin->height += delta_leftover;
246               break;
247             }
248           else
249             win = win->next;
250         }
251     }
252 
253   /* Make sure point is in displayed part of active window. */
254   window_adjust_pagetop (active_window);
255 
256   /* One more loop.  If any heights or widths have become negative,
257      set them to zero.  This can apparently happen with resizing down to
258      very small sizes.  Sadly, it is not apparent to me where in the
259      above calculations it goes wrong.  */
260   for (win = windows; win; win = win->next)
261     {
262       if (win->height < 0)
263         win->height = 0;
264 
265       if (win->width < 0)
266         win->width = 0;
267     }
268 }
269 
270 /* Make a new window by splitting an existing one. If the window could
271    not be made return a null pointer.  The active window is not changed .*/
272 WINDOW *
window_make_window(void)273 window_make_window (void)
274 {
275   WINDOW *window;
276 
277   /* If there isn't enough room to make another window, return now. */
278   if ((active_window->height / 2) < WINDOW_MIN_SIZE)
279     return NULL;
280 
281   /* Make and initialize the new window.
282      The fudging about with -1 and +1 is because the following window in the
283      chain cannot start at window->height, since that is where the modeline
284      for the previous window is displayed.  The inverse adjustment is made
285      in window_delete_window (). */
286   window = xzalloc (sizeof (WINDOW));
287   window->width = the_screen->width;
288   window->height = (active_window->height / 2) - 1;
289   window->first_row = active_window->first_row +
290     (active_window->height - window->height);
291   window->goal_column = -1;
292   memset (&window->line_map, 0, sizeof (window->line_map));
293   window->modeline = xmalloc (1 + window->width);
294   window->line_starts = NULL;
295   window->flags = W_UpdateWindow | W_WindowVisible;
296 
297   /* Adjust the height of the old active window. */
298   active_window->height -= (window->height + 1);
299   active_window->flags |= W_UpdateWindow;
300 
301   window_make_modeline (active_window);
302 
303   /* This window is just after the active one.  Which window is active is
304      not changed. */
305   window->prev = active_window;
306   window->next = active_window->next;
307   active_window->next = window;
308   if (window->next)
309     window->next->prev = window;
310   return window;
311 }
312 
313 /* These useful macros make it possible to read the code in
314    window_change_window_height (). */
315 #define grow_me_shrinking_next(me, next, diff) \
316   do { \
317     me->height += diff; \
318     next->height -= diff; \
319     next->first_row += diff; \
320   } while (0)
321 
322 #define grow_me_shrinking_prev(me, prev, diff) \
323   do { \
324     me->height += diff; \
325     prev->height -= diff; \
326     me->first_row -=diff; \
327   } while (0)
328 
329 #define shrink_me_growing_next(me, next, diff) \
330   do { \
331     me->height -= diff; \
332     next->height += diff; \
333     next->first_row -= diff; \
334   } while (0)
335 
336 #define shrink_me_growing_prev(me, prev, diff) \
337   do { \
338     me->height -= diff; \
339     prev->height += diff; \
340     me->first_row += diff; \
341   } while (0)
342 
343 /* Change the height of WINDOW by AMOUNT.  This also automagically adjusts
344    the previous and next windows in the chain.  If there is only one user
345    window, then no change takes place. */
346 void
window_change_window_height(WINDOW * window,int amount)347 window_change_window_height (WINDOW *window, int amount)
348 {
349   register WINDOW *win, *prev, *next;
350 
351   /* If there is only one window, or if the amount of change is zero,
352      return immediately. */
353   if (!windows->next || amount == 0)
354     return;
355 
356   /* Find this window in our chain. */
357   for (win = windows; win; win = win->next)
358     if (win == window)
359       break;
360 
361   /* If the window is isolated (i.e., doesn't appear in our window list,
362      then quit now. */
363   if (!win)
364     return;
365 
366   /* Change the height of this window by AMOUNT, if that is possible.
367      It can be impossible if there isn't enough available room on the
368      screen, or if the resultant window would be too small. */
369 
370     prev = window->prev;
371     next = window->next;
372 
373   /* WINDOW decreasing in size? */
374   if (amount < 0)
375     {
376       int abs_amount = -amount; /* It is easier to deal with this way. */
377 
378       /* If the resultant window would be too small, stop here. */
379       if ((window->height - abs_amount) < WINDOW_MIN_HEIGHT)
380         return;
381 
382       /* If we have two neighboring windows, choose the smaller one to get
383          larger. */
384       if (next && prev)
385         {
386           if (prev->height < next->height)
387             shrink_me_growing_prev (window, prev, abs_amount);
388           else
389             shrink_me_growing_next (window, next, abs_amount);
390         }
391       else if (next)
392         shrink_me_growing_next (window, next, abs_amount);
393       else
394         shrink_me_growing_prev (window, prev, abs_amount);
395     }
396 
397   /* WINDOW increasing in size? */
398   if (amount > 0)
399     {
400       int total_avail, next_avail = 0, prev_avail = 0;
401 
402       if (next)
403         next_avail = next->height - WINDOW_MIN_SIZE;
404 
405       if (prev)
406         prev_avail = prev->height - WINDOW_MIN_SIZE;
407 
408       total_avail = next_avail + prev_avail;
409 
410       /* If there isn't enough space available to grow this window, give up. */
411       if (amount > total_avail)
412         return;
413 
414       /* If there aren't two neighboring windows, or if one of the neighbors
415          is larger than the other one by at least AMOUNT, grow that one. */
416       if (next_avail - amount >= prev_avail)
417         grow_me_shrinking_next (window, next, amount);
418       else if (prev_avail - amount >= next_avail)
419         grow_me_shrinking_prev (window, prev, amount);
420       else
421         {
422           int change;
423 
424           /* This window has two neighbors.  They both must be shrunk in to
425              make enough space for WINDOW to grow.  Make them both the same
426              size. */
427           if (prev_avail > next_avail)
428             {
429               change = prev_avail - next_avail;
430               grow_me_shrinking_prev (window, prev, change);
431               amount -= change;
432             }
433           else
434             {
435               change = next_avail - prev_avail;
436               grow_me_shrinking_next (window, next, change);
437               amount -= change;
438             }
439 
440           /* Both neighbors are the same size.  Split the difference in
441              AMOUNT between them. */
442           while (amount)
443             {
444               window->height++;
445               amount--;
446 
447               /* Odd numbers grow next, even grow prev. */
448               if (amount & 1)
449                 {
450                   prev->height--;
451                   window->first_row--;
452                 }
453               else
454                 {
455                   next->height--;
456                   next->first_row++;
457                 }
458             }
459         }
460     }
461   if (prev)
462     prev->flags |= W_UpdateWindow;
463 
464   if (next)
465     next->flags |= W_UpdateWindow;
466 
467   window->flags |= W_UpdateWindow;
468 }
469 
470 /* Tile all of the windows currently displayed in the global variable
471    WINDOWS.  If argument STYLE is TILE_INTERNALS, tile windows displaying
472    internal nodes as well, otherwise do not change the height of such
473    windows. */
474 void
window_tile_windows(int style)475 window_tile_windows (int style)
476 {
477   WINDOW *win, *last_adjusted;
478   int numwins, avail, per_win_height, leftover;
479   int do_internals;
480 
481   numwins = avail = 0;
482   do_internals = (style == TILE_INTERNALS);
483 
484   for (win = windows; win; win = win->next)
485     if (do_internals || !win->node ||
486         (win->node->flags & N_IsInternal) == 0)
487       {
488         avail += win->height;
489         numwins++;
490       }
491 
492   if (numwins <= 1 || !the_screen->height)
493     return;
494 
495   /* Find the size for each window.  Divide the size of the usable portion
496      of the screen by the number of windows. */
497   per_win_height = avail / numwins;
498   leftover = avail - (per_win_height * numwins);
499 
500   last_adjusted = NULL;
501   for (win = windows; win; win = win->next)
502     {
503       if (do_internals || !win->node ||
504           (win->node->flags & N_IsInternal) == 0)
505         {
506           last_adjusted = win;
507           win->height = per_win_height;
508         }
509     }
510 
511   if (last_adjusted)
512     last_adjusted->height += leftover;
513 
514   /* Readjust the first_row of every window in the chain. */
515   for (win = windows; win; win = win->next)
516     {
517       if (win->prev)
518         win->first_row = win->prev->first_row + win->prev->height + 1;
519 
520       window_adjust_pagetop (win);
521       win->flags |= W_UpdateWindow;
522     }
523 }
524 
525 /* Toggle the state of line wrapping in WINDOW.  This can do a bit of fancy
526    redisplay. */
527 void
window_toggle_wrap(WINDOW * window)528 window_toggle_wrap (WINDOW *window)
529 {
530   if (window->flags & W_NoWrap)
531     window->flags &= ~W_NoWrap;
532   else
533     window->flags |= W_NoWrap;
534 
535   if (window != the_echo_area)
536     {
537       long *old_starts;
538       long *old_xlat;
539       int old_lines, old_pagetop;
540 
541       old_starts = window->line_starts;
542       old_xlat = window->log_line_no;
543       old_lines = window->line_count;
544       old_pagetop = window->pagetop;
545 
546       calculate_line_starts (window);
547 
548       /* Make sure that point appears within this window. */
549       window_adjust_pagetop (window);
550 
551       /* If the pagetop hasn't changed maybe we can do some scrolling now
552          to speed up the display.  Many of the line starts will be the same,
553          so scrolling here is a very good optimization.*/
554       if (old_pagetop == window->pagetop)
555         display_scroll_line_starts (window, old_pagetop,
556                                     old_starts, old_lines);
557       free (old_starts);
558       free (old_xlat);
559     }
560   window->flags |= W_UpdateWindow;
561 }
562 
563 /* Set WINDOW to display NODE. */
564 void
window_set_node_of_window(WINDOW * window,NODE * node)565 window_set_node_of_window (WINDOW *window, NODE *node)
566 {
567   window->node = node;
568   window->pagetop = 0;
569   window->point = 0;
570 
571   free (window->line_starts);
572   free (window->log_line_no);
573   calculate_line_starts (window);
574   window_compute_line_map (window);
575 
576   /* Clear displayed search matches if any. */
577   free_matches (&window->matches);
578 
579   window->flags |= W_UpdateWindow;
580   if (node)
581     {
582       /* The display_pos member is nonzero if we're displaying an anchor.  */
583       window->point = node ? node->display_pos : 0;
584       window_adjust_pagetop (window);
585     }
586   window_make_modeline (window);
587 }
588 
589 /* Delete WINDOW from the list of known windows.  If this window was the
590    active window, make the next window in the chain be the active window.
591    If the active window is the next or previous window, choose that window
592    as the recipient of the extra space.  Otherwise, prefer the next window.
593    Be aware that info_delete_window_internal (in session.c) should be called
594    instead if you need to remove the window from the info_windows list. */
595 void
window_delete_window(WINDOW * window)596 window_delete_window (WINDOW *window)
597 {
598   WINDOW *next, *prev, *window_to_fix;
599 
600   next = window->next;
601   prev = window->prev;
602 
603   /* You cannot delete the only window or a permanent window. */
604   if ((!next && !prev) || (window->flags & W_WindowIsPerm))
605     return;
606 
607   if (next)
608     next->prev = prev;
609 
610   if (!prev)
611     windows = next;
612   else
613     prev->next = next;
614 
615   free (window->line_starts);
616   free (window->log_line_no);
617   free (window->line_map.map);
618   free (window->modeline);
619   free_matches (&window->matches);
620   free (window->search_string);
621 
622   if (window == active_window)
623     {
624       WINDOW *new_active = 0;
625 
626       /* If there isn't a next window, then there must be a previous one,
627          since we cannot delete the last window.  If there is a next window,
628          prefer to use that as the active window.  Try to find an important
629          window to select, e.g. not a footnotes window. */
630       if (next)
631         {
632           new_active = next;
633           while ((new_active->flags & W_TempWindow) && new_active->next)
634             new_active = new_active->next;
635         }
636 
637       if ((!new_active || new_active->flags & W_TempWindow) && prev)
638         {
639           new_active = prev;
640           while ((new_active->flags & W_TempWindow) && new_active->prev)
641             new_active = new_active->prev;
642         }
643       active_window = new_active;
644     }
645 
646   if (next && active_window == next)
647     window_to_fix = next;
648   else if (prev && active_window == prev)
649     window_to_fix = prev;
650   else if (next)
651     window_to_fix = next;
652   else if (prev)
653     window_to_fix = prev;
654   else
655     window_to_fix = windows;
656 
657   if (window_to_fix->first_row > window->first_row)
658     {
659       int diff;
660 
661       /* Try to adjust the visible part of the node so that as little
662          text as possible has to move. */
663       diff = window_to_fix->first_row - window->first_row;
664       window_to_fix->first_row = window->first_row;
665 
666       window_to_fix->pagetop -= diff;
667       if (window_to_fix->pagetop < 0)
668         window_to_fix->pagetop = 0;
669     }
670 
671   /* The `+ 1' is to offset the difference between the first_row locations.
672      See the code in window_make_window (). */
673   window_to_fix->height += window->height + 1;
674   window_to_fix->flags |= W_UpdateWindow;
675 
676   free (window);
677 }
678 
679 /* For every window in CHAIN, set the flags member to have FLAG set. */
680 void
window_mark_chain(WINDOW * chain,int flag)681 window_mark_chain (WINDOW *chain, int flag)
682 {
683   register WINDOW *win;
684 
685   for (win = chain; win; win = win->next)
686     win->flags |= flag;
687 }
688 
689 /* For every window in CHAIN, clear the flags member of FLAG. */
690 void
window_unmark_chain(WINDOW * chain,int flag)691 window_unmark_chain (WINDOW *chain, int flag)
692 {
693   register WINDOW *win;
694 
695   for (win = chain; win; win = win->next)
696     win->flags &= ~flag;
697 }
698 
699 /* Return the number of first physical line corresponding to the logical
700    line LN.
701 
702    A logical line can occupy one or more physical lines of output.  It
703    occupies more than one physical line if its width is greater than the
704    window width and the flag W_NoWrap is not set for that window.
705  */
706 long
window_log_to_phys_line(WINDOW * window,long ln)707 window_log_to_phys_line (WINDOW *window, long ln)
708 {
709   size_t i;
710 
711   if (ln > window->line_count)
712     return 0;
713   for (i = ln; i < window->line_count && window->log_line_no[i] < ln; i++)
714     ;
715   return i;
716 }
717 
718 /* Change the pagetop of WINDOW to DESIRED_TOP, perhaps scrolling the screen
719    to do so.  WINDOW->pagetop should be the currently displayed pagetop. */
720 void
set_window_pagetop(WINDOW * window,int desired_top)721 set_window_pagetop (WINDOW *window, int desired_top)
722 {
723   int point_line, old_pagetop;
724 
725   if (desired_top < 0)
726     desired_top = 0;
727   else if (desired_top > window->line_count)
728     desired_top = window->line_count - 1;
729 
730   if (window->pagetop == desired_top)
731     return;
732 
733   old_pagetop = window->pagetop;
734   window->pagetop = desired_top;
735 
736   /* Make sure that point appears in this window. */
737   point_line = window_line_of_point (window);
738   if (point_line < window->pagetop)
739     {
740       window->point = window->line_starts[window->pagetop];
741       window->goal_column = 0;
742     }
743   else if (point_line >= window->pagetop + window->height)
744     {
745       long bottom = window->pagetop + window->height - 1;
746       window->point = window->line_starts[bottom];
747       window->goal_column = 0;
748     }
749 
750   window->flags |= W_UpdateWindow;
751 
752   /* Find out which direction to scroll, and scroll the window in that
753      direction.  Do this only if there would be a savings in redisplay
754      time.  This is true if the amount to scroll is less than the height
755      of the window, and if the number of lines scrolled would be greater
756      than 10 % of the window's height.
757 
758      To prevent status line blinking when keeping up or down key,
759      scrolling is disabled if the amount to scroll is 1. */
760   if (old_pagetop < desired_top)
761     {
762       int start, end, amount;
763 
764       amount = desired_top - old_pagetop;
765 
766       if (amount == 1 ||
767           (amount >= window->height) ||
768           (((window->height - amount) * 10) < window->height))
769         return;
770 
771       start = window->first_row;
772       end = window->height + window->first_row;
773 
774       display_scroll_display (start, end, -amount);
775     }
776   else
777     {
778       int start, end, amount;
779 
780       amount = old_pagetop - desired_top;
781 
782       if (amount == 1 ||
783           (amount >= window->height) ||
784           (((window->height - amount) * 10) < window->height))
785         return;
786 
787       start = window->first_row;
788       end = window->first_row + window->height;
789       display_scroll_display (start, end, amount);
790     }
791 }
792 
793 /* Adjust the pagetop of WINDOW such that the cursor point will be visible. */
794 void
window_adjust_pagetop(WINDOW * window)795 window_adjust_pagetop (WINDOW *window)
796 {
797   register int line;
798 
799   if (!window->node)
800     return;
801 
802   line = window_line_of_point (window);
803 
804   /* If this line appears in the current displayable page, do nothing.
805      Otherwise, adjust the top of the page to make this line visible. */
806   if (line < window->pagetop
807       || line - window->pagetop > window->height - 1)
808     {
809       int new_pagetop = line - ((window->height - 1) / 2);
810 
811       if (new_pagetop < 0)
812         new_pagetop = 0;
813       set_window_pagetop (window, new_pagetop);
814     }
815 }
816 
817 /* Return the index of the line containing point. */
818 int
window_line_of_point(WINDOW * window)819 window_line_of_point (WINDOW *window)
820 {
821   register int i, start = 0;
822 
823   if (!window->line_starts)
824     calculate_line_starts (window);
825 
826   /* Check if point is past the pagetop for this window, and if so, start
827      searching forward from there. */
828   if (window->pagetop > -1 && window->pagetop < window->line_count
829       && window->line_starts[window->pagetop] <= window->point)
830     start = window->pagetop;
831 
832   for (i = start; i < window->line_count; i++)
833     {
834       if (window->line_starts[i] > window->point)
835         break;
836     }
837 
838   if (i > 0)
839     return i - 1;
840   else
841     return 0; /* Shouldn't happen */
842 }
843 
844 /* Get and return the printed column offset of the cursor in this window. */
845 int
window_get_cursor_column(WINDOW * window)846 window_get_cursor_column (WINDOW *window)
847 {
848   return window_point_to_column (window, window->point, &window->point);
849 }
850 
851 /* Create a modeline for WINDOW, and store it in window->modeline. */
852 void
window_make_modeline(WINDOW * window)853 window_make_modeline (WINDOW *window)
854 {
855   register int i;
856   char *modeline;
857   char location_indicator[4];
858   int lines_remaining;
859 
860   /* Only make modelines for those windows which have one. */
861   if (window->flags & W_InhibitMode)
862     return;
863 
864   /* Find the number of lines actually displayed in this window. */
865   lines_remaining = window->line_count - window->pagetop;
866 
867   if (window->pagetop == 0)
868     {
869       if (lines_remaining <= window->height)
870         strcpy (location_indicator, "All");
871       else
872         strcpy (location_indicator, "Top");
873     }
874   else
875     {
876       if (lines_remaining <= window->height)
877         strcpy (location_indicator, "Bot");
878       else
879         {
880           float pt, lc;
881           int percentage;
882 
883           pt = (float)window->pagetop;
884           lc = (float)(window->line_count - window->height);
885 
886           percentage = 100 * (pt / lc);
887 
888           sprintf (location_indicator, "%2d%%", percentage);
889         }
890     }
891 
892   /* Calculate the maximum size of the information to stick in MODELINE. */
893   {
894     int modeline_len = 0;
895     char *nodename = "*no node*";
896     NODE *node = window->node;
897     char *name;
898     int dot;
899 
900     if (node && node->nodename)
901       nodename = node->nodename;
902 
903     name = filename_non_directory (node->fullpath);
904 
905     /* 10 for the decimal representation of the number of lines in this
906        node, and the remainder of the text that can appear in the line. */
907     modeline_len += 10 + strlen (_("-----Info: (), lines ----, "));
908     modeline_len += 3; /* strlen (location_indicator) */
909     modeline_len += strlen (name);
910     if (nodename)
911       modeline_len += strlen (nodename);
912     if (modeline_len < window->width)
913       modeline_len = window->width;
914 
915     modeline = xcalloc (1, 1 + modeline_len);
916 
917     sprintf (modeline + strlen (modeline), "-----Info: ");
918 
919     /* Omit any extension like ".info.gz" from file name. */
920     dot = strcspn (name, ".");
921 
922     if (name && strcmp ("", name))
923       {
924         sprintf (modeline + strlen (modeline), "(");
925         strncpy (modeline + strlen (modeline), name, dot);
926         sprintf (modeline + strlen (modeline), ")");
927       }
928     sprintf (modeline + strlen (modeline),
929              "%s, %ld lines --%s",
930              nodename, window->line_count, location_indicator);
931 
932     i = strlen (modeline);
933 
934     if (i >= window->width)
935       modeline[window->width] = '\0';
936     else
937       {
938         while (i < window->width)
939           modeline[i++] = '-';
940         modeline[i] = '\0';
941       }
942 
943     strcpy (window->modeline, modeline);
944     free (modeline);
945   }
946 }
947 
948 /* Make WINDOW start displaying at PERCENT percentage of its node. */
949 void
window_goto_percentage(WINDOW * window,int percent)950 window_goto_percentage (WINDOW *window, int percent)
951 {
952   int desired_line;
953 
954   if (!percent)
955     desired_line = 0;
956   else
957     desired_line =
958       (int) ((float)window->line_count * ((float)percent / 100.0));
959 
960   window->pagetop = desired_line;
961   window->point =
962     window->line_starts[window->pagetop];
963   window->flags |= W_UpdateWindow;
964   window_make_modeline (window);
965 }
966 
967 
968 /* A place to buffer echo area messages. */
969 static NODE *echo_area_node = NULL;
970 
971 /* Make the node of the_echo_area be an empty one. */
972 void
free_echo_area(void)973 free_echo_area (void)
974 {
975   if (echo_area_node)
976     {
977       free (echo_area_node->contents);
978       free (echo_area_node);
979     }
980 
981   echo_area_node = NULL;
982   window_set_node_of_window (the_echo_area, echo_area_node);
983 }
984 
985 /* Clear the echo area, removing any message that is already present.
986    The echo area is cleared immediately. */
987 void
window_clear_echo_area(void)988 window_clear_echo_area (void)
989 {
990   free_echo_area ();
991   display_update_one_window (the_echo_area);
992 }
993 
994 void
vwindow_message_in_echo_area(const char * format,va_list ap)995 vwindow_message_in_echo_area (const char *format, va_list ap)
996 {
997   free_echo_area ();
998   echo_area_node = build_message_node (format, ap);
999   window_set_node_of_window (the_echo_area, echo_area_node);
1000   display_update_one_window (the_echo_area);
1001 }
1002 
1003 /* Make a message appear in the echo area, built from FORMAT, ARG1 and ARG2.
1004    The arguments are treated similar to printf () arguments, but not all of
1005    printf () hair is present.  The message appears immediately.  If there was
1006    already a message appearing in the echo area, it is removed. */
1007 void
window_message_in_echo_area(const char * format,...)1008 window_message_in_echo_area (const char *format, ...)
1009 {
1010   va_list ap;
1011 
1012   va_start (ap, format);
1013   vwindow_message_in_echo_area (format, ap);
1014   va_end (ap);
1015 }
1016 
1017 /* Place a temporary message in the echo area built from FORMAT, ARG1
1018    and ARG2.  The message appears immediately, but does not destroy
1019    any existing message.  A future call to unmessage_in_echo_area ()
1020    restores the old contents. */
1021 static NODE **old_echo_area_nodes = NULL;
1022 static size_t old_echo_area_nodes_index = 0;
1023 static size_t old_echo_area_nodes_slots = 0;
1024 
1025 void
message_in_echo_area(const char * format,...)1026 message_in_echo_area (const char *format, ...)
1027 {
1028   va_list ap;
1029 
1030   if (echo_area_node)
1031     {
1032       add_pointer_to_array (echo_area_node, old_echo_area_nodes_index,
1033                             old_echo_area_nodes, old_echo_area_nodes_slots,
1034                             4);
1035     }
1036   echo_area_node = NULL;
1037   va_start (ap, format);
1038   vwindow_message_in_echo_area (format, ap);
1039   va_end (ap);
1040 }
1041 
1042 void
unmessage_in_echo_area(void)1043 unmessage_in_echo_area (void)
1044 {
1045   free_echo_area ();
1046 
1047   if (old_echo_area_nodes_index)
1048     echo_area_node = old_echo_area_nodes[--old_echo_area_nodes_index];
1049 
1050   window_set_node_of_window (the_echo_area, echo_area_node);
1051   display_update_one_window (the_echo_area);
1052 }
1053 
1054 
1055 /* Build a new node which has FORMAT printed with ARG1 and ARG2 as the
1056    contents. */
1057 NODE *
build_message_node(const char * format,va_list ap)1058 build_message_node (const char *format, va_list ap)
1059 {
1060   struct text_buffer msg;
1061 
1062   text_buffer_init (&msg);
1063   text_buffer_vprintf (&msg, format, ap);
1064 
1065   return text_buffer_to_node (&msg);
1066 }
1067 
1068 NODE *
format_message_node(const char * format,...)1069 format_message_node (const char *format, ...)
1070 {
1071   NODE *node;
1072   va_list ap;
1073 
1074   va_start (ap, format);
1075   node = build_message_node (format, ap);
1076   va_end (ap);
1077   return node;
1078 }
1079 
1080 NODE *
text_buffer_to_node(struct text_buffer * tb)1081 text_buffer_to_node (struct text_buffer *tb)
1082 {
1083   NODE *node;
1084 
1085   node = info_create_node ();
1086 
1087   /* Make sure that this buffer ends with a newline. */
1088   text_buffer_add_char (tb, '\n');
1089   node->nodelen = text_buffer_off (tb);
1090   text_buffer_add_char (tb, '\0');
1091 
1092   node->contents = text_buffer_base (tb);
1093   node->flags |= N_IsInternal;
1094   return node;
1095 }
1096 
1097 /* Used by calculate_line_starts to record line starts in the
1098    win->LINE_COUNT and win->LOG_LINE_NO arrays. */
1099 static void
collect_line_starts(WINDOW * win,long ll_num,long pl_start)1100 collect_line_starts (WINDOW *win, long ll_num, long pl_start)
1101 {
1102   add_element_to_array (pl_start, win->line_count,
1103                         win->line_starts, win->line_slots, 2);
1104 
1105   /* We cannot do add_element_to_array for this, as this would lead
1106      to incrementing cp->win->line_count twice. */
1107   win->log_line_no = xrealloc (win->log_line_no,
1108                                win->line_slots * sizeof (long));
1109   win->log_line_no[win->line_count - 1] = ll_num;
1110 }
1111 
1112 #define NO_NODELINE 0
1113 #define PRINT_NODELINE 1
1114 #define NODELINE_POINTERS_ONLY 2
1115 int nodeline_print = 2;
1116 
1117 /* Calculate a list of line starts for the node belonging to WINDOW.  The
1118    line starts are offsets within WINDOW->node->contents.
1119 
1120    Note that this function must agree with what display_update_one_window
1121    in display.c does. */
1122 void
calculate_line_starts(WINDOW * win)1123 calculate_line_starts (WINDOW *win)
1124 {
1125   long pl_chars = 0;     /* Number of characters in line so far. */
1126   long pl_start;         /* Offset of start of current physical line. */
1127   long ll_num = 0;       /* Number of logical lines */
1128   mbi_iterator_t iter;
1129 
1130   /* Width of character carried over from one physical line to the next.  */
1131   size_t carried_over_chars = 0;
1132 
1133   win->line_starts = NULL;
1134   win->log_line_no = NULL;
1135   win->line_count = 0;
1136   win->line_slots = 0;
1137 
1138   if (!win->node)
1139     return;
1140 
1141   pl_start = 0;
1142   if (nodeline_print != PRINT_NODELINE
1143       && !memcmp (win->node->contents, "File:", strlen ("File:")))
1144     {
1145       char *s = strchr (win->node->contents, '\n');
1146       if (s && nodeline_print == NO_NODELINE)
1147         {
1148           pl_start = s - win->node->contents + 1;
1149         }
1150       else if (s && nodeline_print == NODELINE_POINTERS_ONLY)
1151         {
1152           char *s2;
1153           char saved = *s;
1154           *s = '\0';
1155           s2 = strstr (win->node->contents, "Next: ");
1156           if (!s2)
1157             s2 = strstr (win->node->contents, "Prev: ");
1158           if (!s2)
1159             s2 = strstr (win->node->contents, "Up: ");
1160           if (s2)
1161             pl_start = s2 - win->node->contents;
1162           *s = saved;
1163         }
1164     }
1165 
1166   for (mbi_init (iter,
1167                  win->node->contents + pl_start,
1168                  win->node->nodelen - pl_start);
1169        mbi_avail (iter);
1170        mbi_advance (iter))
1171     {
1172       size_t pchars = 0; /* Screen columns for this character. */
1173       size_t pbytes = 0; /* Not used. */
1174       int delim = 0;
1175 
1176       /* Set pchars. */
1177       (void) printed_representation (&iter, &delim, pl_chars,
1178                                      &pchars, &pbytes);
1179 
1180       /* If this character can be printed without passing the width of
1181          the line, then include it in the line. */
1182       if (!delim && pl_chars + pchars < win->width)
1183         {
1184           pl_chars += pchars;
1185           continue;
1186         }
1187 
1188       /* If this character cannot be printed in this line, we have
1189          found the end of this line as it would appear on the screen. */
1190 
1191       carried_over_chars = delim ? 0 : pchars;
1192 
1193       collect_line_starts (win, ll_num, pl_start);
1194 
1195       if (delim == '\r' || delim == '\n')
1196         ++ll_num;
1197 
1198       /* Start a new physical line at next character, unless a character
1199          was carried over, in which case start there. */
1200       pl_start = mbi_cur_ptr (iter) - win->node->contents;
1201       if (carried_over_chars == 0)
1202         pl_start += mb_len (mbi_cur (iter));
1203       pl_chars = 0;
1204 
1205       /* If there is a character carried over, count it now.  Expected to be
1206          "short", i.e. a representation like "^A". */
1207       if (carried_over_chars != 0)
1208         {
1209           pl_chars = carried_over_chars;
1210 
1211           /* If this window has chosen not to wrap lines, skip to the end
1212              of the logical line in the buffer, and start a new line here. */
1213           if (win->flags & W_NoWrap)
1214             {
1215               for (; mbi_avail (iter); mbi_advance (iter))
1216                 if (mb_len (mbi_cur (iter)) == 1
1217                     && *mbi_cur_ptr (iter) == '\n')
1218                   break;
1219 
1220               pl_chars = 0;
1221               pl_start = mbi_cur_ptr (iter) + mb_len (mbi_cur (iter))
1222                          - win->node->contents;
1223             }
1224         }
1225     }
1226 
1227   if (pl_chars)
1228     collect_line_starts (win, ll_num++, pl_start);
1229 
1230   /* Have one line start at the end of the node. */
1231   collect_line_starts (win, ll_num, mbi_cur_ptr (iter) - win->node->contents);
1232   win->line_count--;
1233 
1234   /* Finally, initialize the line map for the current line. */
1235   window_line_map_init (win);
1236 }
1237 
1238 
1239 static void
line_map_init(LINE_MAP * map,NODE * node,int line)1240 line_map_init (LINE_MAP *map, NODE *node, int line)
1241 {
1242   map->node = node;
1243   map->nline = line;
1244   map->used = 0;
1245 }
1246 
1247 static void
line_map_add(LINE_MAP * map,long pos)1248 line_map_add (LINE_MAP *map, long pos)
1249 {
1250   if (map->used == map->size)
1251     {
1252       if (map->size == 0)
1253 	map->size = 80; /* Initial allocation */
1254       map->map = x2nrealloc (map->map,
1255 			     &map->size,
1256 			     sizeof (map->map[0]));
1257     }
1258 
1259   map->map[map->used++] = pos;
1260 }
1261 
1262 /* Initialize (clear) WIN's line map. */
1263 void
window_line_map_init(WINDOW * win)1264 window_line_map_init (WINDOW *win)
1265 {
1266   win->line_map.used = 0;
1267 }
1268 
1269 /* Compute the line map for the current line in WIN. */
1270 void
window_compute_line_map(WINDOW * win)1271 window_compute_line_map (WINDOW *win)
1272 {
1273   int line = window_line_of_point (win);
1274   mbi_iterator_t iter;
1275   int delim = 0;
1276   char *endp;
1277   const char *cur_ptr;
1278 
1279   if (win->line_map.node == win->node && win->line_map.nline == line
1280       && win->line_map.used)
1281     return;
1282   line_map_init (&win->line_map, win->node, line);
1283   if (!win->node)
1284     return;
1285 
1286   if (line + 1 < win->line_count)
1287     endp = win->node->contents + win->line_starts[line + 1];
1288   else
1289     endp = win->node->contents + win->node->nodelen;
1290 
1291   for (mbi_init (iter,
1292 		 win->node->contents + win->line_starts[line],
1293 		 win->node->nodelen - win->line_starts[line]);
1294        !delim && mbi_avail (iter);
1295        mbi_advance (iter))
1296     {
1297       size_t pchars, pbytes;
1298       cur_ptr = mbi_cur_ptr (iter);
1299 
1300       if (cur_ptr >= endp)
1301 	break;
1302 
1303       /* Set pchars */
1304       (void) printed_representation (&iter, &delim, win->line_map.used,
1305                                      &pchars, &pbytes);
1306 
1307       while (pchars--)
1308         line_map_add (&win->line_map, cur_ptr - win->node->contents);
1309     }
1310 }
1311 
1312 /* Translate the value of POINT into a column number.  If NP is given
1313    store there the value of point corresponding to the beginning of a
1314    multibyte character in this column.  If the character at POINT spans
1315    multiple columns (e.g. a tab), return the leftmost column it occupies. */
1316 int
window_point_to_column(WINDOW * win,long point,long * np)1317 window_point_to_column (WINDOW *win, long point, long *np)
1318 {
1319   int i;
1320 
1321   window_compute_line_map (win);
1322   if (!win->line_map.map || point < win->line_map.map[0])
1323     return 0;
1324   for (i = 0; i < win->line_map.used; i++)
1325     if (win->line_map.map[i] >= point)
1326       break;
1327   if (np)
1328     *np = win->line_map.map[i];
1329   return i;
1330 }
1331 
1332