1 /* $OpenBSD: window.c,v 1.27 2009/06/04 23:39:37 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_rflag = 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_rflag |= 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_rflag |= 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 * If called with a FFOTHARG, flags on the new window are set to 'n'. 204 */ 205 /* ARGSUSED */ 206 int 207 splitwind(int f, int n) 208 { 209 struct mgwin *wp, *wp1, *wp2; 210 struct line *lp; 211 int ntru, ntrd, ntrl; 212 213 if (curwp->w_ntrows < 3) { 214 ewprintf("Cannot split a %d line window", curwp->w_ntrows); 215 return (FALSE); 216 } 217 wp = new_window(curbp); 218 if (wp == NULL) { 219 ewprintf("Unable to create a window"); 220 return (FALSE); 221 } 222 223 /* use the current dot and mark */ 224 wp->w_dotp = curwp->w_dotp; 225 wp->w_doto = curwp->w_doto; 226 wp->w_markp = curwp->w_markp; 227 wp->w_marko = curwp->w_marko; 228 wp->w_dotline = curwp->w_dotline; 229 wp->w_markline = curwp->w_markline; 230 231 /* figure out which half of the screen we're in */ 232 ntru = (curwp->w_ntrows - 1) / 2; /* Upper size */ 233 ntrl = (curwp->w_ntrows - 1) - ntru; /* Lower size */ 234 235 for (lp = curwp->w_linep, ntrd = 0; lp != curwp->w_dotp; 236 lp = lforw(lp)) 237 ntrd++; 238 239 lp = curwp->w_linep; 240 241 /* old is upper window */ 242 if (ntrd <= ntru) { 243 /* hit mode line */ 244 if (ntrd == ntru) 245 lp = lforw(lp); 246 curwp->w_ntrows = ntru; 247 wp->w_wndp = curwp->w_wndp; 248 curwp->w_wndp = wp; 249 wp->w_toprow = curwp->w_toprow + ntru + 1; 250 wp->w_ntrows = ntrl; 251 /* old is lower window */ 252 } else { 253 wp1 = NULL; 254 wp2 = wheadp; 255 while (wp2 != curwp) { 256 wp1 = wp2; 257 wp2 = wp2->w_wndp; 258 } 259 if (wp1 == NULL) 260 wheadp = wp; 261 else 262 wp1->w_wndp = wp; 263 wp->w_wndp = curwp; 264 wp->w_toprow = curwp->w_toprow; 265 wp->w_ntrows = ntru; 266 267 /* mode line */ 268 ++ntru; 269 curwp->w_toprow += ntru; 270 curwp->w_ntrows = ntrl; 271 while (ntru--) 272 lp = lforw(lp); 273 } 274 275 /* adjust the top lines if necessary */ 276 curwp->w_linep = lp; 277 wp->w_linep = lp; 278 279 curwp->w_rflag |= WFMODE | WFFULL; 280 wp->w_rflag |= WFMODE | WFFULL; 281 /* if FFOTHARG, set flags) */ 282 if (f & FFOTHARG) 283 wp->w_flag = n; 284 285 return (TRUE); 286 } 287 288 /* 289 * Enlarge the current window. Find the window that loses space. Make sure 290 * it is big enough. If so, hack the window descriptions, and ask redisplay 291 * to do all the hard work. You don't just set "force reframe" because dot 292 * would move. 293 */ 294 /* ARGSUSED */ 295 int 296 enlargewind(int f, int n) 297 { 298 struct mgwin *adjwp; 299 struct line *lp; 300 int i; 301 302 if (n < 0) 303 return (shrinkwind(f, -n)); 304 if (wheadp->w_wndp == NULL) { 305 ewprintf("Only one window"); 306 return (FALSE); 307 } 308 if ((adjwp = curwp->w_wndp) == NULL) { 309 adjwp = wheadp; 310 while (adjwp->w_wndp != curwp) 311 adjwp = adjwp->w_wndp; 312 } 313 if (adjwp->w_ntrows <= n) { 314 ewprintf("Impossible change"); 315 return (FALSE); 316 } 317 318 /* shrink below */ 319 if (curwp->w_wndp == adjwp) { 320 lp = adjwp->w_linep; 321 for (i = 0; i < n && lp != adjwp->w_bufp->b_headp; ++i) 322 lp = lforw(lp); 323 adjwp->w_linep = lp; 324 adjwp->w_toprow += n; 325 /* shrink above */ 326 } else { 327 lp = curwp->w_linep; 328 for (i = 0; i < n && lback(lp) != curbp->b_headp; ++i) 329 lp = lback(lp); 330 curwp->w_linep = lp; 331 curwp->w_toprow -= n; 332 } 333 curwp->w_ntrows += n; 334 adjwp->w_ntrows -= n; 335 curwp->w_rflag |= WFMODE | WFFULL; 336 adjwp->w_rflag |= WFMODE | WFFULL; 337 return (TRUE); 338 } 339 340 /* 341 * Shrink the current window. Find the window that gains space. Hack at the 342 * window descriptions. Ask the redisplay to do all the hard work. 343 */ 344 int 345 shrinkwind(int f, int n) 346 { 347 struct mgwin *adjwp; 348 struct line *lp; 349 int i; 350 351 if (n < 0) 352 return (enlargewind(f, -n)); 353 if (wheadp->w_wndp == NULL) { 354 ewprintf("Only one window"); 355 return (FALSE); 356 } 357 /* 358 * Bit of flakiness - KRANDOM means it was an internal call, and 359 * to be trusted implicitly about sizes. 360 */ 361 if (!(f & FFRAND) && curwp->w_ntrows <= n) { 362 ewprintf("Impossible change"); 363 return (FALSE); 364 } 365 if ((adjwp = curwp->w_wndp) == NULL) { 366 adjwp = wheadp; 367 while (adjwp->w_wndp != curwp) 368 adjwp = adjwp->w_wndp; 369 } 370 371 /* grow below */ 372 if (curwp->w_wndp == adjwp) { 373 lp = adjwp->w_linep; 374 for (i = 0; i < n && lback(lp) != adjwp->w_bufp->b_headp; ++i) 375 lp = lback(lp); 376 adjwp->w_linep = lp; 377 adjwp->w_toprow -= n; 378 /* grow above */ 379 } else { 380 lp = curwp->w_linep; 381 for (i = 0; i < n && lp != curbp->b_headp; ++i) 382 lp = lforw(lp); 383 curwp->w_linep = lp; 384 curwp->w_toprow += n; 385 } 386 curwp->w_ntrows -= n; 387 adjwp->w_ntrows += n; 388 curwp->w_rflag |= WFMODE | WFFULL; 389 adjwp->w_rflag |= WFMODE | WFFULL; 390 return (TRUE); 391 } 392 393 /* 394 * Delete current window. Call shrink-window to do the screen updating, then 395 * throw away the window. 396 */ 397 /* ARGSUSED */ 398 int 399 delwind(int f, int n) 400 { 401 struct mgwin *wp, *nwp; 402 403 wp = curwp; /* Cheap... */ 404 405 /* shrinkwind returning false means only one window... */ 406 if (shrinkwind(FFRAND, wp->w_ntrows + 1) == FALSE) 407 return (FALSE); 408 if (--wp->w_bufp->b_nwnd == 0) { 409 wp->w_bufp->b_dotp = wp->w_dotp; 410 wp->w_bufp->b_doto = wp->w_doto; 411 wp->w_bufp->b_markp = wp->w_markp; 412 wp->w_bufp->b_marko = wp->w_marko; 413 } 414 415 /* since shrinkwind did't crap out, we know we have a second window */ 416 if (wp == wheadp) 417 wheadp = curwp = wp->w_wndp; 418 else if ((curwp = wp->w_wndp) == NULL) 419 curwp = wheadp; 420 curbp = curwp->w_bufp; 421 for (nwp = wheadp; nwp != NULL; nwp = nwp->w_wndp) 422 if (nwp->w_wndp == wp) { 423 nwp->w_wndp = wp->w_wndp; 424 break; 425 } 426 free(wp); 427 return (TRUE); 428 } 429