1 /* $OpenBSD: window.c,v 1.25 2006/07/25 08:22:32 kjell Exp $ */ 2 3 /* This file is in the public domain. */ 4 5 /* 6 * Window handling. 7 */ 8 9 #include "def.h" 10 11 struct mgwin * 12 new_window(struct buffer *bp) 13 { 14 struct mgwin *wp; 15 16 wp = calloc(1, sizeof(struct mgwin)); 17 if (wp == NULL) 18 return (NULL); 19 20 wp->w_bufp = bp; 21 wp->w_dotp = NULL; 22 wp->w_doto = 0; 23 wp->w_markp = NULL; 24 wp->w_marko = 0; 25 wp->w_flag = 0; 26 wp->w_frame = 0; 27 wp->w_wrapline = NULL; 28 wp->w_dotline = wp->w_markline = 1; 29 if (bp) 30 bp->b_nwnd++; 31 return (wp); 32 } 33 34 /* 35 * Reposition dot in the current window to line "n". If the argument is 36 * positive, it is that line. If it is negative it is that line from the 37 * bottom. If it is 0 the window is centered (this is what the standard 38 * redisplay code does). If GOSREC is undefined, default is 0, so it acts 39 * like GNU. If GOSREC is defined, with no argument it defaults to 1 and 40 * works like in Gosling. 41 */ 42 /* ARGSUSED */ 43 int 44 reposition(int f, int n) 45 { 46 #ifndef GOSREC 47 curwp->w_frame = (f & FFARG) ? (n >= 0 ? n + 1 : n) : 0; 48 #else /* !GOSREC */ 49 curwp->w_frame = n; 50 #endif /* !GOSREC */ 51 curwp->w_flag |= WFFRAME; 52 sgarbf = TRUE; 53 return (TRUE); 54 } 55 56 /* 57 * Refresh the display. A call is made to the "ttresize" entry in the 58 * terminal handler, which tries to reset "nrow" and "ncol". They will, 59 * however, never be set outside of the NROW or NCOL range. If the display 60 * changed size, arrange that everything is redone, then call "update" to 61 * fix the display. We do this so the new size can be displayed. In the 62 * normal case the call to "update" in "main.c" refreshes the screen, and 63 * all of the windows need not be recomputed. This call includes a 64 * 'force' parameter to ensure that the redraw is done, even after a 65 * a suspend/continue (where the window size parameters will already 66 * be updated). Note that when you get to the "display unusable" 67 * message, the screen will be messed up. If you make the window bigger 68 * again, and send another command, everything will get fixed! 69 */ 70 int 71 redraw(int f, int n) 72 { 73 return (do_redraw(f, n, FALSE)); 74 } 75 76 /* ARGSUSED */ 77 int 78 do_redraw(int f, int n, int force) 79 { 80 struct mgwin *wp; 81 int oldnrow, oldncol; 82 83 oldnrow = nrow; 84 oldncol = ncol; 85 ttresize(); 86 if (nrow != oldnrow || ncol != oldncol || force) { 87 88 /* find last */ 89 wp = wheadp; 90 while (wp->w_wndp != NULL) 91 wp = wp->w_wndp; 92 93 /* check if too small */ 94 if (nrow < wp->w_toprow + 3) { 95 ewprintf("Display unusable"); 96 return (FALSE); 97 } 98 wp->w_ntrows = nrow - wp->w_toprow - 2; 99 sgarbf = TRUE; 100 update(); 101 } else 102 sgarbf = TRUE; 103 return (TRUE); 104 } 105 106 /* 107 * The command to make the next window (next => down the screen) the current 108 * window. There are no real errors, although the command does nothing if 109 * there is only 1 window on the screen. 110 */ 111 /* ARGSUSED */ 112 int 113 nextwind(int f, int n) 114 { 115 struct mgwin *wp; 116 117 if ((wp = curwp->w_wndp) == NULL) 118 wp = wheadp; 119 curwp = wp; 120 curbp = wp->w_bufp; 121 return (TRUE); 122 } 123 124 /* not in Gnu Emacs */ 125 /* 126 * This command makes the previous window (previous => up the screen) the 127 * current window. There are no errors, although the command does not do 128 * a lot if there is only 1 window. 129 */ 130 /* ARGSUSED */ 131 int 132 prevwind(int f, int n) 133 { 134 struct mgwin *wp1, *wp2; 135 136 wp1 = wheadp; 137 wp2 = curwp; 138 if (wp1 == wp2) 139 wp2 = NULL; 140 while (wp1->w_wndp != wp2) 141 wp1 = wp1->w_wndp; 142 curwp = wp1; 143 curbp = wp1->w_bufp; 144 return (TRUE); 145 } 146 147 /* 148 * This command makes the current window the only window on the screen. Try 149 * to set the framing so that "." does not have to move on the display. Some 150 * care has to be taken to keep the values of dot and mark in the buffer 151 * structures right if the destruction of a window makes a buffer become 152 * undisplayed. 153 */ 154 /* ARGSUSED */ 155 int 156 onlywind(int f, int n) 157 { 158 struct mgwin *wp; 159 struct line *lp; 160 int i; 161 162 while (wheadp != curwp) { 163 wp = wheadp; 164 wheadp = wp->w_wndp; 165 if (--wp->w_bufp->b_nwnd == 0) { 166 wp->w_bufp->b_dotp = wp->w_dotp; 167 wp->w_bufp->b_doto = wp->w_doto; 168 wp->w_bufp->b_markp = wp->w_markp; 169 wp->w_bufp->b_marko = wp->w_marko; 170 } 171 free(wp); 172 } 173 while (curwp->w_wndp != NULL) { 174 wp = curwp->w_wndp; 175 curwp->w_wndp = wp->w_wndp; 176 if (--wp->w_bufp->b_nwnd == 0) { 177 wp->w_bufp->b_dotp = wp->w_dotp; 178 wp->w_bufp->b_doto = wp->w_doto; 179 wp->w_bufp->b_markp = wp->w_markp; 180 wp->w_bufp->b_marko = wp->w_marko; 181 } 182 free(wp); 183 } 184 lp = curwp->w_linep; 185 i = curwp->w_toprow; 186 while (i != 0 && lback(lp) != curbp->b_headp) { 187 --i; 188 lp = lback(lp); 189 } 190 curwp->w_toprow = 0; 191 192 /* 2 = mode, echo */ 193 curwp->w_ntrows = nrow - 2; 194 curwp->w_linep = lp; 195 curwp->w_flag |= WFMODE | WFFULL; 196 return (TRUE); 197 } 198 199 /* 200 * Split the current window. A window smaller than 3 lines cannot be split. 201 * The only other error that is possible is a "malloc" failure allocating the 202 * structure for the new window. 203 */ 204 /* ARGSUSED */ 205 int 206 splitwind(int f, int n) 207 { 208 struct mgwin *wp, *wp1, *wp2; 209 struct line *lp; 210 int ntru, ntrd, ntrl; 211 212 if (curwp->w_ntrows < 3) { 213 ewprintf("Cannot split a %d line window", curwp->w_ntrows); 214 return (FALSE); 215 } 216 wp = new_window(curbp); 217 if (wp == NULL) { 218 ewprintf("Unable to create a window"); 219 return (FALSE); 220 } 221 222 /* use the current dot and mark */ 223 wp->w_dotp = curwp->w_dotp; 224 wp->w_doto = curwp->w_doto; 225 wp->w_markp = curwp->w_markp; 226 wp->w_marko = curwp->w_marko; 227 wp->w_dotline = curwp->w_dotline; 228 wp->w_markline = curwp->w_markline; 229 230 /* figure out which half of the screen we're in */ 231 ntru = (curwp->w_ntrows - 1) / 2; /* Upper size */ 232 ntrl = (curwp->w_ntrows - 1) - ntru; /* Lower size */ 233 234 for (lp = curwp->w_linep, ntrd = 0; lp != curwp->w_dotp; 235 lp = lforw(lp)) 236 ntrd++; 237 238 lp = curwp->w_linep; 239 240 /* old is upper window */ 241 if (ntrd <= ntru) { 242 /* hit mode line */ 243 if (ntrd == ntru) 244 lp = lforw(lp); 245 curwp->w_ntrows = ntru; 246 wp->w_wndp = curwp->w_wndp; 247 curwp->w_wndp = wp; 248 wp->w_toprow = curwp->w_toprow + ntru + 1; 249 wp->w_ntrows = ntrl; 250 /* old is lower window */ 251 } else { 252 wp1 = NULL; 253 wp2 = wheadp; 254 while (wp2 != curwp) { 255 wp1 = wp2; 256 wp2 = wp2->w_wndp; 257 } 258 if (wp1 == NULL) 259 wheadp = wp; 260 else 261 wp1->w_wndp = wp; 262 wp->w_wndp = curwp; 263 wp->w_toprow = curwp->w_toprow; 264 wp->w_ntrows = ntru; 265 266 /* mode line */ 267 ++ntru; 268 curwp->w_toprow += ntru; 269 curwp->w_ntrows = ntrl; 270 while (ntru--) 271 lp = lforw(lp); 272 } 273 274 /* adjust the top lines if necessary */ 275 curwp->w_linep = lp; 276 wp->w_linep = lp; 277 278 curwp->w_flag |= WFMODE | WFFULL; 279 wp->w_flag |= WFMODE | WFFULL; 280 return (TRUE); 281 } 282 283 /* 284 * Enlarge the current window. Find the window that loses space. Make sure 285 * it is big enough. If so, hack the window descriptions, and ask redisplay 286 * to do all the hard work. You don't just set "force reframe" because dot 287 * would move. 288 */ 289 /* ARGSUSED */ 290 int 291 enlargewind(int f, int n) 292 { 293 struct mgwin *adjwp; 294 struct line *lp; 295 int i; 296 297 if (n < 0) 298 return (shrinkwind(f, -n)); 299 if (wheadp->w_wndp == NULL) { 300 ewprintf("Only one window"); 301 return (FALSE); 302 } 303 if ((adjwp = curwp->w_wndp) == NULL) { 304 adjwp = wheadp; 305 while (adjwp->w_wndp != curwp) 306 adjwp = adjwp->w_wndp; 307 } 308 if (adjwp->w_ntrows <= n) { 309 ewprintf("Impossible change"); 310 return (FALSE); 311 } 312 313 /* shrink below */ 314 if (curwp->w_wndp == adjwp) { 315 lp = adjwp->w_linep; 316 for (i = 0; i < n && lp != adjwp->w_bufp->b_headp; ++i) 317 lp = lforw(lp); 318 adjwp->w_linep = lp; 319 adjwp->w_toprow += n; 320 /* shrink above */ 321 } else { 322 lp = curwp->w_linep; 323 for (i = 0; i < n && lback(lp) != curbp->b_headp; ++i) 324 lp = lback(lp); 325 curwp->w_linep = lp; 326 curwp->w_toprow -= n; 327 } 328 curwp->w_ntrows += n; 329 adjwp->w_ntrows -= n; 330 curwp->w_flag |= WFMODE | WFFULL; 331 adjwp->w_flag |= WFMODE | WFFULL; 332 return (TRUE); 333 } 334 335 /* 336 * Shrink the current window. Find the window that gains space. Hack at the 337 * window descriptions. Ask the redisplay to do all the hard work. 338 */ 339 int 340 shrinkwind(int f, int n) 341 { 342 struct mgwin *adjwp; 343 struct line *lp; 344 int i; 345 346 if (n < 0) 347 return (enlargewind(f, -n)); 348 if (wheadp->w_wndp == NULL) { 349 ewprintf("Only one window"); 350 return (FALSE); 351 } 352 /* 353 * Bit of flakiness - KRANDOM means it was an internal call, and 354 * to be trusted implicitly about sizes. 355 */ 356 if (!(f & FFRAND) && curwp->w_ntrows <= n) { 357 ewprintf("Impossible change"); 358 return (FALSE); 359 } 360 if ((adjwp = curwp->w_wndp) == NULL) { 361 adjwp = wheadp; 362 while (adjwp->w_wndp != curwp) 363 adjwp = adjwp->w_wndp; 364 } 365 366 /* grow below */ 367 if (curwp->w_wndp == adjwp) { 368 lp = adjwp->w_linep; 369 for (i = 0; i < n && lback(lp) != adjwp->w_bufp->b_headp; ++i) 370 lp = lback(lp); 371 adjwp->w_linep = lp; 372 adjwp->w_toprow -= n; 373 /* grow above */ 374 } else { 375 lp = curwp->w_linep; 376 for (i = 0; i < n && lp != curbp->b_headp; ++i) 377 lp = lforw(lp); 378 curwp->w_linep = lp; 379 curwp->w_toprow += n; 380 } 381 curwp->w_ntrows -= n; 382 adjwp->w_ntrows += n; 383 curwp->w_flag |= WFMODE | WFFULL; 384 adjwp->w_flag |= WFMODE | WFFULL; 385 return (TRUE); 386 } 387 388 /* 389 * Delete current window. Call shrink-window to do the screen updating, then 390 * throw away the window. 391 */ 392 /* ARGSUSED */ 393 int 394 delwind(int f, int n) 395 { 396 struct mgwin *wp, *nwp; 397 398 wp = curwp; /* Cheap... */ 399 400 /* shrinkwind returning false means only one window... */ 401 if (shrinkwind(FFRAND, wp->w_ntrows + 1) == FALSE) 402 return (FALSE); 403 if (--wp->w_bufp->b_nwnd == 0) { 404 wp->w_bufp->b_dotp = wp->w_dotp; 405 wp->w_bufp->b_doto = wp->w_doto; 406 wp->w_bufp->b_markp = wp->w_markp; 407 wp->w_bufp->b_marko = wp->w_marko; 408 } 409 410 /* since shrinkwind did't crap out, we know we have a second window */ 411 if (wp == wheadp) 412 wheadp = curwp = wp->w_wndp; 413 else if ((curwp = wp->w_wndp) == NULL) 414 curwp = wheadp; 415 curbp = curwp->w_bufp; 416 for (nwp = wheadp; nwp != NULL; nwp = nwp->w_wndp) 417 if (nwp->w_wndp == wp) { 418 nwp->w_wndp = wp->w_wndp; 419 break; 420 } 421 free(wp); 422 return (TRUE); 423 } 424 425 /* 426 * Pick a window for a pop-up. Split the screen if there is only one window. 427 * Pick the uppermost window that isn't the current window. An LRU algorithm 428 * might be better. Return a pointer, or NULL on error. 429 */ 430 struct mgwin * 431 wpopup(void) 432 { 433 struct mgwin *wp; 434 435 if (wheadp->w_wndp == NULL && 436 splitwind(FFRAND, 0) == FALSE) 437 return (NULL); 438 439 /* find a window to use */ 440 wp = wheadp; 441 442 while (wp != NULL && wp == curwp) 443 wp = wp->w_wndp; 444 return (wp); 445 } 446