Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 479 lines (425 sloc) 12.527 kb
cf92e3b @mbert Import Elvis 2.0 (written by Steve Kirkendall)
authored
1 /* state.c */
2 /* Copyright 1995 by Steve Kirkendall */
3
4 char id_state[] = "$Id: state.c,v 2.25 1996/09/28 15:08:50 steve Exp $";
5
6 #include "elvis.h"
7
8 #if USE_PROTOTYPES
9 static void fixbounds(WINDOW win);
10 #endif
11
12 /* Push a single state, in the current stratum.
13 *
14 * After this function returns, several fields in the struct will still
15 * need to be initialized. This function is meant to be called only from
16 * vipush() and inputpush(); those functions initialize the other fields.
17 */
18 void statepush(win, flags)
19 WINDOW win; /* window to receive new key state */
20 ELVISSTATE flags; /* flags of the new state */
21 {
22 STATE *newp;
23
24 /* allocate a STATE struct */
25 newp = (STATE *)safealloc(1, sizeof(STATE));
26
27 /* initialize the struct's values */
28 newp->flags = flags;
29 if (win->state != NULL)
30 {
31 newp->flags |= (win->state->flags & ELVIS_BOTTOM);
32 newp->cursor = win->state->cursor;
33 newp->top = win->state->top;
34 newp->bottom = win->state->bottom;
35 newp->acton = win->state->acton;
36 }
37 else if (!gui->moveto)
38 {
39 /* if GUI has no move() function, we can't support full-screen */
40 newp->flags |= ELVIS_BOTTOM;
41 }
42 if (newp->flags & ELVIS_BOTTOM)
43 {
44 newp->mapflags = MAP_OPEN;
45 }
46
47 /* link this into the stack */
48 newp->pop = win->state;
49 win->state = newp;
50 }
51
52 /* Pop a single state */
53 void statepop(win)
54 WINDOW win; /* window from which the state will be popped */
55 {
56 STATE *doomed;
57
58 assert(win->state != NULL);
59
60 /* if this is a stratum, then free the marks */
61 if (win->state->enter != NULL)
62 {
63 /* free the marks */
64 markfree(win->state->cursor);
65 markfree(win->state->top);
66 markfree(win->state->bottom);
67
68 /* also restore wantcol */
69 if (win->state->acton)
70 win->wantcol = win->state->acton->wantcol;
71 }
72
73 /* if we were editing a line before, and popping makes us switch
74 * buffers, then we aren't editing that line anymore.
75 */
76 if (!win->state->pop || win->state->cursor != win->state->pop->cursor)
77 {
78 if (win->di->openline)
79 {
80 markfree(win->di->openline);
81 win->di->openline = NULL;
82 }
83 }
84
85 /* if this has an info struct, then free it now, too */
86 if (win->state->info != NULL)
87 {
88 safefree(win->state->info);
89 }
90
91 /* remove the state from the state stack, and free it */
92 doomed = win->state;
93 win->state = doomed->pop;
94 safefree(doomed);
95 }
96
97 /* Push a new stratum. This involves appending a new blank line to a buffer,
98 * possibly adding a the prompt character to that line, and then pushing an
99 * open input state onto the state stack.
100 */
101 #if USE_PROTOTYPES
102 void statestratum(WINDOW win, CHAR *bufname, _CHAR_ prompt, RESULT (*enter)(WINDOW win))
103 #else
104 void statestratum(win, bufname, prompt, enter)
105 WINDOW win; /* window to receive new stratum */
106 CHAR *bufname; /* name of buffer to use in new stratum */
107 _CHAR_ prompt; /* prompt character, or '\0' for none */
108 RESULT (*enter)(); /* function which executes line */
109 #endif
110 {
111 BUFFER buf;
112 CHAR newtext[2];
113 MARK mark;
114
115 /* find the buffer. If it doesn't exist, then create it */
116 buf = bufalloc(bufname, 0);
117
118 /* create a blank line at the end of the buffer, and insert the prompt
119 * character there, if given.
120 */
121 mark = markalloc(buf, o_bufchars(buf));
122 if (prompt && (prompt != ':' || o_prompt))
123 {
124 newtext[0] = prompt;
125 newtext[1] = '\n';
126 bufreplace(mark, mark, newtext, 2);
127 }
128 else
129 {
130 newtext[0] = '\n';
131 bufreplace(mark, mark, newtext, 1);
132 }
133 marksetoffset(mark, o_bufchars(buf) - 1);
134
135 /* use the prompt as a special key in case we hit a [More] prompt
136 * when switching back to the old key state.
137 */
138 win->state->morekey = prompt;
139
140 /* push a new input state */
141 inputpush(win, ELVIS_BOTTOM|ELVIS_1LINE, 'i');
142
143 /* initialize the state to look like a new stratum */
144 win->state->cursor = mark;
145 win->state->top = markdup(mark);
146 win->state->bottom = markdup(mark);
147 win->state->acton = win->state->pop;
148 win->state->enter = enter;
149 win->state->prompt = prompt;
150
151 /* save the old stratum's wantcol (if there was an old stratum) */
152 if (win->state->acton)
153 win->state->acton->wantcol = win->wantcol;
154 }
155
156
157 static void fixbounds(win)
158 WINDOW win; /* window whose edit bounds need tweaking */
159 {
160 STATE *state;
161
162 /* Fix the edit bounds.
163 *
164 * Note that we do this for all strata on the stack, not just
165 * the current one. This is mostly for the benefit of the
166 * visual / and ? commands -- After the search, the current
167 * strata is still the regexp line entry one, but we need to
168 * worry about the edit limits of the main strata.
169 */
170 for (state = win->state; state; state = state->acton)
171 {
172 if (markbuffer(state->top) != markbuffer(state->cursor)
173 || markbuffer(state->top) != markbuffer(state->bottom)
174 || markoffset(state->top) > markoffset(state->cursor)
175 || markoffset(state->cursor) > markoffset(state->bottom))
176 {
177 marksetbuffer(state->top, markbuffer(state->cursor));
178 marksetbuffer(state->bottom, markbuffer(state->cursor));
179 if (state->acton == NULL)
180 {
181 /* in the main edit buffer, the edit bounds are
182 * changed to equal the cursor.
183 */
184 marksetoffset(state->top, markoffset(state->cursor));
185 marksetoffset(state->bottom, markoffset(state->cursor));
186 }
187 else
188 {
189 /* in a history buffer, the edit bounds are set
190 * to the whole line that the cursor is on, and
191 * if the cursor has moved to a different line
192 * then it is moved to the end of that line.
193 */
194 marksetoffset(state->top, markoffset((*dmnormal.move)(windefault, state->top, 0L, 0L, False)));
195 marksetoffset(state->bottom, markoffset((*dmnormal.move)(windefault, state->top, 0L, INFINITY, False)));
196 if (markoffset(state->cursor) < markoffset(state->top)
197 || markoffset(state->cursor) > markoffset(state->bottom))
198 {
199 marksetoffset(state->top, markoffset(dispmove(windefault, 0L, 0L)));
200 marksetoffset(state->bottom, markoffset(dispmove(windefault, 0L, INFINITY)));
201 marksetoffset(state->cursor, markoffset(state->bottom));
202 }
203 }
204 }
205 }
206 }
207
208
209 /* This function processes a single keystroke in the context of the default
210 * window.
211 */
212 void statekey(key)
213 _CHAR_ key; /* a single key to be parsed */
214 {
215 RESULT result;
216 STATE *state;
217 CHAR newtext[2];
218 int i, j;
219
220 assert(windefault);
221
222 state = windefault->state;
223
224 /* If user wants to abort operation, then ignore this key. This is
225 * important to check for, because elvis may be stuck in a recursive
226 * loop.
227 */
228 if (guipoll(False))
229 {
230 mapalert();
231 return;
232 }
233
234 /* if <Enter>, and not quoted, and this is a stratum, then call the
235 * enter() function. If it returns RESULT_MORE then follow that by
236 * processing <Enter> in the usual way; otherwise we're done.
237 */
238 if ((key == '\r' || key == '\n')
239 && (*state->shape)(windefault) != CURSOR_QUOTE
240 && state->enter)
241 {
242 /* adjust the input line */
243 inputbeforeenter(windefault);
244
245 /* if this line was entered via a one-time command from
246 * visual mode, then force drawstate to be DRAW_VISUAL so
247 * the user isn't forced to hit <enter> unless there really
248 * is some useful text to be read. EXCEPTION: If this GUI
249 * doesn't do full-screen, then don't bother.
250 */
251 if ((state->flags & ELVIS_1LINE) != 0 && gui->moveto != NULL)
252 {
253 windefault->di->drawstate = DRAW_VISUAL;
254 }
255
256 /* call the "enter" function for this state, and see whether
257 * the command is complete.
258 */
259 result = (*state->enter)(windefault);
260 if (result != RESULT_MORE)
261 {
262 /* If the window went away, then no more processing
263 * is necessary.
264 */
265 if (!windefault)
266 {
267 return;
268 }
269
270 /* We did one command line. Is that all we wanted? */
271 if (state->flags & ELVIS_1LINE)
272 {
273 /* yes, pop the stratum */
274 while (state->acton != state->pop)
275 {
276 statepop(windefault);
277 }
278 windefault->state->flags |= ELVIS_POP;
279 }
280 else
281 {
282 marksetoffset(state->cursor, o_bufchars(markbuffer(state->cursor)));
283 if (state->prompt && (state->prompt != ':' || o_prompt))
284 {
285 newtext[0] = state->prompt;
286 newtext[1] = '\n';
287 bufreplace(state->cursor, state->cursor, newtext, 2);
288 }
289 else
290 {
291 newtext[0] = '\n';
292 bufreplace(state->cursor, state->cursor, newtext, 1);
293 }
294 marksetoffset(state->cursor, o_bufchars(markbuffer(state->cursor)) - 1);
295 }
296
297 /* do the usual after-keystroke processing */
298 goto AfterKeystroke;
299 }
300 }
301
302 /* parse the keystroke for the current window */
303 if (key != (_CHAR_)-1)
304 result = (*state->parse)(key, state->info);
305 else
306 result = RESULT_COMPLETE;
307
308 /* If error, alert the window */
309 if (result == RESULT_ERROR)
310 {
311 /* clobber the "cmdchars" list - all chars entered */
312 windefault->cmdchars[0] = '\0';
313
314 /* alert the window */
315 mapalert();
316 if (o_errorbells)
317 guibeep(windefault);
318 }
319 else if (result == RESULT_COMPLETE && key != -1)
320 {
321 /* clobber the "cmdchars" list - all chars entered */
322 windefault->cmdchars[0] = '\0';
323
324 /* We have parsed a complete command. Now perform it */
325 switch ((*state->perform)(windefault))
326 {
327 case RESULT_ERROR:
328 /* command failed! alert the window */
329 mapalert();
330 if (o_errorbells)
331 guibeep(windefault);
332 break;
333
334 case RESULT_MORE:
335 /* set the pushed state's ELVIS_MORE flag */
336 state->flags |= ELVIS_MORE;
337 break;
338
339 case RESULT_COMPLETE:
340 /* nothing, just fall through... */
341 ;
342 }
343
344 /* The command may have caused the window to disappear.
345 * If so, then no more processing is necessary.
346 */
347 if (!windefault)
348 {
349 return;
350 }
351
352 /* If cursor has moved outside state->top and state->bottom,
353 * then make state->top and state->bottom equal the cursor.
354 */
355 fixbounds(windefault);
356
357 /* if the "optimize" option is false, and the current window
358 * is in vi mode, then update the current window's image.
359 */
360 if (!o_optimize
361 && windefault
362 && !windefault->state->pop
363 && windefault->di->curchgs != markbuffer(windefault->cursor)->changes
364 && (windefault->di->drawstate == DRAW_VISUAL
365 || windefault->di->drawstate == DRAW_VMSG))
366 {
367 drawimage(windefault);
368 if (gui->flush)
369 (*gui->flush)();
370 }
371 }
372 else if (result == RESULT_MORE)
373 {
374 /* partial command -- add this key to the cmdchars field */
375
376 /* if the array is full, then shift */
377 i = CHARlen(windefault->cmdchars);
378 j = (iscntrl(key) ? 2 : 1);
379 if (i + j >= QTY(windefault->cmdchars))
380 {
381 for (i = 0; windefault->cmdchars[i]; i++)
382 {
383 windefault->cmdchars[i] = windefault->cmdchars[i + j];
384 }
385 i -= j;
386 }
387
388 /* stuff the new char into it */
389 switch (o_nonascii)
390 {
391 case 's': key &= 0x7f; break;
392 case 'n': key = '.'; break;
393 case 'm': if (key>0x7f && key<=0x9f)
394 key = '.'; break;
395 }
396 if (iscntrl(key))
397 {
398 windefault->cmdchars[i++] = '^';
399 key ^= 0x40;
400 }
401 windefault->cmdchars[i++] = key;
402 windefault->cmdchars[i++] = '\0';
403 }
404
405 /* if visibly marking, then adjust the marks */
406 if (windefault->seltop)
407 {
408 (void)v_visible(windefault, NULL);
409 }
410
411 /* if we aren't still parsing, then perform other checks */
412 if (result != RESULT_MORE)
413 {
414 AfterKeystroke:
415 /* pop states, if we're supposed to */
416 while (windefault->state && (windefault->state->flags & ELVIS_POP))
417 {
418 /* pop the state */
419 statepop(windefault);
420
421 /* if the next state has its ELVIS_MORE flag set, then
422 * call the next state's perform() function again.
423 */
424 if (windefault->state
425 && windefault->state->flags & ELVIS_MORE)
426 {
427 /* call the next state's perform() function again */
428 #if 0
429 if (result == RESULT_ERROR ||
430 (*windefault->state->perform)(windefault) != RESULT_COMPLETE)
431 {
432 /* command failed! alert the window */
433 mapalert();
434 if (o_errorbells)
435 guibeep(windefault);
436 }
437 windefault->state->flags &= ~ELVIS_MORE;
438 #else
439 if (result != RESULT_ERROR)
440 result = (*windefault->state->perform)(windefault);
441 switch (result)
442 {
443 case RESULT_ERROR:
444 /* command failed! alert the window */
445 mapalert();
446 if (o_errorbells)
447 guibeep(windefault);
448 windefault->state->flags &= ~ELVIS_MORE;
449 break;
450
451 case RESULT_COMPLETE:
452 windefault->state->flags &= ~ELVIS_MORE;
453 break;
454
455 case RESULT_MORE:
456 windefault->state->flags |= ELVIS_MORE;
457 break;
458 }
459 #endif
460 }
461 }
462
463 /* fix the edit bounds */
464 fixbounds(windefault);
465
466 /* convert ELVIS_ONCE to ELVIS_POP */
467 if (windefault->state && (windefault->state->flags & ELVIS_ONCE))
468 {
469 windefault->state->flags |= ELVIS_POP;
470 }
471
472 /* if no states are left, then destroy the window */
473 if (!windefault->state)
474 {
475 (*gui->destroygw)(windefault->gw, True);
476 }
477 }
478 }
Something went wrong with that request. Please try again.