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