Fix yellow.
[kugel-rb.git] / apps / plugins / text_editor.c
blob32013b6f287b15eee7a8efe788b6242ff72aedea
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2006 Jonathan Gordon
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
21 #include "plugin.h"
22 #include "lib/playback_control.h"
24 #define MAX_LINE_LEN 2048
27 static unsigned char *buffer;
28 static size_t buffer_size;
29 static size_t char_count = 0;
30 static int line_count = 0;
31 static int last_action_line = 0;
32 static int last_char_index = 0;
33 static bool audio_buf = false;
35 static char temp_line[MAX_LINE_LEN];
36 static char copy_buffer[MAX_LINE_LEN];
37 static char filename[MAX_PATH];
38 static char eol[3];
39 static bool newfile;
41 #define ACTION_INSERT 0
42 #define ACTION_GET 1
43 #define ACTION_REMOVE 2
44 #define ACTION_UPDATE 3
45 #define ACTION_CONCAT 4
47 static char* _do_action(int action, char* str, int line);
48 #ifndef HAVE_ADJUSTABLE_CPU_FREQ
49 #define do_action _do_action
50 #else
51 static char* do_action(int action, char* str, int line)
53 char *r;
54 rb->cpu_boost(1);
55 r = _do_action(action,str,line);
56 rb->cpu_boost(0);
57 return r;
59 #endif
61 static char* _do_action(int action, char* str, int line)
63 int len, lennew;
64 int i=0,c=0;
65 if (line>=last_action_line)
67 i = last_action_line;
68 c = last_char_index;
70 while (i<line && i<line_count)
72 c += rb->strlen(&buffer[c])+1;
73 i++;
75 switch (action)
77 case ACTION_INSERT:
78 len = rb->strlen(str)+1;
79 if ( char_count+ len > buffer_size )
80 return NULL;
81 rb->memmove(&buffer[c+len],&buffer[c],char_count-c);
82 rb->strcpy(&buffer[c],str);
83 char_count += len;
84 line_count++;
85 break;
86 case ACTION_GET:
87 if (line > line_count)
88 return &buffer[0];
89 break;
90 case ACTION_REMOVE:
91 if (line > line_count)
92 return NULL;
93 len = rb->strlen(&buffer[c])+1;
94 rb->memmove(&buffer[c],&buffer[c+len],char_count-c-len);
95 char_count -= len;
96 line_count--;
97 break;
98 case ACTION_UPDATE:
99 if (line > line_count)
100 return NULL;
101 len = rb->strlen(&buffer[c])+1;
102 lennew = rb->strlen(str)+1;
103 if ( char_count+ lennew-len > buffer_size )
104 return NULL;
105 rb->memmove(&buffer[c+lennew],&buffer[c+len],char_count-c-len);
106 rb->strcpy(&buffer[c],str);
107 char_count += lennew-len;
108 break;
109 case ACTION_CONCAT:
110 if (line > line_count)
111 return NULL;
112 rb->memmove(&buffer[c-1],&buffer[c],char_count-c);
113 char_count--;
114 line_count--;
115 break;
116 default:
117 return NULL;
119 last_action_line = i;
120 last_char_index = c;
121 return &buffer[c];
123 static const char* list_get_name_cb(int selected_item, void* data,
124 char* buf, size_t buf_len)
126 (void)data;
127 char *b = do_action(ACTION_GET, 0, selected_item);
128 /* strlcpy(dst, src, siz) returns strlen(src) */
129 if (rb->strlcpy(buf, b, buf_len) >= buf_len)
131 rb->strcpy(&buf[buf_len-10], " ...");
133 return buf;
136 static void get_eol_string(char* fn)
138 int fd;
139 char t;
141 /* assume LF first */
142 rb->strcpy(eol,"\n");
144 if (!fn || !fn[0])
145 return;
146 fd = rb->open(fn,O_RDONLY);
147 if (fd<0)
148 return;
150 while (1)
152 if (!rb->read(fd,&t,1) || t == '\n')
154 break;
156 if (t == '\r')
158 if (rb->read(fd,&t,1) && t=='\n')
159 rb->strcpy(eol,"\r\n");
160 else
161 rb->strcpy(eol,"\r");
162 break;
165 rb->close(fd);
166 return;
169 static bool save_changes(int overwrite)
171 int fd;
172 int i;
174 if (newfile || !overwrite)
176 if(rb->kbd_input(filename,MAX_PATH) < 0)
178 newfile = true;
179 return false;
183 fd = rb->open(filename,O_WRONLY|O_CREAT|O_TRUNC, 0666);
184 if (fd < 0)
186 newfile = true;
187 rb->splash(HZ*2, "Changes NOT saved");
188 return false;
191 rb->lcd_clear_display();
192 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
193 rb->cpu_boost(1);
194 #endif
195 for (i=0;i<line_count;i++)
197 rb->fdprintf(fd,"%s%s", do_action(ACTION_GET, 0, i), eol);
199 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
200 rb->cpu_boost(0);
201 #endif
202 rb->close(fd);
204 if (newfile || !overwrite)
205 /* current directory may have changed */
206 rb->reload_directory();
208 newfile = false;
209 return true;
212 static void setup_lists(struct gui_synclist *lists, int sel)
214 rb->gui_synclist_init(lists,list_get_name_cb,0, false, 1, NULL);
215 rb->gui_synclist_set_icon_callback(lists,NULL);
216 rb->gui_synclist_set_nb_items(lists,line_count);
217 rb->gui_synclist_limit_scroll(lists,true);
218 rb->gui_synclist_select_item(lists, sel);
219 rb->gui_synclist_draw(lists);
222 enum {
223 MENU_RET_SAVE = -1,
224 MENU_RET_NO_UPDATE,
225 MENU_RET_UPDATE,
227 static int do_item_menu(int cur_sel)
229 int ret = MENU_RET_NO_UPDATE;
230 MENUITEM_STRINGLIST(menu, "Line Options", NULL,
231 "Cut/Delete", "Copy",
232 "Insert Above", "Insert Below",
233 "Concat To Above",
234 "Save", "Playback Control");
236 switch (rb->do_menu(&menu, NULL, NULL, false))
238 case 0: /* cut */
239 rb->strlcpy(copy_buffer, do_action(ACTION_GET, 0, cur_sel),
240 MAX_LINE_LEN);
241 do_action(ACTION_REMOVE, 0, cur_sel);
242 ret = MENU_RET_UPDATE;
243 break;
244 case 1: /* copy */
245 rb->strlcpy(copy_buffer, do_action(ACTION_GET, 0, cur_sel),
246 MAX_LINE_LEN);
247 ret = MENU_RET_NO_UPDATE;
248 break;
249 case 2: /* insert above */
250 if (!rb->kbd_input(copy_buffer,MAX_LINE_LEN))
252 do_action(ACTION_INSERT,copy_buffer,cur_sel);
253 copy_buffer[0]='\0';
254 ret = MENU_RET_UPDATE;
256 break;
257 case 3: /* insert below */
258 if (!rb->kbd_input(copy_buffer,MAX_LINE_LEN))
260 do_action(ACTION_INSERT,copy_buffer,cur_sel+1);
261 copy_buffer[0]='\0';
262 ret = MENU_RET_UPDATE;
264 break;
265 case 4: /* cat to above */
266 if (cur_sel>0)
268 do_action(ACTION_CONCAT,0,cur_sel);
269 ret = MENU_RET_UPDATE;
271 break;
272 case 5: /* save */
273 ret = MENU_RET_SAVE;
274 break;
275 case 6: /* playback menu */
276 if (!audio_buf)
277 playback_control(NULL);
278 else
279 rb->splash(HZ, "Cannot restart playback");
280 ret = MENU_RET_NO_UPDATE;
281 break;
282 default:
283 ret = MENU_RET_NO_UPDATE;
284 break;
286 return ret;
289 #ifdef HAVE_LCD_COLOR
290 /* in misc.h but no need to polute the api */
291 #define toupper(c) (((c >= 'a') && (c <= 'z'))?c+'A':c)
292 #define isxdigit(c) ((c>='a' && c<= 'f') || (c>='A' && c<= 'F') \
293 || (c>='0' && c<= '9'))
294 #define hex2dec(c) (((c) >= '0' && ((c) <= '9')) ? (toupper(c)) - '0' : \
295 (toupper(c)) - 'A' + 10)
296 static int my_hex_to_rgb(const char* hex, int* color)
297 { int ok = 1;
298 int i;
299 int red, green, blue;
301 if (rb->strlen(hex) == 6) {
302 for (i=0; i < 6; i++ ) {
303 if (!isxdigit(hex[i])) {
304 ok=0;
305 break;
309 if (ok) {
310 red = (hex2dec(hex[0]) << 4) | hex2dec(hex[1]);
311 green = (hex2dec(hex[2]) << 4) | hex2dec(hex[3]);
312 blue = (hex2dec(hex[4]) << 4) | hex2dec(hex[5]);
313 *color = LCD_RGBPACK(red,green,blue);
314 return 0;
318 return -1;
320 #endif /* HAVE_LCD_COLOR */
322 /* this is the plugin entry point */
323 enum plugin_status plugin_start(const void* parameter)
325 int fd;
327 struct gui_synclist lists;
328 bool exit = false;
329 int button;
330 bool changed = false;
331 int cur_sel=0;
332 #ifdef HAVE_LCD_COLOR
333 bool edit_colors_file = false;
334 #endif
336 copy_buffer[0]='\0';
338 #if LCD_DEPTH > 1
339 rb->lcd_set_backdrop(NULL);
340 #endif
341 buffer = rb->plugin_get_buffer(&buffer_size);
343 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
344 rb->cpu_boost(1);
345 #endif
346 if (parameter)
348 #ifdef HAVE_LCD_COLOR
349 char *c = NULL;
350 #endif
351 rb->strlcpy(filename, (char*)parameter, MAX_PATH);
352 get_eol_string(filename);
353 fd = rb->open(filename,O_RDONLY);
354 if (fd<0)
356 rb->splashf(HZ*2, "Couldnt open file: %s", filename);
357 return PLUGIN_ERROR;
359 #ifdef HAVE_LCD_COLOR
360 c = rb->strrchr(filename, '.');
361 if (c && !rb->strcmp(c, ".colours"))
362 edit_colors_file = true;
363 #endif
364 if (buffer_size <= (size_t)rb->filesize(fd) + 0x400)
366 buffer = rb->plugin_get_audio_buffer(&buffer_size);
367 audio_buf = true;
369 /* read in the file */
370 while (rb->read_line(fd,temp_line,MAX_LINE_LEN) > 0)
372 if (!do_action(ACTION_INSERT,temp_line,line_count))
374 rb->splashf(HZ*2,"Error reading file: %s",(char*)parameter);
375 rb->close(fd);
376 return PLUGIN_ERROR;
379 rb->close(fd);
380 newfile = false;
382 else
384 rb->strcpy(filename,"/");
385 rb->strcpy(eol,"\n");
386 newfile = true;
389 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
390 rb->cpu_boost(0);
391 #endif
392 /* now dump it in the list */
393 setup_lists(&lists,0);
394 rb->lcd_update();
395 while (!exit)
397 rb->gui_synclist_draw(&lists);
398 cur_sel = rb->gui_synclist_get_sel_pos(&lists);
399 button = rb->get_action(CONTEXT_LIST,TIMEOUT_BLOCK);
400 if (rb->gui_synclist_do_button(&lists,&button,LIST_WRAP_UNLESS_HELD))
401 continue;
402 switch (button)
404 case ACTION_STD_OK:
406 if (line_count)
407 rb->strlcpy(temp_line, do_action(ACTION_GET, 0, cur_sel),
408 MAX_LINE_LEN);
409 #ifdef HAVE_LCD_COLOR
410 if (edit_colors_file && line_count)
412 char *name = temp_line, *value = NULL;
413 char extension[16];
414 int color, old_color;
415 bool temp_changed = false;
417 MENUITEM_STRINGLIST(menu, "Edit What?", NULL,
418 "Extension", "Colour");
420 rb->settings_parseline(temp_line, &name, &value);
421 rb->strlcpy(extension, name, sizeof(extension));
422 if (value)
423 my_hex_to_rgb(value, &color);
424 else
425 color = 0;
427 switch (rb->do_menu(&menu, NULL, NULL, false))
429 case 0:
430 temp_changed = !rb->kbd_input(extension, sizeof(extension));
431 break;
432 case 1:
433 old_color = color;
434 rb->set_color(rb->screens[SCREEN_MAIN], name, &color, -1);
435 temp_changed = (value == NULL) || (color != old_color);
436 break;
439 if (temp_changed)
441 rb->snprintf(temp_line, MAX_LINE_LEN, "%s: %02X%02X%02X",
442 extension, RGB_UNPACK_RED(color),
443 RGB_UNPACK_GREEN(color),
444 RGB_UNPACK_BLUE(color));
445 do_action(ACTION_UPDATE, temp_line, cur_sel);
446 changed = true;
449 else
450 #endif
451 if (!rb->kbd_input(temp_line,MAX_LINE_LEN))
453 if (line_count)
454 do_action(ACTION_UPDATE,temp_line,cur_sel);
455 else
456 do_action(ACTION_INSERT,temp_line,cur_sel);
457 changed = true;
460 break;
461 case ACTION_STD_CONTEXT:
462 if (!line_count) break;
463 rb->strlcpy(copy_buffer, do_action(ACTION_GET, 0, cur_sel),
464 MAX_LINE_LEN);
465 do_action(ACTION_REMOVE, 0, cur_sel);
466 changed = true;
467 break;
468 case ACTION_STD_MENU:
470 /* do the item menu */
471 switch (do_item_menu(cur_sel))
473 case MENU_RET_SAVE:
474 if(save_changes(1))
475 changed = false;
476 break;
477 case MENU_RET_UPDATE:
478 changed = true;
479 break;
480 case MENU_RET_NO_UPDATE:
481 break;
484 break;
485 case ACTION_STD_CANCEL:
486 if (changed)
488 MENUITEM_STRINGLIST(menu, "Do What?", NULL,
489 "Return",
490 "Playback Control", "Save Changes",
491 "Save As...", "Save and Exit",
492 "Ignore Changes and Exit");
493 switch (rb->do_menu(&menu, NULL, NULL, false))
495 case 0:
496 break;
497 case 1:
498 if (!audio_buf)
499 playback_control(NULL);
500 else
501 rb->splash(HZ, "Cannot restart playback");
502 break;
503 case 2: //save to disk
504 if(save_changes(1))
505 changed = 0;
506 break;
507 case 3:
508 if(save_changes(0))
509 changed = 0;
510 break;
511 case 4:
512 if(save_changes(1))
513 exit=1;
514 break;
515 case 5:
516 exit=1;
517 break;
520 else exit=1;
521 break;
523 rb->gui_synclist_set_nb_items(&lists,line_count);
524 if(line_count > 0 && line_count <= cur_sel)
525 rb->gui_synclist_select_item(&lists,line_count-1);
527 return PLUGIN_OK;