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 }