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