Annotation of src/usr.bin/vim/window.c, Revision 1.1.1.1
1.1 downsj 1: /* $OpenBSD$ */
2: /* vi:set ts=4 sw=4:
3: *
4: * VIM - Vi IMproved by Bram Moolenaar
5: *
6: * Do ":help uganda" in Vim to read a list of people who contributed.
7: * Do ":help credits" in Vim to see a list of people who contributed.
8: */
9:
10: #include "vim.h"
11: #include "globals.h"
12: #include "proto.h"
13: #include "option.h"
14:
15: static void reset_VIsual __ARGS((void));
16: static int win_comp_pos __ARGS((void));
17: static void win_exchange __ARGS((long));
18: static void win_rotate __ARGS((int, int));
19: static void win_append __ARGS((WIN *, WIN *));
20: static void win_remove __ARGS((WIN *));
21: static void win_new_height __ARGS((WIN *, int));
22:
23: static WIN *prevwin = NULL; /* previous window */
24:
25: /*
26: * all CTRL-W window commands are handled here, called from normal().
27: */
28: void
29: do_window(nchar, Prenum)
30: int nchar;
31: long Prenum;
32: {
33: long Prenum1;
34: WIN *wp;
35: char_u *ptr;
36: int len;
37: int type = -1;
38: WIN *wp2;
39:
40: if (Prenum == 0)
41: Prenum1 = 1;
42: else
43: Prenum1 = Prenum;
44:
45: switch (nchar)
46: {
47: /* split current window in two parts */
48: case 'S':
49: case Ctrl('S'):
50: case 's': reset_VIsual(); /* stop Visual mode */
51: win_split((int)Prenum, TRUE);
52: break;
53:
54: /* split current window and edit alternate file */
55: case K_CCIRCM:
56: case '^':
57: reset_VIsual(); /* stop Visual mode */
58: stuffReadbuff((char_u *)":split #");
59: if (Prenum)
60: stuffnumReadbuff(Prenum); /* buffer number */
61: stuffcharReadbuff('\n');
62: break;
63:
64: /* open new window */
65: case Ctrl('N'):
66: case 'n': reset_VIsual(); /* stop Visual mode */
67: stuffcharReadbuff(':');
68: if (Prenum)
69: stuffnumReadbuff(Prenum); /* window height */
70: stuffReadbuff((char_u *)"new\n"); /* it is cmdline.c */
71: break;
72:
73: /* quit current window */
74: case Ctrl('Q'):
75: case 'q': reset_VIsual(); /* stop Visual mode */
76: stuffReadbuff((char_u *)":quit\n"); /* it is cmdline.c */
77: break;
78:
79: /* close current window */
80: case Ctrl('C'):
81: case 'c': reset_VIsual(); /* stop Visual mode */
82: stuffReadbuff((char_u *)":close\n"); /* it is cmdline.c */
83: break;
84:
85: /* close all but current window */
86: case Ctrl('O'):
87: case 'o': reset_VIsual(); /* stop Visual mode */
88: stuffReadbuff((char_u *)":only\n"); /* it is cmdline.c */
89: break;
90:
91: /* cursor to next window */
92: case 'j':
93: case K_DOWN:
94: case Ctrl('J'):
95: for (wp = curwin; wp->w_next != NULL && Prenum1-- > 0;
96: wp = wp->w_next)
97: ;
98: new_win:
99: /*
100: * When jumping to another buffer, stop visual mode
101: * Do this before changing windows so we can yank the
102: * selection into the '"*' register.
103: */
104: if (wp->w_buffer != curbuf && VIsual_active)
105: {
106: end_visual_mode();
107: for (wp2 = firstwin; wp2 != NULL; wp2 = wp2->w_next)
108: if (wp2->w_buffer == curbuf &&
109: wp2->w_redr_type < NOT_VALID)
110: {
111: wp2->w_redr_type = NOT_VALID;
112: redraw_later(NOT_VALID);
113: }
114: }
115: win_enter(wp, TRUE);
116: cursupdate();
117: break;
118:
119: /* cursor to next window with wrap around */
120: case Ctrl('W'):
121: case 'w':
122: /* cursor to previous window with wrap around */
123: case 'W':
124: if (lastwin == firstwin) /* just one window */
125: beep_flush();
126: else
127: {
128: if (Prenum) /* go to specified window */
129: {
130: for (wp = firstwin; --Prenum > 0; )
131: {
132: if (wp->w_next == NULL)
133: break;
134: else
135: wp = wp->w_next;
136: }
137: }
138: else
139: {
140: if (nchar == 'W') /* go to previous window */
141: {
142: wp = curwin->w_prev;
143: if (wp == NULL)
144: wp = lastwin; /* wrap around */
145: }
146: else /* go to next window */
147: {
148: wp = curwin->w_next;
149: if (wp == NULL)
150: wp = firstwin; /* wrap around */
151: }
152: }
153: goto new_win;
154: }
155: break;
156:
157: /* cursor to window above */
158: case 'k':
159: case K_UP:
160: case Ctrl('K'):
161: for (wp = curwin; wp->w_prev != NULL && Prenum1-- > 0;
162: wp = wp->w_prev)
163: ;
164: goto new_win;
165:
166: /* cursor to top window */
167: case 't':
168: case Ctrl('T'):
169: wp = firstwin;
170: goto new_win;
171:
172: /* cursor to bottom window */
173: case 'b':
174: case Ctrl('B'):
175: wp = lastwin;
176: goto new_win;
177:
178: /* cursor to last accessed (previous) window */
179: case 'p':
180: case Ctrl('P'):
181: if (prevwin == NULL)
182: beep_flush();
183: else
184: {
185: wp = prevwin;
186: goto new_win;
187: }
188: break;
189:
190: /* exchange current and next window */
191: case 'x':
192: case Ctrl('X'):
193: win_exchange(Prenum);
194: break;
195:
196: /* rotate windows downwards */
197: case Ctrl('R'):
198: case 'r': reset_VIsual(); /* stop Visual mode */
199: win_rotate(FALSE, (int)Prenum1); /* downwards */
200: break;
201:
202: /* rotate windows upwards */
203: case 'R': reset_VIsual(); /* stop Visual mode */
204: win_rotate(TRUE, (int)Prenum1); /* upwards */
205: break;
206:
207: /* make all windows the same height */
208: case '=': win_equal(NULL, TRUE);
209: break;
210:
211: /* increase current window height */
212: case '+': win_setheight(curwin->w_height + (int)Prenum1);
213: break;
214:
215: /* decrease current window height */
216: case '-': win_setheight(curwin->w_height - (int)Prenum1);
217: break;
218:
219: /* set current window height */
220: case Ctrl('_'):
221: case '_': win_setheight(Prenum ? (int)Prenum : 9999);
222: break;
223:
224: /* jump to tag and split window if tag exists */
225: case ']':
226: case Ctrl(']'):
227: reset_VIsual(); /* stop Visual mode */
228: postponed_split = TRUE;
229: stuffcharReadbuff(Ctrl(']'));
230: break;
231:
232: /* edit file name under cursor in a new window */
233: case 'f':
234: case Ctrl('F'):
235: reset_VIsual(); /* stop Visual mode */
236: ptr = file_name_at_cursor(FNAME_MESS|FNAME_HYP|FNAME_EXP);
237: if (ptr != NULL)
238: {
239: setpcmark();
240: if (win_split(0, FALSE) == OK)
241: (void)do_ecmd(0, ptr, NULL, NULL, p_hid, (linenr_t)0, FALSE);
242: vim_free(ptr);
243: }
244: break;
245:
246: /* Go to the first occurence of the identifier under cursor along path in a
247: * new window -- webb
248: */
249: case 'i': /* Go to any match */
250: case Ctrl('I'):
251: type = FIND_ANY;
252: /* FALLTHROUGH */
253: case 'd': /* Go to definition, using p_def */
254: case Ctrl('D'):
255: if (type == -1)
256: type = FIND_DEFINE;
257:
258: if ((len = find_ident_under_cursor(&ptr, FIND_IDENT)) == 0)
259: break;
260: find_pattern_in_path(ptr, len, TRUE, TRUE, type,
261: Prenum1, ACTION_SPLIT, (linenr_t)1, (linenr_t)MAXLNUM);
262: curwin->w_set_curswant = TRUE;
263: break;
264:
265: default: beep_flush();
266: break;
267: }
268: }
269:
270: static void
271: reset_VIsual()
272: {
273: if (VIsual_active)
274: {
275: end_visual_mode();
276: update_curbuf(NOT_VALID); /* delete the inversion */
277: }
278: }
279:
280: /*
281: * split the current window, implements CTRL-W s and :split
282: *
283: * new_height is the height for the new window, 0 to make half of current
284: * height redraw is TRUE when redraw now
285: *
286: * return FAIL for failure, OK otherwise
287: */
288: int
289: win_split(new_height, redraw)
290: int new_height;
291: int redraw;
292: {
293: WIN *wp;
294: linenr_t lnum;
295: int h;
296: int i;
297: int need_status;
298: int do_equal = (p_ea && new_height == 0);
299: int needed;
300: int available;
301: int curwin_height;
302:
303: /* add a status line when p_ls == 1 and splitting the first window */
304: if (lastwin == firstwin && p_ls == 1 && curwin->w_status_height == 0)
305: need_status = STATUS_HEIGHT;
306: else
307: need_status = 0;
308:
309: /*
310: * check if we are able to split the current window and compute its height
311: */
312: available = curwin->w_height;
313: needed = 2 * MIN_ROWS + STATUS_HEIGHT + need_status;
314: if (p_ea)
315: {
316: for (wp = firstwin; wp != NULL; wp = wp->w_next)
317: if (wp != curwin)
318: {
319: available += wp->w_height;
320: needed += MIN_ROWS;
321: }
322: }
323: if (available < needed)
324: {
325: EMSG(e_noroom);
326: return FAIL;
327: }
328: curwin_height = curwin->w_height;
329: if (need_status)
330: {
331: curwin->w_status_height = STATUS_HEIGHT;
332: curwin_height -= STATUS_HEIGHT;
333: }
334: if (new_height == 0)
335: new_height = curwin_height / 2;
336:
337: if (new_height > curwin_height - MIN_ROWS - STATUS_HEIGHT)
338: new_height = curwin_height - MIN_ROWS - STATUS_HEIGHT;
339:
340: if (new_height < MIN_ROWS)
341: new_height = MIN_ROWS;
342:
343: /* if it doesn't fit in the current window, need win_equal() */
344: if (curwin_height - new_height - STATUS_HEIGHT < MIN_ROWS)
345: do_equal = TRUE;
346: /*
347: * allocate new window structure and link it in the window list
348: */
349: if (p_sb) /* new window below current one */
350: wp = win_alloc(curwin);
351: else
352: wp = win_alloc(curwin->w_prev);
353: if (wp == NULL)
354: return FAIL;
355: /*
356: * compute the new screen positions
357: */
358: win_new_height(wp, new_height);
359: win_new_height(curwin, curwin_height - (new_height + STATUS_HEIGHT));
360: if (p_sb) /* new window below current one */
361: {
362: wp->w_winpos = curwin->w_winpos + curwin->w_height + STATUS_HEIGHT;
363: wp->w_status_height = curwin->w_status_height;
364: curwin->w_status_height = STATUS_HEIGHT;
365: }
366: else /* new window above current one */
367: {
368: wp->w_winpos = curwin->w_winpos;
369: wp->w_status_height = STATUS_HEIGHT;
370: curwin->w_winpos = wp->w_winpos + wp->w_height + STATUS_HEIGHT;
371: }
372: /*
373: * make the contents of the new window the same as the current one
374: */
375: wp->w_buffer = curbuf;
376: curbuf->b_nwindows++;
377: wp->w_cursor = curwin->w_cursor;
378: wp->w_row = curwin->w_row;
379: wp->w_col = curwin->w_col;
380: wp->w_virtcol = curwin->w_virtcol;
381: wp->w_curswant = curwin->w_curswant;
382: wp->w_set_curswant = curwin->w_set_curswant;
383: wp->w_empty_rows = curwin->w_empty_rows;
384: wp->w_leftcol = curwin->w_leftcol;
385: wp->w_pcmark = curwin->w_pcmark;
386: wp->w_prev_pcmark = curwin->w_prev_pcmark;
387: wp->w_alt_fnum = curwin->w_alt_fnum;
388:
389: wp->w_arg_idx = curwin->w_arg_idx;
390: /*
391: * copy tagstack and options from existing window
392: */
393: for (i = 0; i < curwin->w_tagstacklen; i++)
394: {
395: wp->w_tagstack[i].fmark = curwin->w_tagstack[i].fmark;
396: wp->w_tagstack[i].tagname = strsave(curwin->w_tagstack[i].tagname);
397: }
398: wp->w_tagstackidx = curwin->w_tagstackidx;
399: wp->w_tagstacklen = curwin->w_tagstacklen;
400: win_copy_options(curwin, wp);
401: /*
402: * Both windows need redrawing
403: */
404: wp->w_redr_type = NOT_VALID;
405: wp->w_redr_status = TRUE;
406: curwin->w_redr_type = NOT_VALID;
407: curwin->w_redr_status = TRUE;
408: /*
409: * Cursor is put in middle of window in both windows
410: */
411: if (wp->w_height < curwin->w_height) /* use smallest of two heights */
412: h = wp->w_height;
413: else
414: h = curwin->w_height;
415: h >>= 1;
416: for (lnum = wp->w_cursor.lnum; lnum > 1; --lnum)
417: {
418: h -= plines(lnum);
419: if (h <= 0)
420: break;
421: }
422: wp->w_topline = lnum;
423: curwin->w_topline = lnum;
424: if (need_status)
425: {
426: msg_pos((int)Rows - 1, sc_col);
427: msg_clr_eos(); /* Old command/ruler may still be there -- webb */
428: comp_col();
429: msg_pos((int)Rows - 1, 0); /* put position back at start of line */
430: }
431: /*
432: * make the new window the current window and redraw
433: */
434: if (do_equal)
435: win_equal(wp, FALSE);
436: win_enter(wp, FALSE);
437:
438: if (redraw)
439: updateScreen(NOT_VALID);
440:
441: return OK;
442: }
443:
444: /*
445: * Return the number of windows.
446: */
447: int
448: win_count()
449: {
450: WIN *wp;
451: int count = 0;
452:
453: for (wp = firstwin; wp != NULL; wp = wp->w_next)
454: ++count;
455: return count;
456: }
457:
458: /*
459: * Make 'count' windows on the screen.
460: * Return actual number of windows on the screen.
461: * Must be called when there is just one window, filling the whole screen
462: * (excluding the command line).
463: */
464: int
465: make_windows(count)
466: int count;
467: {
468: int maxcount;
469: int todo;
470: int p_sb_save;
471:
472: /*
473: * Each window needs at least MIN_ROWS lines and a status line.
474: * Add 4 lines for one window, otherwise we may end up with all one-line
475: * windows. Use value of 'winheight' if it is set
476: */
477: maxcount = (curwin->w_height + curwin->w_status_height -
478: (p_wh ? (p_wh - 1) : 4)) / (MIN_ROWS + STATUS_HEIGHT);
479: if (maxcount < 2)
480: maxcount = 2;
481: if (count > maxcount)
482: count = maxcount;
483:
484: /*
485: * add status line now, otherwise first window will be too big
486: */
487: if ((p_ls == 2 || (count > 1 && p_ls == 1)) && curwin->w_status_height == 0)
488: {
489: curwin->w_status_height = STATUS_HEIGHT;
490: win_new_height(curwin, curwin->w_height - STATUS_HEIGHT);
491: }
492:
493: /*
494: * set 'splitbelow' off for a moment, don't want that now
495: */
496: p_sb_save = p_sb;
497: p_sb = FALSE;
498: /* todo is number of windows left to create */
499: for (todo = count - 1; todo > 0; --todo)
500: if (win_split(curwin->w_height - (curwin->w_height - todo
501: * STATUS_HEIGHT) / (todo + 1) - STATUS_HEIGHT, FALSE) == FAIL)
502: break;
503: p_sb = p_sb_save;
504:
505: /* return actual number of windows */
506: return (count - todo);
507: }
508:
509: /*
510: * Exchange current and next window
511: */
512: static void
513: win_exchange(Prenum)
514: long Prenum;
515: {
516: WIN *wp;
517: WIN *wp2;
518: int temp;
519:
520: if (lastwin == firstwin) /* just one window */
521: {
522: beep_flush();
523: return;
524: }
525:
526: /*
527: * find window to exchange with
528: */
529: if (Prenum)
530: {
531: wp = firstwin;
532: while (wp != NULL && --Prenum > 0)
533: wp = wp->w_next;
534: }
535: else if (curwin->w_next != NULL) /* Swap with next */
536: wp = curwin->w_next;
537: else /* Swap last window with previous */
538: wp = curwin->w_prev;
539:
540: if (wp == curwin || wp == NULL)
541: return;
542:
543: /*
544: * 1. remove curwin from the list. Remember after which window it was in wp2
545: * 2. insert curwin before wp in the list
546: * if wp != wp2
547: * 3. remove wp from the list
548: * 4. insert wp after wp2
549: * 5. exchange the status line height
550: */
551: wp2 = curwin->w_prev;
552: win_remove(curwin);
553: win_append(wp->w_prev, curwin);
554: if (wp != wp2)
555: {
556: win_remove(wp);
557: win_append(wp2, wp);
558: }
559: temp = curwin->w_status_height;
560: curwin->w_status_height = wp->w_status_height;
561: wp->w_status_height = temp;
562:
563: win_comp_pos(); /* recompute window positions */
564:
565: win_enter(wp, TRUE);
566: cursupdate();
567: updateScreen(CLEAR);
568:
569: #ifdef USE_GUI
570: if (gui.in_use)
571: {
572: if (gui.which_scrollbars[SB_LEFT])
573: gui_mch_reorder_scrollbars(SB_LEFT);
574: if (gui.which_scrollbars[SB_RIGHT])
575: gui_mch_reorder_scrollbars(SB_RIGHT);
576: }
577: #endif
578: }
579:
580: /*
581: * rotate windows: if upwards TRUE the second window becomes the first one
582: * if upwards FALSE the first window becomes the second one
583: */
584: static void
585: win_rotate(upwards, count)
586: int upwards;
587: int count;
588: {
589: WIN *wp;
590: int height;
591:
592: if (firstwin == lastwin) /* nothing to do */
593: {
594: beep_flush();
595: return;
596: }
597: while (count--)
598: {
599: if (upwards) /* first window becomes last window */
600: {
601: wp = firstwin;
602: win_remove(wp);
603: win_append(lastwin, wp);
604: wp = lastwin->w_prev; /* previously last window */
605: }
606: else /* last window becomes first window */
607: {
608: wp = lastwin;
609: win_remove(lastwin);
610: win_append(NULL, wp);
611: wp = firstwin; /* previously last window */
612: }
613: /* exchange status height of old and new last window */
614: height = lastwin->w_status_height;
615: lastwin->w_status_height = wp->w_status_height;
616: wp->w_status_height = height;
617:
618: /* recompute w_winpos for all windows */
619: (void) win_comp_pos();
620: }
621:
622: cursupdate();
623: updateScreen(CLEAR);
624:
625: #ifdef USE_GUI
626: if (gui.in_use)
627: {
628: if (gui.which_scrollbars[SB_LEFT])
629: gui_mch_reorder_scrollbars(SB_LEFT);
630: if (gui.which_scrollbars[SB_RIGHT])
631: gui_mch_reorder_scrollbars(SB_RIGHT);
632: }
633: #endif
634: }
635:
636: /*
637: * Make all windows the same height.
638: * 'next_curwin' will soon be the current window, make sure it has enough
639: * rows.
640: */
641: void
642: win_equal(next_curwin, redraw)
643: WIN *next_curwin; /* pointer to current window to be */
644: int redraw;
645: {
646: int total;
647: int less;
648: int wincount;
649: int winpos;
650: int temp;
651: WIN *wp;
652: int new_height;
653:
654: /*
655: * count the number of lines available
656: */
657: total = 0;
658: wincount = 0;
659: for (wp = firstwin; wp; wp = wp->w_next)
660: {
661: total += wp->w_height - MIN_ROWS;
662: wincount++;
663: }
664:
665: /*
666: * If next_curwin given and 'winheight' set, make next_curwin p_wh lines.
667: */
668: less = 0;
669: if (next_curwin != NULL)
670: {
671: if (p_wh)
672: {
673: if (p_wh - MIN_ROWS > total) /* all lines go to current window */
674: less = total;
675: else
676: {
677: less = p_wh - MIN_ROWS - total / wincount;
678: if (less < 0)
679: less = 0;
680: }
681: }
682: }
683:
684: /*
685: * spread the available lines over the windows
686: */
687: winpos = 0;
688: for (wp = firstwin; wp != NULL; wp = wp->w_next)
689: {
690: if (wp == next_curwin && less)
691: {
692: less = 0;
693: temp = p_wh - MIN_ROWS;
694: if (temp > total)
695: temp = total;
696: }
697: else
698: temp = (total - less + (wincount >> 1)) / wincount;
699: new_height = MIN_ROWS + temp;
700: if (wp->w_winpos != winpos || wp->w_height != new_height)
701: {
702: wp->w_redr_type = NOT_VALID;
703: wp->w_redr_status = TRUE;
704: }
705: wp->w_winpos = winpos;
706: win_new_height(wp, new_height);
707: total -= temp;
708: --wincount;
709: winpos += wp->w_height + wp->w_status_height;
710: }
711: if (redraw)
712: {
713: cursupdate();
714: updateScreen(CLEAR);
715: }
716: }
717:
718: /*
719: * close all windows for buffer 'buf'
720: */
721: void
722: close_windows(buf)
723: BUF *buf;
724: {
725: WIN *win;
726:
727: ++RedrawingDisabled;
728: for (win = firstwin; win != NULL && lastwin != firstwin; )
729: {
730: if (win->w_buffer == buf)
731: {
732: close_window(win, FALSE);
733: win = firstwin; /* go back to the start */
734: }
735: else
736: win = win->w_next;
737: }
738: --RedrawingDisabled;
739: }
740:
741: /*
742: * close window "win"
743: * If "free_buf" is TRUE related buffer may be freed.
744: *
745: * called by :quit, :close, :xit, :wq and findtag()
746: */
747: void
748: close_window(win, free_buf)
749: WIN *win;
750: int free_buf;
751: {
752: WIN *wp;
753:
754: if (lastwin == firstwin)
755: {
756: EMSG("Cannot close last window");
757: return;
758: }
759:
760: /*
761: * Remove the window.
762: * if 'splitbelow' the free space goes to the window above it.
763: * if 'nosplitbelow' the free space goes to the window below it.
764: * This makes opening a window and closing it immediately keep the same window
765: * layout.
766: */
767: /* freed space goes to next window */
768: if ((!p_sb && win->w_next != NULL) || win->w_prev == NULL)
769: {
770: wp = win->w_next;
771: wp->w_winpos = win->w_winpos;
772: }
773: else /* freed space goes to previous window */
774: wp = win->w_prev;
775: win_new_height(wp, wp->w_height + win->w_height + win->w_status_height);
776:
777: #ifdef AUTOCMD
778: if (win == curwin)
779: {
780: if (wp->w_buffer != curbuf)
781: apply_autocmds(EVENT_BUFLEAVE, NULL, NULL);
782: apply_autocmds(EVENT_WINLEAVE, NULL, NULL);
783: }
784: #endif
785:
786: /*
787: * Close the link to the buffer.
788: */
789: close_buffer(win, win->w_buffer, free_buf, FALSE);
790:
791: win_free(win);
792: if (win == curwin)
793: curwin = NULL;
794: if (p_ea)
795: win_equal(wp, FALSE);
796: if (curwin == NULL)
797: win_enter(wp, FALSE);
798: /*
799: * if last window has status line now and we don't want one,
800: * remove the status line
801: */
802: if (lastwin->w_status_height &&
803: (p_ls == 0 || (p_ls == 1 && firstwin == lastwin)))
804: {
805: win_new_height(lastwin, lastwin->w_height + lastwin->w_status_height);
806: lastwin->w_status_height = 0;
807: comp_col();
808: }
809:
810: updateScreen(NOT_VALID);
811: if (RedrawingDisabled)
812: comp_Botline(wp); /* need to do this before cursupdate() */
813: }
814:
815: /*
816: * close all windows except current one
817: * buffers in the windows become hidden
818: *
819: * called by :only and do_arg_all();
820: */
821: void
822: close_others(message)
823: int message;
824: {
825: WIN *wp;
826: WIN *nextwp;
827:
828: if (lastwin == firstwin)
829: {
830: if (message
831: #ifdef AUTOCMD
832: && !autocmd_busy
833: #endif
834: )
835: MSG("Already only one window");
836: return;
837: }
838:
839: for (wp = firstwin; wp != NULL; wp = nextwp)
840: {
841: nextwp = wp->w_next;
842: if (wp == curwin) /* don't close current window */
843: continue;
844: /*
845: * Close the link to the buffer.
846: */
847: close_buffer(wp, wp->w_buffer, FALSE, FALSE);
848:
849: /*
850: * Remove the window. All lines go to current window.
851: */
852: win_new_height(curwin,
853: curwin->w_height + wp->w_height + wp->w_status_height);
854: win_free(wp);
855: }
856:
857: /*
858: * If current window has status line and we don't want one,
859: * remove the status line.
860: */
861: if (curwin->w_status_height && p_ls != 2)
862: {
863: win_new_height(curwin, curwin->w_height + curwin->w_status_height);
864: curwin->w_status_height = 0;
865: }
866: curwin->w_winpos = 0; /* put current window at top of the screen */
867: if (message)
868: updateScreen(NOT_VALID);
869: }
870:
871: /*
872: * init the cursor in the window
873: *
874: * called when a new file is being edited
875: */
876: void
877: win_init(wp)
878: WIN *wp;
879: {
880: wp->w_redr_type = NOT_VALID;
881: wp->w_cursor.lnum = 1;
882: wp->w_curswant = wp->w_cursor.col = 0;
883: wp->w_pcmark.lnum = 1; /* pcmark not cleared but set to line 1 */
884: wp->w_pcmark.col = 0;
885: wp->w_prev_pcmark.lnum = 0;
886: wp->w_prev_pcmark.col = 0;
887: wp->w_topline = 1;
888: wp->w_botline = 2;
889: }
890:
891: /*
892: * make window wp the current window
893: */
894: void
895: win_enter(wp, undo_sync)
896: WIN *wp;
897: int undo_sync;
898: {
899: #ifdef AUTOCMD
900: int other_buffer = FALSE;
901: #endif
902:
903: if (wp == curwin) /* nothing to do */
904: return;
905:
906: #ifdef AUTOCMD
907: if (curwin != NULL)
908: {
909: if (wp->w_buffer != curbuf)
910: {
911: apply_autocmds(EVENT_BUFLEAVE, NULL, NULL);
912: other_buffer = TRUE;
913: }
914: apply_autocmds(EVENT_WINLEAVE, NULL, NULL);
915: }
916: #endif
917:
918: /* sync undo before leaving the current buffer */
919: if (undo_sync && curbuf != wp->w_buffer)
920: u_sync();
921: /* may have to copy the buffer options when 'cpo' contains 'S' */
922: if (wp->w_buffer != curbuf)
923: buf_copy_options(curbuf, wp->w_buffer, TRUE);
924: if (curwin != NULL)
925: prevwin = curwin; /* remember for CTRL-W p */
926: curwin = wp;
927: curbuf = wp->w_buffer;
928:
929: #ifdef AUTOCMD
930: apply_autocmds(EVENT_WINENTER, NULL, NULL);
931: if (other_buffer)
932: apply_autocmds(EVENT_BUFENTER, NULL, NULL);
933: #endif
934:
935: maketitle();
936: /* set window height to desired minimal value */
937: if (p_wh && curwin->w_height < p_wh)
938: win_setheight((int)p_wh);
939: #ifdef USE_MOUSE
940: setmouse(); /* in case jumped to/from help buffer */
941: #endif
942: }
943:
944: /*
945: * allocate a window structure and link it in the window list
946: */
947: WIN *
948: win_alloc(after)
949: WIN *after;
950: {
951: WIN *newwin;
952:
953: /*
954: * allocate window structure and linesizes arrays
955: */
956: newwin = (WIN *)alloc((unsigned)sizeof(WIN));
957: if (newwin)
958: {
959: /*
960: * most stucture members have to be zero
961: */
962: (void)vim_memset(newwin, 0, sizeof(WIN));
963: /*
964: * link the window in the window list
965: */
966: win_append(after, newwin);
967:
968: win_alloc_lsize(newwin);
969:
970: /* position the display and the cursor at the top of the file. */
971: newwin->w_topline = 1;
972: newwin->w_botline = 2;
973: newwin->w_cursor.lnum = 1;
974:
975: #ifdef USE_GUI
976: /* Let the GUI know this is a new scrollbar */
977: newwin->w_scrollbar.height = 0;
978: #endif /* USE_GUI */
979: }
980: return newwin;
981: }
982:
983: /*
984: * remove window 'wp' from the window list and free the structure
985: */
986: void
987: win_free(wp)
988: WIN *wp;
989: {
990: int i;
991:
992: if (prevwin == wp)
993: prevwin = NULL;
994: win_free_lsize(wp);
995:
996: for (i = 0; i < wp->w_tagstacklen; ++i)
997: free(wp->w_tagstack[i].tagname);
998:
999: #ifdef USE_GUI
1000: if (gui.in_use)
1001: gui_mch_destroy_scrollbar(wp);
1002: #endif /* USE_GUI */
1003:
1004: win_remove(wp);
1005: vim_free(wp);
1006: }
1007:
1008: static void
1009: win_append(after, wp)
1010: WIN *after, *wp;
1011: {
1012: WIN *before;
1013:
1014: if (after == NULL) /* after NULL is in front of the first */
1015: before = firstwin;
1016: else
1017: before = after->w_next;
1018:
1019: wp->w_next = before;
1020: wp->w_prev = after;
1021: if (after == NULL)
1022: firstwin = wp;
1023: else
1024: after->w_next = wp;
1025: if (before == NULL)
1026: lastwin = wp;
1027: else
1028: before->w_prev = wp;
1029: }
1030:
1031: /*
1032: * remove window from the window list
1033: */
1034: static void
1035: win_remove(wp)
1036: WIN *wp;
1037: {
1038: if (wp->w_prev)
1039: wp->w_prev->w_next = wp->w_next;
1040: else
1041: firstwin = wp->w_next;
1042: if (wp->w_next)
1043: wp->w_next->w_prev = wp->w_prev;
1044: else
1045: lastwin = wp->w_prev;
1046: }
1047:
1048: /*
1049: * allocate lsize arrays for a window
1050: * return FAIL for failure, OK for success
1051: */
1052: int
1053: win_alloc_lsize(wp)
1054: WIN *wp;
1055: {
1056: wp->w_lsize_valid = 0;
1057: wp->w_lsize_lnum = (linenr_t *) malloc((size_t) (Rows * sizeof(linenr_t)));
1058: wp->w_lsize = (char_u *)malloc((size_t) Rows);
1059: if (wp->w_lsize_lnum == NULL || wp->w_lsize == NULL)
1060: {
1061: win_free_lsize(wp); /* one of the two may have worked */
1062: wp->w_lsize_lnum = NULL;
1063: wp->w_lsize = NULL;
1064: return FAIL;
1065: }
1066: return OK;
1067: }
1068:
1069: /*
1070: * free lsize arrays for a window
1071: */
1072: void
1073: win_free_lsize(wp)
1074: WIN *wp;
1075: {
1076: vim_free(wp->w_lsize_lnum);
1077: vim_free(wp->w_lsize);
1078: }
1079:
1080: /*
1081: * call this fuction whenever Rows changes value
1082: */
1083: void
1084: screen_new_rows()
1085: {
1086: WIN *wp;
1087: int extra_lines;
1088:
1089: if (firstwin == NULL) /* not initialized yet */
1090: return;
1091: /*
1092: * the number of extra lines is the difference between the position where
1093: * the command line should be and where it is now
1094: */
1095: compute_cmdrow();
1096: extra_lines = Rows - p_ch - cmdline_row;
1097: if (extra_lines < 0) /* reduce windows height */
1098: {
1099: for (wp = lastwin; wp; wp = wp->w_prev)
1100: {
1101: if (wp->w_height - MIN_ROWS < -extra_lines)
1102: {
1103: extra_lines += wp->w_height - MIN_ROWS;
1104: win_new_height(wp, MIN_ROWS);
1105: }
1106: else
1107: {
1108: win_new_height(wp, wp->w_height + extra_lines);
1109: break;
1110: }
1111: }
1112: (void)win_comp_pos(); /* compute w_winpos */
1113: }
1114: else if (extra_lines > 0) /* increase height of last window */
1115: win_new_height(lastwin, lastwin->w_height + extra_lines);
1116:
1117: compute_cmdrow();
1118:
1119: if (p_ea)
1120: win_equal(curwin, FALSE);
1121: }
1122:
1123: /*
1124: * update the w_winpos field for all windows
1125: * returns the row just after the last window
1126: */
1127: static int
1128: win_comp_pos()
1129: {
1130: WIN *wp;
1131: int row;
1132:
1133: row = 0;
1134: for (wp = firstwin; wp != NULL; wp = wp->w_next)
1135: {
1136: if (wp->w_winpos != row) /* if position changes, redraw */
1137: {
1138: wp->w_winpos = row;
1139: wp->w_redr_type = NOT_VALID;
1140: wp->w_redr_status = TRUE;
1141: }
1142: row += wp->w_height + wp->w_status_height;
1143: }
1144: return row;
1145: }
1146:
1147: /*
1148: * set current window height
1149: */
1150: void
1151: win_setheight(height)
1152: int height;
1153: {
1154: WIN *wp;
1155: int room; /* total number of lines available */
1156: int take; /* number of lines taken from other windows */
1157: int room_cmdline; /* lines available from cmdline */
1158: int row;
1159: int run;
1160:
1161: if (height < MIN_ROWS) /* need at least some lines */
1162: height = MIN_ROWS;
1163: /*
1164: * compute the room we have from all the windows
1165: */
1166: room = MIN_ROWS; /* count the MIN_ROWS for the current window */
1167: for (wp = firstwin; wp != NULL; wp = wp->w_next)
1168: room += wp->w_height - MIN_ROWS;
1169: /*
1170: * compute the room available from the command line
1171: */
1172: room_cmdline = Rows - p_ch - cmdline_row;
1173: /*
1174: * limit new height to the room available
1175: */
1176: if (height > room + room_cmdline) /* can't make it that large */
1177: height = room + room_cmdline; /* use all available room */
1178: /*
1179: * compute the number of lines we will take from the windows (can be negative)
1180: */
1181: take = height - curwin->w_height;
1182: if (take == 0) /* no change, nothing to do */
1183: return;
1184:
1185: if (take > 0)
1186: {
1187: take -= room_cmdline; /* use lines from cmdline first */
1188: if (take < 0)
1189: take = 0;
1190: }
1191: /*
1192: * set the current window to the new height
1193: */
1194: win_new_height(curwin, height);
1195:
1196: /*
1197: * First take lines from the windows below the current window.
1198: * If that is not enough, takes lines from windows above the current window.
1199: */
1200: for (run = 0; run < 2; ++run)
1201: {
1202: if (run == 0)
1203: wp = curwin->w_next; /* 1st run: start with next window */
1204: else
1205: wp = curwin->w_prev; /* 2nd run: start with prev window */
1206: while (wp != NULL && take != 0)
1207: {
1208: if (wp->w_height - take < MIN_ROWS)
1209: {
1210: take -= wp->w_height - MIN_ROWS;
1211: win_new_height(wp, MIN_ROWS);
1212: }
1213: else
1214: {
1215: win_new_height(wp, wp->w_height - take);
1216: take = 0;
1217: }
1218: if (run == 0)
1219: wp = wp->w_next;
1220: else
1221: wp = wp->w_prev;
1222: }
1223: }
1224:
1225: /* recompute the window positions */
1226: row = win_comp_pos();
1227:
1228: /*
1229: * If there is extra space created between the last window and the command line,
1230: * clear it.
1231: */
1232: screen_fill(row, cmdline_row, 0, (int)Columns, ' ', ' ');
1233: cmdline_row = row;
1234:
1235: updateScreen(NOT_VALID);
1236: }
1237:
1238: #ifdef USE_MOUSE
1239: void
1240: win_drag_status_line(offset)
1241: int offset;
1242: {
1243: WIN *wp;
1244: int room;
1245: int row;
1246: int up; /* if TRUE, drag status line up, otherwise down */
1247:
1248: if (offset < 0)
1249: {
1250: up = TRUE;
1251: offset = -offset;
1252: }
1253: else
1254: up = FALSE;
1255:
1256: if (up) /* drag up */
1257: {
1258: room = 0;
1259: for (wp = curwin; wp != NULL && room < offset; wp = wp->w_prev)
1260: room += wp->w_height - MIN_ROWS;
1261: wp = curwin->w_next; /* put wp at window that grows */
1262: }
1263: else /* drag down */
1264: {
1265: /*
1266: * Only dragging the last status line can reduce p_ch.
1267: */
1268: room = Rows - cmdline_row;
1269: if (curwin->w_next == NULL)
1270: room -= 1;
1271: else
1272: room -= p_ch;
1273: for (wp = curwin->w_next; wp != NULL && room < offset; wp = wp->w_next)
1274: room += wp->w_height - MIN_ROWS;
1275: wp = curwin; /* put wp at window that grows */
1276: }
1277:
1278: if (room < offset) /* Not enough room */
1279: offset = room; /* Move as far as we can */
1280: if (offset <= 0)
1281: return;
1282:
1283: if (wp != NULL) /* grow window wp by offset lines */
1284: win_new_height(wp, wp->w_height + offset);
1285:
1286: if (up)
1287: wp = curwin; /* current window gets smaller */
1288: else
1289: wp = curwin->w_next; /* next window gets smaller */
1290:
1291: while (wp != NULL && offset > 0)
1292: {
1293: if (wp->w_height - offset < MIN_ROWS)
1294: {
1295: offset -= wp->w_height - MIN_ROWS;
1296: win_new_height(wp, MIN_ROWS);
1297: }
1298: else
1299: {
1300: win_new_height(wp, wp->w_height - offset);
1301: offset = 0;
1302: }
1303: if (up)
1304: wp = wp->w_prev;
1305: else
1306: wp = wp->w_next;
1307: }
1308: row = win_comp_pos();
1309: screen_fill(row, cmdline_row, 0, (int)Columns, ' ', ' ');
1310: cmdline_row = row;
1311: p_ch = Rows - cmdline_row;
1312: updateScreen(NOT_VALID);
1313: showmode();
1314: }
1315: #endif /* USE_MOUSE */
1316:
1317: /*
1318: * Set new window height.
1319: */
1320: static void
1321: win_new_height(wp, height)
1322: WIN *wp;
1323: int height;
1324: {
1325: /* should adjust topline to keep cursor at same relative postition */
1326:
1327: wp->w_height = height;
1328: win_comp_scroll(wp);
1329: wp->w_redr_type = NOT_VALID;
1330: wp->w_redr_status = TRUE;
1331: }
1332:
1333: void
1334: win_comp_scroll(wp)
1335: WIN *wp;
1336: {
1337: wp->w_p_scroll = (wp->w_height >> 1);
1338: if (wp->w_p_scroll == 0)
1339: wp->w_p_scroll = 1;
1340: }
1341:
1342: /*
1343: * command_height: called whenever p_ch has been changed
1344: */
1345: void
1346: command_height()
1347: {
1348: int current;
1349:
1350: current = Rows - cmdline_row;
1351: if (p_ch > current) /* p_ch got bigger */
1352: {
1353: if (lastwin->w_height - (p_ch - current) < MIN_ROWS)
1354: {
1355: emsg(e_noroom);
1356: p_ch = lastwin->w_height - MIN_ROWS + current;
1357: }
1358: /* clear the lines added to cmdline */
1359: screen_fill((int)(Rows - p_ch), (int)Rows, 0, (int)Columns, ' ', ' ');
1360: }
1361: win_new_height(lastwin, lastwin->w_height + current - (int)p_ch);
1362: cmdline_row = Rows - p_ch;
1363: redraw_cmdline = TRUE;
1364: }
1365:
1366: void
1367: last_status()
1368: {
1369: if (lastwin->w_status_height)
1370: {
1371: /* remove status line */
1372: if (p_ls == 0 || (p_ls == 1 && firstwin == lastwin))
1373: {
1374: win_new_height(lastwin, lastwin->w_height + 1);
1375: lastwin->w_status_height = 0;
1376: }
1377: }
1378: else
1379: {
1380: /* add status line */
1381: if (p_ls == 2 || (p_ls == 1 && firstwin != lastwin))
1382: {
1383: if (lastwin->w_height <= MIN_ROWS) /* can't do it */
1384: emsg(e_noroom);
1385: else
1386: {
1387: win_new_height(lastwin, lastwin->w_height - 1);
1388: lastwin->w_status_height = 1;
1389: }
1390: }
1391: }
1392: }
1393:
1394: /*
1395: * file_name_at_cursor()
1396: *
1397: * Return the name of the file under (or to the right of) the cursor. The
1398: * p_path variable is searched if the file name does not start with '/'.
1399: * The string returned has been alloc'ed and should be freed by the caller.
1400: * NULL is returned if the file name or file is not found.
1401: */
1402: char_u *
1403: file_name_at_cursor(options)
1404: int options;
1405: {
1406: return get_file_name_in_path(ml_get_curline(),
1407: curwin->w_cursor.col, options);
1408: }
1409: /* options:
1410: * FNAME_MESS give error messages
1411: * FNAME_EXP expand to path
1412: * FNAME_HYP check for hypertext link
1413: */
1414: char_u *
1415: get_file_name_in_path(ptr, col, options)
1416: char_u *ptr;
1417: int col;
1418: int options;
1419: {
1420: char_u *dir;
1421: char_u *file_name;
1422: char_u save_char;
1423: char_u *curr_path = NULL;
1424: int curr_path_len;
1425: int len;
1426:
1427: /* search forward for what could be the start of a file name */
1428: while (ptr[col] != NUL && !isfilechar(ptr[col]))
1429: ++col;
1430: if (ptr[col] == NUL) /* nothing found */
1431: {
1432: if (options & FNAME_MESS)
1433: EMSG("No file name under cursor");
1434: return NULL;
1435: }
1436:
1437: /* search backward for char that cannot be in a file name */
1438: while (col >= 0 && isfilechar(ptr[col]))
1439: --col;
1440: ptr += col + 1;
1441: col = 0;
1442:
1443: /* search forward for a char that cannot be in a file name */
1444: while (isfilechar(ptr[col]))
1445: ++col;
1446:
1447: if (options & FNAME_HYP)
1448: {
1449: /* For hypertext links, ignore the name of the machine.
1450: * Such a link looks like "type://machine/path". Only "/path" is used.
1451: * First search for the string "://", then for the extra '/'
1452: */
1453: if ((file_name = vim_strchr(ptr, ':')) != NULL &&
1454: STRNCMP(file_name, "://", (size_t)3) == 0 &&
1455: (file_name = vim_strchr(file_name + 3, '/')) != NULL &&
1456: file_name < ptr + col)
1457: {
1458: col -= file_name - ptr;
1459: ptr = file_name;
1460: if (ptr[1] == '~') /* skip '/' for /~user/path */
1461: {
1462: ++ptr;
1463: --col;
1464: }
1465: }
1466: }
1467:
1468: if (!(options & FNAME_EXP))
1469: return strnsave(ptr, col);
1470:
1471: /* copy file name into NameBuff, expanding environment variables */
1472: save_char = ptr[col];
1473: ptr[col] = NUL;
1474: expand_env(ptr, NameBuff, MAXPATHL);
1475: ptr[col] = save_char;
1476:
1477: if (isFullName(NameBuff)) /* absolute path */
1478: {
1479: if ((file_name = strsave(NameBuff)) == NULL)
1480: return NULL;
1481: if (getperm(file_name) >= 0)
1482: return file_name;
1483: if (options & FNAME_MESS)
1484: {
1485: sprintf((char *)IObuff, "Can't find file `%s'", NameBuff);
1486: emsg(IObuff);
1487: }
1488: }
1489: else /* relative path, use 'path' option */
1490: {
1491: if (curbuf->b_xfilename != NULL)
1492: {
1493: curr_path = curbuf->b_xfilename;
1494: ptr = gettail(curr_path);
1495: curr_path_len = ptr - curr_path;
1496: }
1497: else
1498: curr_path_len = 0;
1499: if ((file_name = alloc((int)(curr_path_len + STRLEN(p_path) +
1500: STRLEN(NameBuff) + 3))) == NULL)
1501: return NULL;
1502:
1503: for (dir = p_path; *dir;)
1504: {
1505: len = copy_option_part(&dir, file_name, 31000, " ,");
1506: /* len == 0 means: use current directory */
1507: if (len != 0)
1508: {
1509: /* Look for file relative to current file */
1510: if (file_name[0] == '.' && curr_path_len > 0)
1511: {
1512: if (len == 1) /* just a "." */
1513: len = 0;
1514: else /* "./path": move "path" */
1515: {
1516: len -= 2;
1517: vim_memmove(file_name + curr_path_len, file_name + 2,
1518: (size_t)len);
1519: }
1520: STRNCPY(file_name, curr_path, curr_path_len);
1521: len += curr_path_len;
1522: }
1523: if (!ispathsep(file_name[len - 1]))
1524: file_name[len++] = PATHSEP;
1525: }
1526: STRCPY(file_name + len, NameBuff);
1527: if (getperm(file_name) >= 0)
1528: return file_name;
1529: }
1530: if (options & FNAME_MESS)
1531: EMSG2("Can't find file \"%s\" in path", NameBuff);
1532: }
1533: vim_free(file_name); /* file doesn't exist */
1534: return NULL;
1535: }
1536:
1537: /*
1538: * Return the minimal number of rows that is needed on the screen to display
1539: * the current number of windows.
1540: */
1541: int
1542: min_rows()
1543: {
1544: WIN *wp;
1545: int total;
1546:
1547: if (firstwin == NULL) /* not initialized yet */
1548: return MIN_ROWS + 1; /* one window plus command line */
1549:
1550: total = p_ch; /* count the room for the status line */
1551: for (wp = firstwin; wp != NULL; wp = wp->w_next)
1552: total += MIN_ROWS + wp->w_status_height;
1553: return total;
1554: }
1555:
1556: /*
1557: * Return TRUE if there is only one window, not counting a help window, unless
1558: * it is the current window.
1559: */
1560: int
1561: only_one_window()
1562: {
1563: int count = 0;
1564: WIN *wp;
1565:
1566: for (wp = firstwin; wp != NULL; wp = wp->w_next)
1567: if (!wp->w_buffer->b_help || wp == curwin)
1568: ++count;
1569: return (count <= 1);
1570: }