1 /*
2 * Schism Tracker - a cross-platform Impulse Tracker clone
3 * copyright (c) 2003-2005 Storlek <storlek@rigelseven.com>
4 * copyright (c) 2005-2008 Mrs. Brisby <mrs.brisby@nimh.org>
5 * copyright (c) 2009 Storlek & Mrs. Brisby
6 * copyright (c) 2010-2012 Storlek
7 * URL: http://schismtracker.org/
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 */
23
24 /* --->> WARNING <<---
25 *
26 * This is an excellent example of how NOT to write a text editor.
27 * IMHO, the best way to add a song message is by writing it in some
28 * other program and attaching it to the song with something like
29 * ZaStaR's ITTXT utility (hmm, maybe I should rewrite that, too ^_^) so
30 * I'm not *really* concerned about the fact that this code completely
31 * sucks. Just remember, this ain't Xcode. */
32
33 #include "headers.h"
34
35 #include "song.h"
36 #include "clippy.h"
37
38 #include <ctype.h>
39 #include <assert.h>
40
41 /* --------------------------------------------------------------------- */
42
43 static struct widget widgets_message[1];
44
45 static int top_line = 0;
46 static int cursor_line = 0;
47 static int cursor_char = 0;
48 /* this is the absolute cursor position from top of message.
49 * (should be updated whenever cursor_line/cursor_char change) */
50 static int cursor_pos = 0;
51
52 static int edit_mode = 0;
53
54 /* nonzero => message should use the alternate font */
55 static int message_extfont = 1;
56
57 /* This is a bit weird... Impulse Tracker usually wraps at 74, but if
58 * the line doesn't have any spaces in it (like in a solid line of
59 * dashes or something) it gets wrapped at 75. I'm using 75 for
60 * everything because it's always nice to have a bit extra space :) */
61 #define LINE_WRAP 75
62
63 /* --------------------------------------------------------------------- */
64
65 static int message_handle_key_editmode(struct key_event * k);
66 static int message_handle_key_viewmode(struct key_event * k);
67
68 /* --------------------------------------------------------------------- */
69
70 /* returns the number of characters on the nth line of text, setting ptr
71 * to the first character on the line. if it there are fewer than n
72 * lines, ptr is set to the \0 at the end of the string, and the
73 * function returns -1. note: if *ptr == text, weird things will
74 * probably happen, so don't do that. */
get_nth_line(char * text,int n,char ** ptr)75 static int get_nth_line(char *text, int n, char **ptr)
76 {
77 char *tmp;
78
79 assert(text != NULL);
80
81 *ptr = text;
82 while (n > 0) {
83 n--;
84 *ptr = strpbrk(*ptr, "\xd\xa");
85 if (!(*ptr)) {
86 *ptr = text + strlen(text);
87 return -1;
88 }
89 if ((*ptr)[0] == 13 && (*ptr)[1] == 10)
90 *ptr += 2;
91 else
92 (*ptr)++;
93 }
94
95 tmp = strpbrk(*ptr, "\xd\xa");
96 return (tmp ? (unsigned) (tmp - *ptr) : strlen(*ptr));
97 }
set_absolute_position(char * text,int pos,int * line,int * ch)98 static void set_absolute_position(char *text, int pos, int *line, int *ch)
99 {
100 int len;
101 char *ptr;
102
103 *line = *ch = 0;
104 ptr = NULL;
105 while (pos > 0) {
106 len = get_nth_line(text, *line, &ptr);
107 if (len < 0) {
108 /* end of file */
109 (*line) = (*line) - 1;
110 if (*line < 0) *line = 0;
111 len = get_nth_line(text, *line, &ptr);
112 if (len < 0) {
113 *ch = 0;
114 } else {
115 *ch = len;
116 }
117 pos = 0;
118
119 } else if (len >= pos) {
120 *ch = pos;
121 pos = 0;
122 } else {
123 pos -= (len+1); /* EOC */
124 (*line) = (*line) + 1;
125 }
126 }
127 }
get_absolute_position(char * text,int line,int character)128 static int get_absolute_position(char *text, int line, int character)
129 {
130 int len;
131 char *ptr;
132
133 ptr = NULL;
134 len = get_nth_line(text, line, &ptr);
135 if (len < 0) {
136 return 0;
137 }
138 /* hrm... what if cursor_char > len? */
139 return (ptr - text) + character;
140 }
141
142 /* --------------------------------------------------------------------- */
143
message_reposition(void)144 static void message_reposition(void)
145 {
146 if (cursor_line < top_line) {
147 top_line = cursor_line;
148 } else if (cursor_line > top_line + 34) {
149 top_line = cursor_line - 34;
150 }
151 }
152
153 /* --------------------------------------------------------------------- */
154
155 /* returns 1 if a character was actually added */
message_add_char(int newchar,int position)156 static int message_add_char(int newchar, int position)
157 {
158 int len = strlen(current_song->message);
159
160 if (len == MAX_MESSAGE) {
161 dialog_create(DIALOG_OK, " Song message too long! ", NULL, NULL, 0, NULL);
162 return 0;
163 }
164 if (position < 0 || position > len) {
165 log_appendf(4, "message_add_char: position=%d, len=%d - shouldn't happen!", position, len);
166 return 0;
167 }
168
169 memmove(current_song->message + position + 1, current_song->message + position, len - position);
170 current_song->message[len + 1] = 0;
171 current_song->message[position] = (unsigned char)newchar;
172 return 1;
173 }
174
175 /* this returns the new length of the line */
message_wrap_line(char * bol_ptr)176 static int message_wrap_line(char *bol_ptr)
177 {
178 char *eol_ptr;
179 char *last_space = NULL;
180 char *tmp = bol_ptr;
181
182 if (!bol_ptr)
183 /* shouldn't happen, but... */
184 return 0;
185
186 eol_ptr = strpbrk(bol_ptr, "\xd\xa");
187 if (!eol_ptr)
188 eol_ptr = bol_ptr + strlen(bol_ptr);
189
190 for (;;) {
191 tmp = strpbrk((tmp + 1), " \t");
192 if (tmp == NULL || tmp > eol_ptr
193 || tmp - bol_ptr > LINE_WRAP)
194 break;
195 last_space = tmp;
196 }
197
198 if (last_space) {
199 *last_space = 13;
200 return last_space - bol_ptr;
201 } else {
202 /* what, no spaces to cut at? chop it mercilessly. */
203 if (message_add_char(13, bol_ptr + LINE_WRAP - current_song->message)
204 == 0)
205 /* ack, the message is too long to wrap the line!
206 * gonna have to resort to something ugly. */
207 bol_ptr[LINE_WRAP] = 13;
208 return LINE_WRAP;
209 }
210 }
211
212 /* --------------------------------------------------------------------- */
text(char * line,int len,int n)213 static void text(char *line, int len, int n)
214 {
215 unsigned char ch;
216 int fg = (message_extfont ? 12 : 6);
217 int i;
218
219 for (i = 0; line[i] && i < len; i++) {
220 ch = line[i];
221
222 if (ch == ' ') {
223 draw_char(' ', 2+i, 13+n, 3,0);
224 } else {
225 (message_extfont ? draw_char_bios : draw_char)(ch, 2+i, 13+n, fg, 0);
226 }
227 }
228 }
229
message_draw(void)230 static void message_draw(void)
231 {
232 char *line, *prevline = current_song->message;
233 int len = get_nth_line(current_song->message, top_line, &line);
234 int n, cp, clipl, clipr;
235 int skipc, cutc;
236
237 draw_fill_chars(2, 13, 77, 47, 0);
238
239 if (clippy_owner(CLIPPY_SELECT) == widgets_message) {
240 clipl = widgets_message[0].clip_start;
241 clipr = widgets_message[0].clip_end;
242 if (clipl > clipr) {
243 cp = clipl;
244 clipl = clipr;
245 clipr = cp;
246 }
247 } else {
248 clipl = clipr = -1;
249 }
250
251 for (n = 0; n < 35; n++) {
252 if (len < 0) {
253 break;
254 } else if (len > 0) {
255 /* FIXME | shouldn't need this check here,
256 * FIXME | because the line should already be
257 * FIXME | short enough to fit */
258 if (len > LINE_WRAP)
259 len = LINE_WRAP;
260 text(line, len, n);
261
262 if (clipl > -1) {
263 cp = line - current_song->message;
264 skipc = clipl - cp;
265 cutc = clipr - clipl;
266 if (skipc < 0) {
267 cutc += skipc; /* ... -skipc */
268 skipc = 0;
269 }
270 if (cutc < 0) cutc = 0;
271 if (cutc > (len-skipc)) cutc = (len-skipc);
272 if (cutc > 0 && skipc < len) {
273 if (message_extfont)
274 draw_text_bios_len(line+skipc, cutc, 2+skipc, 13 + n, 6, 8);
275 else
276 draw_text_len(line+skipc, cutc, 2+skipc, 13 + n, 6, 8);
277 }
278 }
279 }
280 if (edit_mode) {
281 draw_char(20, 2 + len, 13 + n, 1, 0);
282 }
283 prevline = line;
284 len = get_nth_line(prevline, 1, &line);
285 }
286
287 if (edit_mode && len < 0) {
288 /* end of the message */
289 len = get_nth_line(prevline, 0, &line);
290 /* FIXME: see above */
291 if (len > LINE_WRAP)
292 len = LINE_WRAP;
293 draw_char(20, 2 + len, 13 + n - 1, 2, 0);
294 }
295
296 if (edit_mode) {
297 /* draw the cursor */
298 len = get_nth_line(current_song->message, cursor_line, &line);
299
300 /* FIXME: ... ugh */
301 if (len > LINE_WRAP)
302 len = LINE_WRAP;
303 if (cursor_char > LINE_WRAP + 1)
304 cursor_char = LINE_WRAP + 1;
305
306 if (cursor_char >= len) {
307 (message_extfont ? draw_char_bios : draw_char)
308 (20, 2 + cursor_char, 13 + (cursor_line - top_line), 0, 3);
309 } else {
310 (message_extfont ? draw_char_bios : draw_char)
311 (line[cursor_char], 2 + cursor_char, 13 + (cursor_line - top_line), 8, 3);
312 }
313 }
314 }
315
316 /* --------------------------------------------------------------------- */
317
message_set_editmode(void)318 static inline void message_set_editmode(void)
319 {
320 edit_mode = 1;
321 widgets_message[0].accept_text = 1;
322 top_line = cursor_line = cursor_char = cursor_pos = 0;
323 widgets_message[0].d.other.handle_key = message_handle_key_editmode;
324
325 status.flags |= NEED_UPDATE;
326 }
327
message_set_viewmode(void)328 static inline void message_set_viewmode(void)
329 {
330 edit_mode = 0;
331 widgets_message[0].accept_text = 0;
332 widgets_message[0].d.other.handle_key = message_handle_key_viewmode;
333
334 status.flags |= NEED_UPDATE;
335 }
336
337 /* --------------------------------------------------------------------- */
338
message_insert_char(int c)339 static void message_insert_char(int c)
340 {
341 char *ptr;
342 int n;
343
344 if (!edit_mode)
345 return;
346
347 memused_songchanged();
348 if (c == '\t') {
349 /* Find the number of characters until the next tab stop.
350 * (This is new behaviour; Impulse Tracker just inserts
351 * eight characters regardless of the cursor position.) */
352 n = 8 - cursor_char % 8;
353 if (cursor_char + n > LINE_WRAP) {
354 message_insert_char('\r');
355 } else {
356 do {
357 if (!message_add_char(' ', cursor_pos))
358 break;
359 cursor_char++;
360 cursor_pos++;
361 n--;
362 } while (n);
363 }
364 } else if (c < 32 && c != '\r') {
365 return;
366 } else {
367 if (!message_add_char(c, cursor_pos))
368 return;
369 cursor_pos++;
370 if (c == '\r') {
371 cursor_char = 0;
372 cursor_line++;
373 } else {
374 cursor_char++;
375 }
376 }
377 if (get_nth_line(current_song->message, cursor_line, &ptr) >= LINE_WRAP) {
378 message_wrap_line(ptr);
379 }
380 if (cursor_char >= LINE_WRAP) {
381 cursor_char = get_nth_line(current_song->message, ++cursor_line, &ptr);
382 cursor_pos = get_absolute_position(current_song->message, cursor_line, cursor_char);
383 }
384
385 message_reposition();
386 status.flags |= NEED_UPDATE | SONG_NEEDS_SAVE;
387 }
388
message_delete_char(void)389 static void message_delete_char(void)
390 {
391 int len = strlen(current_song->message);
392 char *ptr;
393
394 if (cursor_pos == 0)
395 return;
396 memmove(current_song->message + cursor_pos - 1, current_song->message + cursor_pos,
397 len - cursor_pos + 1);
398 current_song->message[MAX_MESSAGE] = 0;
399 cursor_pos--;
400 if (cursor_char == 0) {
401 cursor_line--;
402 cursor_char = get_nth_line(current_song->message, cursor_line, &ptr);
403 } else {
404 cursor_char--;
405 }
406
407 message_reposition();
408 status.flags |= NEED_UPDATE | SONG_NEEDS_SAVE;
409 }
410
message_delete_next_char(void)411 static void message_delete_next_char(void)
412 {
413 int len = strlen(current_song->message);
414
415 if (cursor_pos == len)
416 return;
417 memmove(current_song->message + cursor_pos, current_song->message + cursor_pos + 1,
418 len - cursor_pos);
419 current_song->message[MAX_MESSAGE] = 0;
420
421 status.flags |= NEED_UPDATE | SONG_NEEDS_SAVE;
422 }
423
message_delete_line(void)424 static void message_delete_line(void)
425 {
426 int len;
427 int movelen;
428 char *ptr;
429
430 len = get_nth_line(current_song->message, cursor_line, &ptr);
431 if (len < 0)
432 return;
433 if (ptr[len] == 13 && ptr[len + 1] == 10)
434 len++;
435 movelen = (current_song->message + strlen(current_song->message) - ptr);
436 if (movelen == 0)
437 return;
438 memmove(ptr, ptr + len + 1, movelen);
439 len = get_nth_line(current_song->message, cursor_line, &ptr);
440 if (cursor_char > len) {
441 cursor_char = len;
442 cursor_pos = get_absolute_position(current_song->message, cursor_line, cursor_char);
443 }
444 message_reposition();
445 status.flags |= NEED_UPDATE | SONG_NEEDS_SAVE;
446 }
447
message_clear(UNUSED void * data)448 static void message_clear(UNUSED void *data)
449 {
450 current_song->message[0] = 0;
451 memused_songchanged();
452 message_set_viewmode();
453 status.flags |= SONG_NEEDS_SAVE;
454 }
455
456 /* --------------------------------------------------------------------- */
457
prompt_message_clear(void)458 static void prompt_message_clear(void)
459 {
460 dialog_create(DIALOG_OK_CANCEL, "Clear song message?", message_clear, NULL, 1, NULL);
461 }
462
463 /* --------------------------------------------------------------------- */
464
message_handle_key_viewmode(struct key_event * k)465 static int message_handle_key_viewmode(struct key_event * k)
466 {
467 if (k->state == KEY_PRESS) {
468 if (k->mouse == MOUSE_SCROLL_UP) {
469 top_line -= MOUSE_SCROLL_LINES;
470 } else if (k->mouse == MOUSE_SCROLL_DOWN) {
471 top_line += MOUSE_SCROLL_LINES;
472 } else if (k->mouse == MOUSE_CLICK) {
473 message_set_editmode();
474 return message_handle_key_editmode(k);
475 }
476 }
477
478 switch (k->sym) {
479 case SDLK_UP:
480 if (k->state == KEY_RELEASE)
481 return 0;
482 top_line--;
483 break;
484 case SDLK_DOWN:
485 if (k->state == KEY_RELEASE)
486 return 0;
487 top_line++;
488 break;
489 case SDLK_PAGEUP:
490 if (k->state == KEY_RELEASE)
491 return 0;
492 top_line -= 35;
493 break;
494 case SDLK_PAGEDOWN:
495 if (k->state == KEY_RELEASE)
496 return 0;
497 top_line += 35;
498 break;
499 case SDLK_HOME:
500 if (k->state == KEY_RELEASE)
501 return 0;
502 top_line = 0;
503 break;
504 case SDLK_END:
505 if (k->state == KEY_RELEASE)
506 return 0;
507 top_line = get_num_lines(current_song->message) - 34;
508 break;
509 case SDLK_t:
510 if (k->state == KEY_RELEASE)
511 return 0;
512 if (k->mod & KMOD_CTRL) {
513 message_extfont = !message_extfont;
514 break;
515 }
516 return 1;
517 case SDLK_RETURN:
518 if (k->state == KEY_PRESS)
519 return 0;
520 message_set_editmode();
521 return 1;
522 default:
523 return 0;
524 }
525
526 if (top_line < 0)
527 top_line = 0;
528
529 status.flags |= NEED_UPDATE;
530
531 return 1;
532 }
_delete_selection(void)533 static void _delete_selection(void)
534 {
535 int len = strlen(current_song->message);
536 int eat;
537
538 cursor_pos = widgets_message[0].clip_start;
539 if (cursor_pos > widgets_message[0].clip_end) {
540 cursor_pos = widgets_message[0].clip_end;
541 eat = widgets_message[0].clip_start - cursor_pos;
542 } else {
543 eat = widgets_message[0].clip_end - cursor_pos;
544 }
545 clippy_select(NULL, NULL, 0);
546 if (cursor_pos == len)
547 return;
548 memmove(current_song->message + cursor_pos, current_song->message + cursor_pos + eat + 1,
549 ((len - cursor_pos) - eat)+1);
550 current_song->message[MAX_MESSAGE] = 0;
551 set_absolute_position(current_song->message, cursor_pos, &cursor_line, &cursor_char);
552 message_reposition();
553
554 status.flags |= NEED_UPDATE | SONG_NEEDS_SAVE;
555 }
556
message_handle_key_editmode(struct key_event * k)557 static int message_handle_key_editmode(struct key_event * k)
558 {
559 int line_len, num_lines = -1;
560 int new_cursor_line = cursor_line;
561 int new_cursor_char = cursor_char;
562 char *ptr;
563 int doing_drag = 0;
564 int clipl, clipr, cp;
565
566 if (k->mouse == MOUSE_SCROLL_UP) {
567 if (k->state == KEY_RELEASE)
568 return 0;
569 new_cursor_line -= MOUSE_SCROLL_LINES;
570 } else if (k->mouse == MOUSE_SCROLL_DOWN) {
571 if (k->state == KEY_RELEASE)
572 return 0;
573 new_cursor_line += MOUSE_SCROLL_LINES;
574 } else if (k->mouse == MOUSE_CLICK && k->mouse_button == 2) {
575 if (k->state == KEY_RELEASE)
576 status.flags |= CLIPPY_PASTE_SELECTION;
577 return 1;
578 } else if (k->mouse == MOUSE_CLICK) {
579 if (k->x >= 2 && k->x <= 77 && k->y >= 13 && k->y <= 47) {
580 new_cursor_line = (k->y - 13) + top_line;
581 new_cursor_char = (k->x - 2);
582 if (k->sx != k->x || k->sy != k->y) {
583 /* yay drag operation */
584 cp = get_absolute_position(current_song->message, (k->sy-13)+top_line,
585 (k->sx-2));
586 widgets_message[0].clip_start = cp;
587 doing_drag = 1;
588 }
589 }
590 }
591
592 line_len = get_nth_line(current_song->message, cursor_line, &ptr);
593
594
595 switch (k->sym) {
596 case SDLK_UP:
597 if (!NO_MODIFIER(k->mod))
598 return 0;
599 if (k->state == KEY_RELEASE)
600 return 1;
601 new_cursor_line--;
602 break;
603 case SDLK_DOWN:
604 if (!NO_MODIFIER(k->mod))
605 return 0;
606 if (k->state == KEY_RELEASE)
607 return 1;
608 new_cursor_line++;
609 break;
610 case SDLK_LEFT:
611 if (!NO_MODIFIER(k->mod))
612 return 0;
613 if (k->state == KEY_RELEASE)
614 return 1;
615 new_cursor_char--;
616 break;
617 case SDLK_RIGHT:
618 if (!NO_MODIFIER(k->mod))
619 return 0;
620 if (k->state == KEY_RELEASE)
621 return 1;
622 new_cursor_char++;
623 break;
624 case SDLK_PAGEUP:
625 if (!NO_MODIFIER(k->mod))
626 return 0;
627 if (k->state == KEY_RELEASE)
628 return 1;
629 new_cursor_line -= 35;
630 break;
631 case SDLK_PAGEDOWN:
632 if (!NO_MODIFIER(k->mod))
633 return 0;
634 if (k->state == KEY_RELEASE)
635 return 1;
636 new_cursor_line += 35;
637 break;
638 case SDLK_HOME:
639 if (k->state == KEY_RELEASE)
640 return 1;
641 if (k->mod & KMOD_CTRL)
642 new_cursor_line = 0;
643 else
644 new_cursor_char = 0;
645 break;
646 case SDLK_END:
647 if (k->state == KEY_RELEASE)
648 return 1;
649 if (k->mod & KMOD_CTRL) {
650 num_lines = get_num_lines(current_song->message);
651 new_cursor_line = num_lines;
652 } else {
653 new_cursor_char = line_len;
654 }
655 break;
656 case SDLK_ESCAPE:
657 if (!NO_MODIFIER(k->mod))
658 return 0;
659 if (k->state == KEY_RELEASE)
660 return 1;
661 message_set_viewmode();
662 memused_songchanged();
663 return 1;
664 case SDLK_BACKSPACE:
665 if (!NO_MODIFIER(k->mod))
666 return 0;
667 if (k->state == KEY_RELEASE)
668 return 1;
669 if (k->sym && clippy_owner(CLIPPY_SELECT) == widgets_message) {
670 _delete_selection();
671 } else {
672 message_delete_char();
673 }
674 return 1;
675 case SDLK_DELETE:
676 if (!NO_MODIFIER(k->mod))
677 return 0;
678 if (k->state == KEY_RELEASE)
679 return 1;
680 if (k->sym && clippy_owner(CLIPPY_SELECT) == widgets_message) {
681 _delete_selection();
682 } else {
683 message_delete_next_char();
684 }
685 return 1;
686 default:
687 if (k->mod & KMOD_CTRL) {
688 if (k->state == KEY_RELEASE)
689 return 1;
690 if (k->sym == SDLK_t) {
691 message_extfont = !message_extfont;
692 break;
693 } else if (k->sym == SDLK_y) {
694 clippy_select(NULL, NULL, 0);
695 message_delete_line();
696 break;
697 }
698 } else if (k->mod & KMOD_ALT) {
699 if (k->state == KEY_RELEASE)
700 return 1;
701 if (k->sym == SDLK_c) {
702 prompt_message_clear();
703 return 1;
704 }
705 } else if (k->mouse == MOUSE_NONE) {
706 if (k->unicode == '\r' || k->unicode == '\t'
707 || k->unicode >= 32) {
708 if (k->state == KEY_RELEASE)
709 return 1;
710 if (k->sym && clippy_owner(CLIPPY_SELECT) == widgets_message) {
711 _delete_selection();
712 }
713 if (k->mod & (KMOD_SHIFT|KMOD_CAPS)) {
714 message_insert_char(toupper((unsigned int)k->unicode));
715 } else {
716 message_insert_char(k->unicode);
717 }
718 return 1;
719 }
720 return 0;
721 }
722
723 if (k->mouse != MOUSE_CLICK)
724 return 0;
725
726 if (k->state == KEY_RELEASE)
727 return 1;
728 if (!doing_drag) {
729 clippy_select(NULL, NULL, 0);
730 }
731 }
732
733 if (new_cursor_line != cursor_line) {
734 if (num_lines == -1)
735 num_lines = get_num_lines(current_song->message);
736
737 if (new_cursor_line < 0)
738 new_cursor_line = 0;
739 else if (new_cursor_line > num_lines)
740 new_cursor_line = num_lines;
741
742 /* make sure the cursor doesn't go past the new eol */
743 line_len = get_nth_line(current_song->message, new_cursor_line, &ptr);
744 if (new_cursor_char > line_len)
745 new_cursor_char = line_len;
746
747 cursor_char = new_cursor_char;
748 cursor_line = new_cursor_line;
749 } else if (new_cursor_char != cursor_char) {
750 /* we say "else" here ESPECIALLY because the mouse can only come
751 in the top section - not because it's some clever optimization */
752 if (new_cursor_char < 0) {
753 if (cursor_line == 0) {
754 new_cursor_char = cursor_char;
755 } else {
756 cursor_line--;
757 new_cursor_char =
758 get_nth_line(current_song->message, cursor_line, &ptr);
759 }
760
761 } else if (new_cursor_char >
762 get_nth_line(current_song->message, cursor_line, &ptr)) {
763 if (cursor_line == get_num_lines(current_song->message)) {
764 new_cursor_char = cursor_char;
765 } else {
766 cursor_line++;
767 new_cursor_char = 0;
768 }
769 }
770 cursor_char = new_cursor_char;
771 }
772
773 message_reposition();
774 cursor_pos = get_absolute_position(current_song->message, cursor_line, cursor_char);
775
776 if (doing_drag) {
777 widgets_message[0].clip_end = cursor_pos;
778
779 clipl = widgets_message[0].clip_start;
780 clipr = widgets_message[0].clip_end;
781 if (clipl > clipr) {
782 cp = clipl;
783 clipl = clipr;
784 clipr = cp;
785 }
786 clippy_select(widgets_message, (current_song->message+clipl), clipr-clipl);
787 }
788
789 status.flags |= NEED_UPDATE;
790
791 return 1;
792 }
793
794 /* --------------------------------------------------------------------- */
795
message_draw_const(void)796 static void message_draw_const(void)
797 {
798 draw_box(1, 12, 78, 48, BOX_THICK | BOX_INNER | BOX_INSET);
799 }
800
song_changed_cb(void)801 static void song_changed_cb(void)
802 {
803 char *line, *prevline;
804 int len;
805
806 edit_mode = 0;
807 widgets_message[0].accept_text = 0;
808 widgets_message[0].d.other.handle_key = message_handle_key_viewmode;
809 top_line = 0;
810
811 len = get_nth_line(current_song->message, 0, &line);
812 while (len >= 0) {
813 if (len > LINE_WRAP)
814 message_wrap_line(line);
815 prevline = line;
816 len = get_nth_line(prevline, 1, &line);
817 }
818
819 if (status.current_page == PAGE_MESSAGE)
820 status.flags |= NEED_UPDATE;
821 }
822
823 /* --------------------------------------------------------------------- */
824
message_load_page(struct page * page)825 void message_load_page(struct page *page)
826 {
827 page->title = "Message Editor (Shift-F9)";
828 page->draw_const = message_draw_const;
829 page->song_changed_cb = song_changed_cb;
830 page->total_widgets = 1;
831 page->widgets = widgets_message;
832 page->help_index = HELP_MESSAGE_EDITOR;
833
834 create_other(widgets_message + 0, 0, message_handle_key_viewmode, message_draw);
835 widgets_message[0].accept_text = edit_mode;
836 }
837
message_reset_selection(void)838 void message_reset_selection(void)
839 {
840 widgets_message[0].clip_start = widgets_message[0].clip_end = 0;
841 }
842