1 2 3 /* This version of microEmacs is based on the public domain C 4 * version written by Dave G. Conroy. 5 * The D programming language version is written by Walter Bright. 6 * http://www.digitalmars.com/d/ 7 * This program is in the public domain. 8 */ 9 10 11 /* 12 * Window management. Some of the functions are internal, and some are 13 * attached to keys that the user actually types. 14 */ 15 16 module window; 17 18 import core.memory; 19 import std..string; 20 21 import ed; 22 import buffer; 23 import line; 24 import main; 25 import terminal; 26 import display; 27 28 /* 29 * There is a window structure allocated for every active display window. The 30 * windows are kept in a big list, in top to bottom screen order, with the 31 * listhead at "wheadp". Each window contains its own values of dot and mark. 32 * The flag field contains some bits that are set by commands to guide 33 * redisplay; although this is a bit of a compromise in terms of decoupling, 34 * the full blown redisplay is just too expensive to run for every input 35 * character. 36 */ 37 struct WINDOW { 38 BUFFER* w_bufp; /* Buffer displayed in window */ 39 LINE* w_linep; /* Top line in the window */ 40 LINE* w_dotp; /* Line containing "." */ 41 int w_doto; /* Byte offset for "." */ 42 LINE* w_markp; /* Line containing "mark" */ 43 int w_marko; /* Byte offset for "mark" */ 44 int w_toprow; /* Origin 0 top row of window */ 45 int w_ntrows; /* # of rows of text in window */ 46 int w_force; /* If NZ, forcing row. */ 47 ubyte w_flag; /* Flags. */ 48 int w_startcol; /* starting column */ 49 } 50 51 enum 52 { 53 WFFORCE = 0x01, /* Window needs forced reframe */ 54 WFMOVE = 0x02, /* Movement from line to line */ 55 WFEDIT = 0x04, /* Editing within a line */ 56 WFHARD = 0x08, /* Better do a full display */ 57 WFMODE = 0x10, /* Update mode line. */ 58 } 59 60 __gshared WINDOW*[] windows; 61 62 __gshared WINDOW* winSearchPat; // the window with highlighted search results 63 64 /* !=0 means marking */ 65 bool window_marking(WINDOW* wp) { return wp.w_markp != null; } 66 67 68 /************************************* 69 * Reposition dot in the current window to line "n". If the argument is 70 * positive, it is that line. If it is negative it is that line from the 71 * bottom. If it is 0 the window is centered (this is what the standard 72 * redisplay code does). With no argument it defaults to 1. Bound to M-!. 73 * Because of the default, it works like in Gosling. 74 */ 75 76 int window_reposition(bool f, int n) 77 { 78 curwp.w_force = n; 79 curwp.w_flag |= WFFORCE; 80 return (TRUE); 81 } 82 83 /* 84 * Refresh the screen. With no argument, it just does the refresh. With an 85 * argument it recenters "." in the current window. Bound to "C-L". 86 */ 87 int window_refresh(bool f, int n) 88 { 89 if (f == FALSE) 90 sgarbf = TRUE; 91 else 92 { 93 curwp.w_force = 0; /* Center dot. */ 94 curwp.w_flag |= WFFORCE; 95 } 96 97 return (TRUE); 98 } 99 100 /* 101 * The command make the next window (next => down the screen) the current 102 * window. There are no real errors, although the command does nothing if 103 * there is only 1 window on the screen. Bound to "C-X C-N". 104 */ 105 int window_next(bool f, int n) 106 { 107 foreach (i, wp; windows) 108 { 109 if (wp == curwp) 110 { 111 i = i + 1; 112 if (i == windows.length) 113 i = 0; 114 curwp = windows[i]; 115 curbp = curwp.w_bufp; 116 return TRUE; 117 } 118 } 119 return FALSE; 120 } 121 122 /* 123 * This command makes the previous window (previous => up the screen) the 124 * current window. There arn't any errors, although the command does not do a 125 * lot if there is 1 window. 126 */ 127 int window_prev(bool f, int n) 128 { 129 foreach (i, wp; windows) 130 { 131 if (wp == curwp) 132 { 133 if (i == 0) 134 i = windows.length; 135 i--; 136 curwp = windows[i]; 137 curbp = curwp.w_bufp; 138 return TRUE; 139 } 140 } 141 return FALSE; 142 } 143 144 /* 145 * This command moves the current window down by "arg" lines. Recompute the 146 * top line in the window. The move up and move down code is almost completely 147 * the same; most of the work has to do with reframing the window, and picking 148 * a new dot. We share the code by having "move down" just be an interface to 149 * "move up". Magic. Bound to "C-X C-N". 150 */ 151 int window_mvdn(bool f, int n) 152 { 153 return window_mvup(f, -n); 154 } 155 156 /* 157 * Move the current window up by "arg" lines. Recompute the new top line of 158 * the window. Look to see if "." is still on the screen. If it is, you win. 159 * If it isn't, then move "." to center it in the new framing of the window 160 * (this command does not really move "."; it moves the frame). Bound to 161 * "C-X C-P". 162 */ 163 int window_mvup(bool f, int n) 164 { 165 LINE *lp; 166 int i; 167 168 lp = curwp.w_linep; 169 170 if (n < 0) 171 { 172 while (n++ && lp!=curbp.b_linep) 173 lp = lforw(lp); 174 } 175 else 176 { 177 while (n-- && lback(lp)!=curbp.b_linep) 178 lp = lback(lp); 179 } 180 181 curwp.w_linep = lp; /* new top line of window */ 182 curwp.w_flag |= WFHARD; /* Mode line is OK. */ 183 184 for (i = 0; i < curwp.w_ntrows; ++i) 185 { 186 if (lp == curwp.w_dotp) 187 return (TRUE); 188 if (lp == curbp.b_linep) 189 break; 190 lp = lforw(lp); 191 } 192 193 lp = curwp.w_linep; 194 i = curwp.w_ntrows/2; 195 196 while (i-- && lp != curbp.b_linep) 197 lp = lforw(lp); 198 199 curwp.w_dotp = lp; 200 curwp.w_doto = 0; 201 return (TRUE); 202 } 203 204 /* 205 * This command makes the current window the only window on the screen. Bound 206 * to "C-X 1". Try to set the framing so that "." does not have to move on the 207 * display. Some care has to be taken to keep the values of dot and mark in 208 * the buffer structures right if the destruction of a window makes a buffer 209 * become undisplayed. 210 */ 211 int window_only(bool f, int n) 212 { 213 foreach (wp; windows) 214 { 215 if (wp != curwp) 216 { 217 if (--wp.w_bufp.b_nwnd == 0) { 218 wp.w_bufp.b_dotp = wp.w_dotp; 219 wp.w_bufp.b_doto = wp.w_doto; 220 wp.w_bufp.b_markp = wp.w_markp; 221 wp.w_bufp.b_marko = wp.w_marko; 222 } 223 if (winSearchPat == wp) 224 winSearchPat = null; 225 //delete wp; 226 core.memory.GC.free(wp); 227 } 228 } 229 windows = windows[0 .. 1]; 230 windows[0] = curwp; 231 232 auto lp = curwp.w_linep; 233 auto i = curwp.w_toprow; 234 while (i!=0 && lback(lp)!=curbp.b_linep) { 235 --i; 236 lp = lback(lp); 237 } 238 curwp.w_toprow = 0; 239 curwp.w_ntrows = term.t_nrow - 2; 240 curwp.w_linep = lp; 241 curwp.w_flag |= WFMODE|WFHARD; 242 return (TRUE); 243 } 244 245 /* 246 * Split the current window. A window smaller than 3 lines cannot be split. 247 * The only other error that is possible is a "malloc" failure allocating the 248 * structure for the new window. Bound to "C-X 2". 249 */ 250 int window_split(bool f, int n) 251 { 252 LINE *lp; 253 int ntru; 254 int ntrl; 255 int ntrd; 256 WINDOW *wp1; 257 WINDOW *wp2; 258 259 if (curwp.w_ntrows < 3) { 260 mlwrite("Cannot split a %d line window", curwp.w_ntrows); 261 return (FALSE); 262 } 263 auto wp = new WINDOW; 264 ++curbp.b_nwnd; /* Displayed twice. */ 265 wp.w_bufp = curbp; 266 wp.w_dotp = curwp.w_dotp; 267 wp.w_doto = curwp.w_doto; 268 wp.w_markp = curwp.w_markp; 269 wp.w_marko = curwp.w_marko; 270 ntru = (curwp.w_ntrows-1) / 2; /* Upper size */ 271 ntrl = (curwp.w_ntrows-1) - ntru; /* Lower size */ 272 lp = curwp.w_linep; 273 ntrd = 0; 274 while (lp != curwp.w_dotp) { 275 ++ntrd; 276 lp = lforw(lp); 277 } 278 lp = curwp.w_linep; 279 if (ntrd <= ntru) { /* Old is upper window. */ 280 if (ntrd == ntru) /* Hit mode line. */ 281 lp = lforw(lp); 282 curwp.w_ntrows = ntru; 283 // Insert wp after curwp 284 foreach (i, w; windows) 285 { if (w == curwp) 286 { 287 windows = windows[0 .. i + 1] ~ wp ~ windows[i + 1 .. $]; 288 break; 289 } 290 } 291 wp.w_toprow = curwp.w_toprow+ntru+1; 292 wp.w_ntrows = ntrl; 293 } else { /* Old is lower window */ 294 // Insert wp before curwp 295 foreach (i, w; windows) 296 { if (w == curwp) 297 { 298 windows = windows[0 .. i] ~ wp ~ windows[i .. $]; 299 break; 300 } 301 } 302 303 wp.w_toprow = curwp.w_toprow; 304 wp.w_ntrows = ntru; 305 ++ntru; /* Mode line. */ 306 curwp.w_toprow += ntru; 307 curwp.w_ntrows = ntrl; 308 while (ntru--) 309 lp = lforw(lp); 310 } 311 curwp.w_linep = lp; /* Adjust the top lines */ 312 wp.w_linep = lp; /* if necessary. */ 313 curwp.w_flag |= WFMODE|WFHARD; 314 wp.w_flag |= WFMODE|WFHARD; 315 return (TRUE); 316 } 317 318 /* 319 * Enlarge the current window. Find the window that loses space. Make sure it 320 * is big enough. If so, hack the window descriptions, and ask redisplay to do 321 * all the hard work. You don't just set "force reframe" because dot would 322 * move. Bound to "C-X Z". 323 */ 324 int window_enlarge(bool f, int n) 325 { 326 WINDOW *adjwp; 327 LINE *lp; 328 bool prev; 329 330 if (n < 0) 331 return (window_shrink(f, -n)); 332 333 if (windows.length == 1) { 334 mlwrite("Only one window"); 335 return (FALSE); 336 } 337 338 foreach (i, wp; windows) 339 { 340 if (wp == curwp) 341 { 342 if (i == windows.length - 1) 343 { 344 adjwp = windows[i - 1]; 345 prev = true; 346 } 347 else 348 { 349 adjwp = windows[i + 1]; 350 prev = false; 351 } 352 break; 353 } 354 } 355 356 if (adjwp.w_ntrows <= n) { 357 mlwrite("Impossible enlarge change"); 358 return (FALSE); 359 } 360 if (!prev) { // Shrink below. 361 lp = adjwp.w_linep; 362 for (int i=0; i<n && lp!=adjwp.w_bufp.b_linep; ++i) 363 lp = lforw(lp); 364 adjwp.w_linep = lp; 365 adjwp.w_toprow += n; 366 } else { /* Shrink above. */ 367 lp = curwp.w_linep; 368 for (int i=0; i<n && lback(lp)!=curbp.b_linep; ++i) 369 lp = lback(lp); 370 curwp.w_linep = lp; 371 curwp.w_toprow -= n; 372 } 373 curwp.w_ntrows += n; 374 adjwp.w_ntrows -= n; 375 curwp.w_flag |= WFMODE|WFHARD; 376 adjwp.w_flag |= WFMODE|WFHARD; 377 return (TRUE); 378 } 379 380 /* 381 * Shrink the current window. Find the window that gains space. Hack at the 382 * window descriptions. Ask the redisplay to do all the hard work. Bound to 383 * "C-X C-Z". 384 */ 385 int window_shrink(bool f, int n) 386 { 387 WINDOW *adjwp; 388 LINE *lp; 389 bool prev; 390 391 if (n < 0) 392 return (window_enlarge(f, -n)); 393 if (windows.length == 1) { 394 mlwrite("Only one window"); 395 return (FALSE); 396 } 397 398 foreach (i, wp; windows) 399 { 400 if (wp == curwp) 401 { 402 if (i == windows.length - 1) 403 { 404 adjwp = windows[i - 1]; 405 prev = true; 406 } 407 else 408 { 409 adjwp = windows[i + 1]; 410 prev = false; 411 } 412 break; 413 } 414 } 415 416 if (curwp.w_ntrows <= n) { 417 mlwrite("Impossible shrink change"); 418 return (FALSE); 419 } 420 if (!prev) { // Grow below. 421 lp = adjwp.w_linep; 422 for (int i=0; i<n && lback(lp)!=adjwp.w_bufp.b_linep; ++i) 423 lp = lback(lp); 424 adjwp.w_linep = lp; 425 adjwp.w_toprow -= n; 426 } else { /* Grow above. */ 427 lp = curwp.w_linep; 428 for (int i=0; i<n && lp!=curbp.b_linep; ++i) 429 lp = lforw(lp); 430 curwp.w_linep = lp; 431 curwp.w_toprow += n; 432 } 433 curwp.w_ntrows -= n; 434 adjwp.w_ntrows += n; 435 curwp.w_flag |= WFMODE|WFHARD; 436 adjwp.w_flag |= WFMODE|WFHARD; 437 return (TRUE); 438 } 439 440 /* 441 * Pick a window for a pop-up. Split the screen if there is only one window. 442 * Pick the uppermost window that isn't the current window. An LRU algorithm 443 * might be better. Return a pointer, or null on error. 444 */ 445 WINDOW *wpopup() 446 { 447 if (windows.length == 1 // Only 1 window 448 && window_split(FALSE, 0) == FALSE) // and it won't split 449 return null; 450 auto wp = windows[0]; // Find window to use 451 if (wp == curwp) 452 wp = windows[1]; 453 return wp; 454 } 455 456 /* 457 * Delete the current window. Does nothing if this window is last the 458 * one on the screen. Otherwise, it moves to the next window and 459 * deletes the previous one. 460 */ 461 int delwind(bool f, int n) 462 { 463 if (windows.length == 1) 464 return TRUE; // Only 1 window 465 466 auto delwp = curwp; 467 if (windows[0] == delwp) // Pick which window to be in next 468 { 469 curwp = windows[1]; 470 windows = windows[1 .. $]; 471 curbp = curwp.w_bufp; 472 auto lp = curwp.w_linep; 473 int i = curwp.w_toprow; 474 while( i!=0 && lback(lp)!=curbp.b_linep ) 475 { 476 i--; 477 lp = lback(lp); 478 } 479 curwp.w_toprow = delwp.w_toprow; 480 } 481 else 482 { 483 foreach (i, wp; windows) 484 { 485 if (wp == delwp) 486 { 487 curwp = windows[i - 1]; 488 windows = windows[0 .. i] ~ windows[i + 1 .. $]; 489 break; 490 } 491 } 492 curbp = curwp.w_bufp; 493 } 494 495 curwp.w_ntrows += delwp.w_ntrows+1; 496 curwp.w_flag |= WFMODE|WFHARD; 497 498 delwp.w_bufp.b_dotp = delwp.w_dotp; 499 delwp.w_bufp.b_doto = delwp.w_doto; 500 delwp.w_bufp.b_markp = delwp.w_markp; 501 delwp.w_bufp.b_marko = delwp.w_marko; 502 delwp.w_bufp.b_nwnd--; 503 504 if (winSearchPat == delwp) 505 winSearchPat = null; 506 //delete delwp; 507 core.memory.GC.free(delwp); 508 509 return( TRUE ); 510 }