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