1 /* editbox.c -- text editor/viewer in kevedit
2 * $Id: editbox.c,v 1.3 2005/05/28 03:17:46 bitman Exp $
3 * Copyright (C) 2000 Ryan Phillips <bitman@users.sf.net>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place Suite 330; Boston, MA 02111-1307, USA.
18 */
19
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23
24
25 #include "editbox.h"
26
27 #include "kevedit/screen.h"
28
29 #include "structures/svector.h"
30 #include "zzm.h"
31
32 #include "register.h"
33 #include "help/help.h"
34
35 #include "themes/theme.h"
36
37 #include "synth/synth.h"
38 #include "synth/zzm.h"
39
40 #include "libzzt2/zztoop.h"
41
42 #include "display/display.h"
43 #include "display/colours.h"
44
45 #include <stdlib.h>
46 #include <stdio.h>
47 #include <string.h>
48 #include <ctype.h>
49
50
51 /* What portion of display box needs update? */
52 #define U_NONE 0x00
53 #define U_CENTER 0x01
54 #define U_BOTTOM 0x02
55 #define U_TOP 0x04
56 #define U_EDITAREA 0x07
57 #define U_TITLE 0x08
58 #define U_PANEL 0x10
59 #define U_ALL 0xFF
60
61 /* string leader: string to appear at beginning and end of edit field
62 * (now it looks like in zzt, except centered) */
63 #define SLEADER "\x07 \x07 \x07 \x07 \x07 \x07 \x07 \x07 \x07"
64
65 #define ZOC_TEXT_COLOUR GREEN_F | BRIGHT_F
66 #define ZOC_HIGHLIGHT_COLOUR BLACK_F | WHITE_B
67 #define ZOC_MPLAY_COLOUR WHITE_F | BRIGHT_F
68
69 void testMusic(stringvector* sv, int slur, int editwidth, int flags, displaymethod* d);
70
71 /* how to display a line of text in updateditbox() */
72 #define displayline(x, y, s, edit, flags, firstline, d) ((flags & EDITBOX_ZOCMODE) ? displayzoc((x), (y), (s), !(edit), (firstline), (d)) : d->print_discrete((x), (y), ZOC_TEXT_COLOUR, (s)))
73
74
75 /***** draweditpanel() ***********/
draweditpanel(int insertflag,int wrapwidth,int zocmode,displaymethod * d)76 void draweditpanel(int insertflag, int wrapwidth, int zocmode, displaymethod * d)
77 {
78 char buf[10] = "";
79 drawsidepanel(d, PANEL_EDIT);
80
81 d->print(76, 6, YELLOW_F | BRIGHT_F | BLUE_B, (insertflag ? "on" : "off"));
82
83 sprintf(buf, "%d", wrapwidth);
84
85 if (wrapwidth)
86 d->print(76, 8, YELLOW_F | BRIGHT_F | BLUE_B, buf);
87 else
88 d->print(72, 8, YELLOW_F | BRIGHT_F | BLUE_B, "off");
89 }
90
updateditbox(displaymethod * d,stringvector * sv,int updateflags,int editwidth,int flags,char * title,int doupdate)91 void updateditbox(displaymethod * d, stringvector* sv, int updateflags, int editwidth, int flags, char* title, int doupdate)
92 {
93 /* update title if needed */
94 if (updateflags & U_TITLE) {
95 drawscrollbox(d, 1, 17, 0);
96 d->print_discrete(30 - (strlen(title) / 2), 4, 0x0a, title);
97 }
98
99 /* clear the scrollbox */
100 if (updateflags & U_TOP)
101 drawscrollbox(d, 3, 9, 0);
102 if (updateflags & U_CENTER)
103 drawscrollbox(d, 10, 8, 0);
104 if (updateflags & U_BOTTOM)
105 drawscrollbox(d, 11, 1, 0);
106
107 if (updateflags & (U_CENTER)) {
108 /* Draw the center */
109 displayline(9, 13, sv->cur->s, editwidth, flags, sv->cur->prev == NULL, d);
110 }
111
112 if (updateflags & (U_BOTTOM)) {
113 /* Draw bottom half */
114 int i;
115 stringnode* loopstr = sv->cur->next;
116 for (i = 1; i < 8 && loopstr != NULL; i++, loopstr = loopstr->next)
117 displayline(9, i + 13, loopstr->s, editwidth, flags, 0, d);
118
119 if (i < 8)
120 d->print_discrete(9, i + 13, 0x07, SLEADER);
121 }
122 if (updateflags & U_TOP) {
123 /* Draw top half */
124 int i;
125 stringnode* loopstr = sv->cur->prev;
126 for (i = -1; i > -8 && loopstr != NULL; i--, loopstr = loopstr->prev)
127 displayline(9, i + 13, loopstr->s, editwidth, flags, loopstr->prev == NULL, d);
128
129 if (!editwidth && loopstr == NULL && sv->first->s[0] == '@')
130 i++;
131
132 if (i > -8)
133 d->print_discrete(9, i + 13, 0x07, SLEADER);
134 }
135
136 /* Update the display */
137 if (doupdate)
138 d->update(3, 4, 51, 19);
139 }
140
141
142 /* Space to reserve for the saved file name */
143 #define SAVEFILENAME_LEN 1024
144
editbox(char * title,stringvector * sv,int editwidth,int flags,displaymethod * d)145 int editbox(char *title, stringvector * sv, int editwidth, int flags, displaymethod * d)
146 {
147 int key; /* the key */
148 int i, j; /* general counters */
149 int done = 0; /* true when editing/viewing is done */
150 int updateflags; /* flags to determine what needs update */
151 stringnode *centerstr; /* str in center of dialog */
152 stringnode *loopstr; /* node pointer for use in looping */
153 int selChar = 0;
154
155 /* vars only relating to editing */
156 int pos = 0; /* position in sv->cur->s */
157 char *tmpstr; /* temporary string for pushing */
158 char strbuf[80] = ""; /* general buffer */
159 static char savefilename[SAVEFILENAME_LEN] = "temp.zoc";
160
161 /* selection variables */
162 int selectFlag = 0; /* Status of the shift key */
163 int selPos = -1; /* Position of cursor when selection started; -1 when no selection */
164 int selLineOffset = 0; /* Offset of line where selection started;
165 positive when below centerstr, negative when above */
166
167 /* statics */
168 static int insertflag = 1; /* nonzero when in insert mode */
169 static int wrapwidth = 42; /* where to wrap */
170
171 /* if there is no string, add one */
172 if (sv->cur == NULL || sv->first == NULL || sv->last == NULL)
173 pushstring(sv, strcpy((char *) malloc(editwidth + 2), ""));
174
175 if (sv->cur == NULL)
176 return 0;
177
178 centerstr = sv->cur;
179
180 if (editwidth == EDITBOX_NOEDIT) {
181 d->cursorgo(9, 13);
182 /* Look for @title on first line */
183 if ((flags & EDITBOX_ZOCMODE) && sv->first != NULL && sv->first->s[0] == '@') {
184 /* Display the first line as the title, not in the box itself */
185 if (sv->first->s[1] != '\x0')
186 title = sv->first->s + 1; /* What's the harm? We're only looking. */
187 if (centerstr == sv->first)
188 centerstr = centerstr->next;
189 }
190 }
191
192 /* Check for NULL after advancing past @title, if we did so */
193 if (centerstr == NULL)
194 return 0;
195
196 drawscrollbox(d, 0, 0, 1);
197 updateflags = U_ALL;
198
199 while (!done) {
200 if (editwidth)
201 d->cursorgo(9 + pos, 13);
202
203 /* If in select mode, center line should be updated no matter what */
204 if (selPos != -1)
205 updateflags |= U_CENTER;
206
207 if (updateflags & U_PANEL && editwidth)
208 draweditpanel(insertflag, wrapwidth, flags & EDITBOX_ZOCMODE, d);
209
210 sv->cur = centerstr;
211 updateditbox(d, sv, updateflags, editwidth, flags, title, selPos == -1);
212 updateflags = U_NONE;
213
214 /* Draw highlighted text if applicable */
215 if (selPos != -1) {
216 int startPos = 0, endPos = 0;
217 if (selLineOffset > 0) {
218 startPos = pos;
219 endPos = strlen(centerstr->s);
220 } else if (selLineOffset < 0) {
221 startPos = 0;
222 endPos = pos;
223 } else {
224 if (selPos > pos) {
225 startPos = pos;
226 endPos = selPos;
227 } else {
228 startPos = selPos;
229 endPos = pos;
230 }
231 }
232 for (j = startPos; j < endPos; j++)
233 d->putch(9 + j, 13, centerstr->s[j], ZOC_HIGHLIGHT_COLOUR);
234
235 if (selLineOffset != 0) {
236 /* Draw meat lines */
237 if (selLineOffset > 0) {
238 for (i = 1, loopstr = centerstr->next; i < selLineOffset && i < 8 && loopstr != NULL; i++, loopstr = loopstr->next)
239 d->print_discrete(9, 13 + i, ZOC_HIGHLIGHT_COLOUR, loopstr->s);
240 } else {
241 for (i = -1, loopstr = centerstr->prev; i > selLineOffset && i > -8 && loopstr != NULL; i--, loopstr = loopstr->prev)
242 d->print_discrete(9, 13 + i, ZOC_HIGHLIGHT_COLOUR, loopstr->s);
243 }
244
245 /* Draw farthest line from centerstr */
246 if (i < 8 && i > -8 && loopstr != NULL) {
247 if (selLineOffset < 0) {
248 startPos = selPos;
249 endPos = strlen(loopstr->s);
250 } else if (selLineOffset > 0) {
251 startPos = 0;
252 endPos = selPos;
253 }
254 for (j = startPos; j < endPos; j++)
255 d->putch_discrete(9 + j, 13 + i, loopstr->s[j], ZOC_HIGHLIGHT_COLOUR);
256 }
257 }
258
259 /* Update the display */
260 d->update(3, 4, 51, 19);
261 }
262
263 /* Get the key */
264 key = d->getch();
265
266 selectFlag = d->shift();
267
268 /* If we just started selecting, remember where we started */
269 if (selectFlag && selPos == -1)
270 selPos = pos;
271
272 /* Keys which work when editing and browsing */
273 switch (key) {
274 case DKEY_UP: /* Up Arrow */
275 if (centerstr->prev != NULL && !(!editwidth && centerstr->prev == sv->first && sv->first->s[0] == '@')) {
276 centerstr = centerstr->prev;
277 if (pos > strlen(centerstr->s))
278 pos = strlen(centerstr->s);
279 if (selectFlag)
280 selLineOffset++;
281 updateflags = U_EDITAREA;
282 }
283 break;
284
285 case DKEY_DOWN: /* Down Arrow */
286 if (centerstr->next != NULL) {
287 centerstr = centerstr->next;
288 if (pos > strlen(centerstr->s))
289 pos = strlen(centerstr->s);
290 if (selectFlag)
291 selLineOffset--;
292 updateflags = U_EDITAREA;
293 }
294 break;
295
296 case DKEY_PAGEUP: /* Page Up */
297 for (i = 0; i < 7 && centerstr->prev != NULL && !(!editwidth && centerstr->prev == sv->first && sv->first->s[0] == '@'); i++) {
298 centerstr = centerstr->prev;
299 if (selectFlag)
300 selLineOffset++;
301 }
302 if (pos > strlen(centerstr->s))
303 pos = strlen(centerstr->s);
304 updateflags = U_EDITAREA;
305 break;
306
307 case DKEY_PAGEDOWN: /* Page Down */
308 for (i = 0; i < 7 && centerstr->next != NULL; i++) {
309 centerstr = centerstr->next;
310 if (selectFlag)
311 selLineOffset--;
312 }
313 if (pos > strlen(centerstr->s))
314 pos = strlen(centerstr->s);
315 updateflags = U_EDITAREA;
316 break;
317
318 case DKEY_CTRL_C:
319 case DKEY_CTRL_X:
320 /* Copy to register */
321 if (selPos != -1) {
322 stringnode *selStart = centerstr, *selEnd = centerstr;
323 int selStartPos, selEndPos;
324 selectionBounds bounds;
325
326 if (selLineOffset > 0) {
327 /* Other end of selection is below current line, move end down to meet it. */
328 selStartPos = pos;
329 selEndPos = selPos;
330 for (i = 0; i < selLineOffset; i++)
331 if (selEnd->next != NULL)
332 selEnd = selEnd->next;
333 } else if (selLineOffset < 0) {
334 /* Other end of selection is above current line, move end up to meet it. */
335 selStartPos = selPos;
336 selEndPos = pos;
337 for (i = 0; i > selLineOffset; i--)
338 if (selStart->prev != NULL)
339 selStart = selStart->prev;
340 } else {
341 /* Selection is only on current line: selStartPos gets the lesser of selPos & pos */
342 if (selPos > pos) {
343 selStartPos = pos;
344 selEndPos = selPos;
345 } else {
346 selStartPos = selPos;
347 selEndPos = pos;
348 }
349 }
350
351 bounds.startLine = selStart;
352 bounds.endLine = selEnd;
353 bounds.startPos = selStartPos;
354 bounds.endPos = selEndPos;
355
356 regyank('\"', bounds);
357 }
358 break;
359
360 case DKEY_ESC:
361 if (editwidth > EDITBOX_NOEDIT)
362 done = EDITBOX_OK;
363 else
364 done = EDITBOX_CANCEL;
365 break;
366 }
367
368 /* Keys pertaining to browsing only */
369 if (editwidth == EDITBOX_NOEDIT) {
370 switch (key) {
371 case DKEY_ENTER:
372 done = EDITBOX_OK;
373 break;
374 }
375
376 /* If movement is enabled... */
377 if (flags & EDITBOX_MOVEMENT) {
378 switch (key) {
379 case DKEY_BACKSPACE:
380 done = EDITBOX_BACK;
381 break;
382
383 case DKEY_RIGHT:
384 done = EDITBOX_FORWARD;
385 break;
386
387 case DKEY_LEFT:
388 done = EDITBOX_BACKWARD;
389 break;
390
391 case DKEY_F1:
392 done = EDITBOX_HELP;
393 break;
394 }
395 }
396 }
397
398 /* Keys pertaining to editing only */
399 if (editwidth > EDITBOX_NOEDIT) {
400 /* We are edititing! Yea! Fun time! */
401
402 switch (key) {
403 case DKEY_NONE:
404 break;
405
406 /********** Movement ***********/
407
408 case DKEY_LEFT: /* Left Arrow */
409 if (pos > 0)
410 pos--;
411 else {
412 /* Move to end of previous line (or current line) */
413 if (centerstr->prev != NULL) {
414 centerstr = centerstr->prev;
415 updateflags = U_EDITAREA;
416 }
417 pos = strlen(centerstr->s);
418 if (selectFlag)
419 selLineOffset++;
420 }
421 break;
422
423 case DKEY_RIGHT: /* Right Arrow */
424 if (pos < strlen(centerstr->s))
425 pos++;
426 else {
427 /* Move to begining of next line (or current line) */
428 if (centerstr->next != NULL) {
429 centerstr = centerstr->next;
430 updateflags = U_EDITAREA;
431 }
432 pos = 0;
433 if (selectFlag)
434 selLineOffset--;
435 }
436 break;
437
438 case DKEY_HOME: /* Home */
439 pos = 0;
440 break;
441
442 case DKEY_END: /* End */
443 pos = strlen(centerstr->s);
444 break;
445
446 case DKEY_UP:
447 case DKEY_DOWN:
448 case DKEY_PAGEUP:
449 case DKEY_PAGEDOWN:
450 case DKEY_CTRL_C:
451 /* Avoid inserting these keys */
452 break;
453
454 /********** Insert & Delete ***********/
455
456 case DKEY_INSERT:
457 /* Insert */
458 insertflag = !insertflag;
459 updateflags = U_PANEL;
460 break;
461
462 case DKEY_DELETE:
463 /* Delete */
464 if (pos < strlen(centerstr->s)) {
465 for (i = pos; i < strlen(centerstr->s); i++)
466 centerstr->s[i] = centerstr->s[i+1];
467 updateflags = U_CENTER;
468 }
469 else if (strlen(centerstr->s) == 0 && !(sv->first == sv->last)) {
470 /* This string is empty: destroy */
471 sv->cur = centerstr;
472 deletestring(sv);
473 centerstr = sv->cur;
474 pos = strlen(centerstr->s);
475 updateflags = U_EDITAREA;
476 }
477 else if (centerstr->next != NULL) {
478 if (strlen(centerstr->next->s) == 0) {
479 /* Next string is empty: destroy */
480 sv->cur = centerstr->next;
481 deletestring(sv);
482 updateflags = U_BOTTOM;
483 }
484 else if (strlen(centerstr->s) + 1 < wrapwidth) {
485 /* merge lines; wordwrap */
486 i = strlen(centerstr->s);
487 if (centerstr->s[i-1] != ' ' && centerstr->next->s[0] != ' ') {
488 /* add a space at the end */
489 centerstr->s[i] = ' ';
490 centerstr->s[++i] = 0;
491 }
492 sv->cur = centerstr->next;
493 tmpstr = removestring(sv);
494 sv->cur = centerstr;
495 pos = wordwrap(sv, tmpstr, i, -1, wrapwidth, editwidth);
496 centerstr = sv->cur;
497 free(tmpstr);
498 updateflags = U_CENTER | U_BOTTOM | U_TOP;
499 }
500 }
501 break;
502
503 /****** ZOC Mode & Wordwrap settings **********/
504
505 case DKEY_ALT_Z: /* alt-z - toggle ZOC mode */
506 flags ^= EDITBOX_ZOCMODE;
507 updateflags = U_PANEL | U_EDITAREA;
508 break;
509
510 case DKEY_ALT_MINUS: /* alt - */
511 if (wrapwidth > 10)
512 wrapwidth--;
513 else
514 wrapwidth = editwidth;
515 updateflags = U_PANEL;
516 break;
517
518 case DKEY_ALT_PLUS: /* alt + */
519 if (wrapwidth < editwidth)
520 wrapwidth++;
521 else
522 wrapwidth = 10;
523 updateflags = U_PANEL;
524 break;
525
526 /****** Help dialog ******/
527
528 case DKEY_F1: /* F1: help dialog */
529 /* Look for #command on current line for lookup in help */
530 i = pos;
531 while (i > 0 && centerstr->s[i] != '#')
532 i--;
533
534 if (centerstr->s[i] == '#') {
535 /* Copy the command onto tmpstr */
536 tmpstr = str_dup(centerstr->s + i + 1);
537 for (i = 0; tmpstr[i] != ' ' && tmpstr[i] != '\0'; i++)
538 ;
539 tmpstr[i] = '\0';
540
541 if (zztoopFindCommand(tmpstr) == -1) {
542 /* If it's not a valid command, don't bother looking for it */
543 tmpstr[0] = '\0';
544 }
545
546 /* Display the help file with the command as the topic */
547 helpsectiontopic("langref", tmpstr, d);
548
549 free(tmpstr);
550 } else {
551 /* Display the oop help file */
552 helpsectiontopic("langref", NULL, d);
553 }
554
555 updateflags = U_ALL;
556 break;
557
558 /********* ZZM Testing ********************/
559 case DKEY_CTRL_T:
560 case DKEY_ALT_T:
561 sv->cur = centerstr;
562 testMusic(sv, key == DKEY_CTRL_T, editwidth, flags, d);
563 updateflags = U_EDITAREA;
564 break;
565
566 /********* File access operations *********/
567 case DKEY_ALT_O: /* alt+o: open file */
568 case DKEY_ALT_I:
569 /* alt+i: insert file */
570 {
571 stringvector filetypelist;
572 char* filename = NULL;
573
574 initstringvector(&filetypelist);
575
576 pushstring(&filetypelist, "*.zoc");
577 pushstring(&filetypelist, "*.txt");
578 pushstring(&filetypelist, "*.hlp");
579 pushstring(&filetypelist, "*.zzm");
580 pushstring(&filetypelist, "*.*");
581 if (editbox("Select A File Type", &filetypelist, 0, 1, d) == 27) {
582 updateflags = U_EDITAREA | U_TITLE;
583 break;
584 }
585
586 if (filetypelist.cur != NULL)
587 filename =
588 filedialog(".", filetypelist.cur->s + 2,
589 (key == DKEY_ALT_O ?
590 "Open ZZT Object Code (ZOC) File" :
591 "Insert ZZT Object Code (ZOC) File"),
592 FTYPE_ALL, d);
593
594 if (filename != NULL && strlen(filename) != 0) {
595 stringvector newsvector;
596 newsvector = filetosvector(filename, wrapwidth, editwidth);
597 if (newsvector.first != NULL) {
598 if (key == DKEY_ALT_O) {
599 strcpy(savefilename, filename);
600 /* erase & replace sv */
601 deletestringvector(sv);
602 *sv = newsvector;
603 centerstr = sv->first;
604 } else {
605 /* insert newsvector before centerstr */
606 sv->cur = centerstr;
607 if (sv->cur == sv->first) {
608 /* first node */
609 sv->first = newsvector.first;
610 sv->cur->prev = newsvector.last;
611 newsvector.last->next = sv->cur;
612 centerstr = newsvector.first;
613 } else if (sv->cur->prev != NULL) {
614 /* middle/end node */
615 newsvector.first->prev = sv->cur->prev;
616 sv->cur->prev->next = newsvector.first;
617 newsvector.last->next = sv->cur;
618 sv->cur->prev = newsvector.last;
619 centerstr = newsvector.first;
620 } else {
621 /* this code should be unreachable */
622 deletestringvector(&newsvector);
623 }
624 } /* esle alt-i */
625 } /* fi file selected */
626 } /* fi not empty */
627 free(filename);
628 removestringvector(&filetypelist);
629 } /* block */
630 updateflags = U_EDITAREA | U_TITLE | U_PANEL;
631 break;
632
633 case DKEY_ALT_S: /* alt-s: save to file */
634 {
635 char* filename;
636 filename = filenamedialog(savefilename, "", "Save Object Code As",
637 1, d);
638 if (filename != NULL) {
639 /* Save to the file */
640 svectortofile(sv, filename);
641 /* Remember the file name */
642 strncpy(savefilename, filename, SAVEFILENAME_LEN - 1);
643 savefilename[SAVEFILENAME_LEN - 1] = '\x0';
644
645 free(filename);
646 }
647 }
648 updateflags = U_EDITAREA | U_PANEL | U_PANEL;
649 break;
650
651 case DKEY_ALT_M: /* alt-m: load .zzm music */
652 {
653 char* filename;
654 filename = filedialog(".", "zzm", "Choose ZZT Music (ZZM) File",
655 FTYPE_ALL, d);
656 if (filename != NULL) {
657 stringvector zzmv;
658 zzmv = filetosvector(filename, 80, 80);
659 if (zzmv.first != NULL) {
660 stringvector song;
661 song = zzmpullsong(&zzmv, zzmpicksong(&zzmv, d));
662 if (song.first != NULL) {
663 /* copy song into sv */
664 sv->cur = centerstr;
665 for (song.cur = song.first; song.cur != NULL; song.cur = song.cur->next) {
666 tmpstr = (char*) malloc(editwidth + 2);
667
668 if (flags & EDITBOX_ZOCMODE) {
669 strcpy(tmpstr, "#play ");
670 strncat(tmpstr, song.cur->s, editwidth - 6);
671 } else {
672 strncpy(tmpstr, song.cur->s, editwidth);
673 }
674
675 preinsertstring(sv, tmpstr);
676 }
677 deletestringvector(&song);
678 }
679 deletestringvector(&zzmv);
680 }
681 }
682 free(filename);
683 }
684 updateflags = U_EDITAREA | U_TITLE | U_PANEL;
685 break;
686
687 case DKEY_CTRL_R: /* ctrl-r: rip music */
688 {
689 /* This is mostly worthless just now */
690 stringvector ripped;
691 sv->cur = centerstr;
692 ripped = zzmripsong(sv, 4);
693 scrolldialog("Ripped Music", &ripped, d);
694 deletestringvector(&ripped);
695 updateflags = U_ALL;
696 }
697 break;
698
699 /******** Cut operation *********/
700
701 case DKEY_CTRL_DELETE: /* ctrl-delete: clear selected text */
702 case DKEY_CTRL_X: /* ctrl-x: cut selected text */
703 sv->cur = centerstr;
704 /* Destroy the meat of the selection */
705 if (selPos != -1) {
706 int selStartPos, selEndPos, offset = selLineOffset;
707 if (offset < 0) {
708 /* Other end is above centerstr */
709 offset = -offset;
710 selStartPos = selPos;
711 selEndPos = pos;
712 /* Move back to top of selection */
713 for (i = 0; i < offset; i++) {
714 if (sv->cur->prev != NULL)
715 sv->cur = sv->cur->prev;
716 }
717 /* Change centerstr to reflect the top of the selection */
718 centerstr = sv->cur;
719 } else {
720 selStartPos = pos;
721 selEndPos = selPos;
722 }
723 if (offset == 0) {
724 /* Only one line to work with */
725 int deltaPos;
726
727 /* Reverse selStartPos and selEndPos if start is bigger */
728 if (selStartPos > selEndPos) {
729 int swapPos = selStartPos;
730 selStartPos = selEndPos;
731 selEndPos = swapPos;
732 }
733
734 /* Remove everything between selStartPos and selEndPos */
735 deltaPos = selEndPos - selStartPos;
736 for (i = selEndPos; i < strlen(centerstr->s); i++) {
737 centerstr->s[i - deltaPos] = centerstr->s[i];
738 }
739 centerstr->s[i - deltaPos] = '\0';
740
741 /* Move the cursor to the starting position of the cut */
742 pos = selStartPos;
743 } else {
744 /* Multiple lines were involved */
745
746 /* Remove lines following the first line of the block */
747 sv->cur = centerstr->next;
748 for (i = 0; i + 1 < offset; i++) {
749 deletestring(sv);
750 }
751
752 /* Remove the string at the end of the cut */
753 sv->cur = centerstr->next;
754 tmpstr = removestring(sv);
755 /* Remove first selEndPos chars from end string */
756 for (i = 0; i < (strlen(tmpstr) - selEndPos); i++)
757 tmpstr[i] = tmpstr[i+selEndPos];
758 tmpstr[i] = 0;
759
760 /* Truncate the string at the start of the cut */
761 sv->cur = centerstr;
762 sv->cur->s[selStartPos] = '\0';
763 /* Wordwrap the end string onto this one */
764 /* The -1 tells wordwrap to track the cursor position at
765 * the beginning of tmpstr. Negative tracking values should
766 * be used only by wordwrap for internal purposes, but
767 * necessity warrents in this case. vv */
768 pos = wordwrap(sv, tmpstr, selStartPos, -1, wrapwidth, editwidth);
769 centerstr = sv->cur; /* Follow cursor */
770 /* tmpstr is our responsability */
771 free(tmpstr);
772 }
773 updateflags = U_EDITAREA;
774 }
775 break;
776
777 case DKEY_CTRL_V: /* ctrl-v: paste register */
778 sv->cur = centerstr;
779 pos = regput('\"', sv, pos, wrapwidth, editwidth);
780 centerstr = sv->cur;
781 updateflags = U_EDITAREA;
782 break;
783
784 case DKEY_TAB: /* Tab */
785 /* determine tab amount */
786 j = 4 - (pos % 4);
787 if (strlen(centerstr->s) + j < (wrapwidth?wrapwidth:editwidth)) {
788 /* insert if there is room */
789 for (i = strlen(centerstr->s) + j; i > pos; i--)
790 centerstr->s[i] = centerstr->s[i-j];
791 for (i = 0; i < j; i++)
792 centerstr->s[pos++] = ' ';
793 updateflags = U_CENTER;
794 }
795 else {
796 /* no room; wordwrap */
797 for (i = 0; i < j; i++)
798 strbuf[i] = ' ';
799 strbuf[i] = 0;
800 sv->cur = centerstr;
801 pos = wordwrap(sv, strbuf, pos, pos, wrapwidth, editwidth);
802 centerstr = sv->cur;
803 updateflags = U_EDITAREA;
804 }
805 break;
806
807 case DKEY_ENTER:
808 /* Enter */
809 tmpstr = (char*) malloc(editwidth + 2);
810 for (i = pos, j = 0; i < strlen(centerstr->s); i++, j++)
811 tmpstr[j] = centerstr->s[i];
812 centerstr->s[pos] = 0;
813
814 tmpstr[j] = 0;
815 sv->cur = centerstr;
816 insertstring(sv, tmpstr);
817 centerstr = centerstr->next;
818 pos = 0;
819 updateflags = U_EDITAREA;
820 break;
821
822 case DKEY_BACKSPACE:
823 /* Backspace */
824 if (pos > 0) {
825 for (i = pos - 1; i < strlen(centerstr->s); i++)
826 centerstr->s[i] = centerstr->s[i+1];
827 pos--;
828 updateflags = U_CENTER;
829 }
830 else if (centerstr->prev != NULL) {
831 if (strlen(centerstr->s) == 0) {
832 /* remove current line & move up & to eol */
833 sv->cur = centerstr;
834 centerstr = centerstr->prev;
835 pos = strlen(centerstr->s);
836 deletestring(sv);
837 updateflags = U_TOP | U_CENTER;
838 }
839 else if (strlen(centerstr->prev->s) == 0) {
840 /* remove previous line */
841 sv->cur = centerstr->prev;
842 deletestring(sv);
843 /* update center too, in case @ line has moved to top now */
844 updateflags = U_TOP | U_CENTER;
845 }
846 else if (strlen(centerstr->prev->s) + 1 < wrapwidth) {
847 /* merge lines; wordwrap */
848 i = strlen(centerstr->prev->s);
849 if (centerstr->prev->s[i-1] != ' ' && centerstr->s[0] != ' ') {
850 /* add a space at the end */
851 centerstr->prev->s[i] = ' ';
852 centerstr->prev->s[i + 1] = 0;
853 }
854 sv->cur = centerstr->prev;
855 tmpstr = removestring(sv);
856 sv->cur = centerstr;
857 pos = wordwrap(sv, tmpstr, 0, 0, wrapwidth, editwidth);
858 centerstr = sv->cur;
859 free(tmpstr);
860 updateflags = U_EDITAREA;
861 }
862 }
863 break;
864
865 case DKEY_CTRL_Y: /* ctrl-y: delete line */
866 pos = 0;
867 sv->cur = centerstr;
868 if (centerstr->next != NULL) {
869 centerstr = centerstr->next;
870 deletestring(sv);
871 updateflags = U_CENTER | U_BOTTOM;
872 }
873 else if (centerstr->prev != NULL) {
874 centerstr = centerstr->prev;
875 deletestring(sv);
876 updateflags = U_TOP | U_CENTER;
877 }
878 else {
879 centerstr->s[0] = 0;
880 updateflags = U_CENTER;
881 }
882 break;
883
884 case DKEY_ESC: /* escape when done */
885 done = EDITBOX_OK;
886 break;
887
888 case DKEY_CTRL_A: /* ctrl-a: insert ascii char/decimal-value */
889 strcpy(strbuf, centerstr->s);
890 updateflags = U_EDITAREA;
891
892 if (str_equ(strbuf, "#char", STREQU_UNCASE | STREQU_RFRONT)) {
893 /* append dec value for ascii char */
894
895 sscanf(strbuf + 5, "%d", &selChar);
896 key = charselect(d, selChar);
897 if (key == -1)
898 break;
899 selChar = key;
900 centerstr->s[5] = ' ';
901 centerstr->s[6] = 0;
902
903 /* change c to a string */
904 sprintf(strbuf, "%d", selChar);
905 strcat(centerstr->s, strbuf);
906 pos = strlen(centerstr->s);
907 updateflags = U_EDITAREA;
908 break;
909 }
910 else {
911 /* ctrl-a: insert ascii char */
912 key = charselect(d, selChar);
913 if (key == -1)
914 break;
915 else
916 selChar = key;
917 }
918 /* no break; we just changed the key & want to insert it */
919
920 default:
921 key = key & 0xFF; /* Clear all but the first 8 bits */
922 /* Normal/weird char for insert/replace */
923 if (insertflag) {
924 /* insert */
925 if (strlen(centerstr->s) < (wrapwidth?wrapwidth:editwidth)) {
926 /* insert if there is room */
927 for (i = strlen(centerstr->s) + 1; i > pos; i--)
928 centerstr->s[i] = centerstr->s[i-1];
929 centerstr->s[pos++] = key;
930 updateflags |= U_CENTER;
931 }
932 else if (wrapwidth) {
933 /* no room; wordwrap */
934 strbuf[0] = key;
935 strbuf[1] = 0;
936 sv->cur = centerstr;
937 pos = wordwrap(sv, strbuf, pos, pos, wrapwidth, editwidth);
938 centerstr = sv->cur;
939 updateflags = U_EDITAREA;
940 }
941 }
942 else {
943 /* easy replace */
944 if (centerstr->s[pos] == 0) {
945 if (strlen(centerstr->s) < (wrapwidth?wrapwidth:editwidth)) {
946 centerstr->s[pos+1] = 0;
947 centerstr->s[pos++] = key;
948 updateflags |= U_CENTER;
949 }
950 else if (wrapwidth) {
951 /* no room; wordwrap */
952 strbuf[0] = key;
953 strbuf[1] = 0;
954 sv->cur = centerstr;
955 pos = wordwrap(sv, strbuf, pos, pos, wrapwidth, editwidth);
956 centerstr = sv->cur;
957 updateflags = U_EDITAREA;
958 }
959 }
960 else {
961 centerstr->s[pos++] = key;
962 updateflags |= U_CENTER;
963 }
964 } /* esle replace */
965 break;
966 }
967 } /* esle in editmode */
968
969 /* if the shift key is not still held down and we are selecting,
970 * then stop select mode */
971 /* also stop if true ASCII key was pressed and selection is active */
972 if ((!selectFlag && selPos != -1) || (key < 0x7F && selPos != -1)) {
973 selPos = -1;
974 selLineOffset = 0;
975 updateflags |= U_EDITAREA;
976 }
977 } /* elihw */
978
979 sv->cur = centerstr;
980
981 return done;
982 }
983
testMusic(stringvector * sv,int slur,int editwidth,int flags,displaymethod * d)984 void testMusic(stringvector* sv, int slur, int editwidth, int flags, displaymethod* d)
985 {
986 int done;
987 #ifdef SDL
988 SDL_AudioSpec spec;
989
990 /* IF opening the audio device fails, return now before we crash something. */
991 if (OpenSynth(&spec))
992 return;
993 #endif
994
995 done = 0;
996
997 /* Loop through the stringvector looking for #play statements */
998 while (sv->cur != NULL && !done) {
999 char* tune = strstr(sv->cur->s, "#");
1000 if (tune != NULL && str_equ(tune, "#play ", STREQU_UNCASE | STREQU_RFRONT)) {
1001 /* Current note and settings */
1002 musicalNote note = zzmGetDefaultNote();
1003 musicSettings settings = zzmGetDefaultSettings();
1004
1005 #ifdef DOS
1006 int xoff = tune - sv->cur->s;
1007 #endif
1008 tune += 6; /* Advance to notes! */
1009
1010 /* Change the slur setting */
1011 note.slur = slur;
1012
1013 /* Update everything because we have likely shifted to a new line. */
1014 updateditbox(d, sv, U_EDITAREA, editwidth, flags, "", 1);
1015
1016 while (note.src_pos < strlen(tune) && !done) {
1017 #ifdef DOS
1018 int oldpos = note.src_pos;
1019 char* strpart;
1020 #endif
1021
1022 if (d->getkey() != DKEY_NONE)
1023 done = 1;
1024
1025 note = zzmGetNote(tune, note);
1026
1027 #ifdef DOS
1028 /* In DOS, display everything as we go along */
1029 /* Display the whole line */
1030 updateditbox(d, sv, U_CENTER, editwidth, flags, "", 1);
1031
1032 /* Display the part of the string which will be played now */
1033 strpart = str_duplen(tune + oldpos, note.src_pos - oldpos);
1034 d->print(oldpos + 15 + xoff, 13, ZOC_MPLAY_COLOUR, strpart);
1035 free(strpart);
1036 d->cursorgo(oldpos + 15 + xoff, 13);
1037
1038 pcSpeakerPlayNote(note, settings);
1039 #elif defined SDL
1040 SynthPlayNote(spec, note, settings);
1041 #endif
1042 }
1043 }
1044 sv->cur = sv->cur->next;
1045 }
1046
1047 #ifdef SDL
1048 /* TODO: instead of just sitting here, display the progress of playback */
1049 /* Wait until the music is done or the user presses a key */
1050 while (!IsSynthBufferEmpty() && d->getkey() == DKEY_NONE)
1051 ;
1052
1053 CloseSynth();
1054 #elif defined DOS
1055 pcSpeakerFinish();
1056 #endif
1057 }
1058
1059 /***************************************************************************/
1060 /**** Syntax highlighting functions ****************************************/
1061 /***************************************************************************/
1062
1063 /* Draw highlighted syntax using ZOOPDraw */
1064 #include "zoopdraw.h"
1065
displayzoc(int x,int y,char * s,int format,int firstline,displaymethod * d)1066 void displayzoc(int x, int y, char *s, int format, int firstline, displaymethod * d)
1067 {
1068 ZZTOOPparser * parser = zztoopCreateParser(s);
1069 ZZTOOPdrawer drawer;
1070
1071 zztoopInitDrawer(&drawer);
1072 drawer.display = d;
1073
1074 if (format) {
1075 /* Non-strict help */
1076 parser->flags = ZOOPFLAG_HELP;
1077 drawer.helpformatting = 1;
1078 }
1079
1080 if (firstline)
1081 parser->flags |= ZOOPFLAG_FIRSTLINE;
1082
1083 drawer.x = x; drawer.y = y;
1084 drawer.length = 42; /* TODO: replace 42 w/ constant or something */
1085
1086 zztoopDraw(drawer, zztoopParseLine(parser));
1087
1088 zztoopDeleteParser(parser);
1089 }
1090
1091
1092