Correct beast manual install instructions in Windows.
[kugel-rb.git] / apps / plugins / text_editor.c
bloba55165e63eba22896b71c9e9c18139c868a6c82b
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 #if PLUGIN_BUFFER_SIZE > 0x45000
25 #define MAX_CHARS 0x40000 /* 256 kiB */
26 #else
27 #define MAX_CHARS 0x6000 /* 24 kiB */
28 #endif
29 #define MAX_LINE_LEN 2048
30 PLUGIN_HEADER
32 static char buffer[MAX_CHARS];
33 static int char_count = 0;
34 static int line_count = 0;
35 static int last_action_line = 0;
36 static int last_char_index = 0;
38 #define ACTION_INSERT 0
39 #define ACTION_GET 1
40 #define ACTION_REMOVE 2
41 #define ACTION_UPDATE 3
42 #define ACTION_CONCAT 4
44 int _do_action(int action, char* str, int line);
45 #ifndef HAVE_ADJUSTABLE_CPU_FREQ
46 #define do_action _do_action
47 #else
48 int do_action(int action, char* str, int line)
50 int r;
51 rb->cpu_boost(1);
52 r = _do_action(action,str,line);
53 rb->cpu_boost(0);
54 return r;
56 #endif
58 int _do_action(int action, char* str, int line)
60 int len, lennew;
61 int i=0,c=0;
62 if (line>=last_action_line)
64 i = last_action_line;
65 c = last_char_index;
67 while (i<line && i<line_count)
69 c += rb->strlen(&buffer[c])+1;
70 i++;
72 switch (action)
74 case ACTION_INSERT:
75 len = rb->strlen(str)+1;
76 if ( char_count+ len > MAX_CHARS )
77 return 0;
78 rb->memmove(&buffer[c+len],&buffer[c],char_count-c);
79 rb->strcpy(&buffer[c],str);
80 char_count += len;
81 line_count++;
82 break;
83 case ACTION_GET:
84 if (line > line_count)
85 return 0;
86 last_action_line = i;
87 last_char_index = c;
88 return c;
89 break;
90 case ACTION_REMOVE:
91 if (line > line_count)
92 return 0;
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 0;
101 len = rb->strlen(&buffer[c])+1;
102 lennew = rb->strlen(str)+1;
103 if ( char_count+ lennew-len > MAX_CHARS )
104 return 0;
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 0;
112 rb->memmove(&buffer[c-1],&buffer[c],char_count-c);
113 char_count--;
114 line_count--;
115 break;
116 default:
117 return 0;
119 last_action_line = i;
120 last_char_index = c;
121 return 1;
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 = &buffer[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 char filename[MAX_PATH];
137 char eol[3];
138 bool newfile;
139 void get_eol_string(char* fn)
141 int fd;
142 char t;
144 /* assume LF first */
145 rb->strcpy(eol,"\n");
147 if (!fn || !fn[0])
148 return;
149 fd = rb->open(fn,O_RDONLY);
150 if (fd<0)
151 return;
153 while (1)
155 if (!rb->read(fd,&t,1) || t == '\n')
157 break;
159 if (t == '\r')
161 if (rb->read(fd,&t,1) && t=='\n')
162 rb->strcpy(eol,"\r\n");
163 else
164 rb->strcpy(eol,"\r");
165 break;
168 rb->close(fd);
169 return;
172 bool save_changes(int overwrite)
174 int fd;
175 int i;
177 if (newfile || !overwrite)
179 if(rb->kbd_input(filename,MAX_PATH) < 0)
181 newfile = true;
182 return false;
186 fd = rb->open(filename,O_WRONLY|O_CREAT|O_TRUNC);
187 if (fd < 0)
189 newfile = true;
190 rb->splash(HZ*2, "Changes NOT saved");
191 return false;
194 if (!overwrite)
195 /* current directory may have changed */
196 rb->reload_directory();
198 rb->lcd_clear_display();
199 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
200 rb->cpu_boost(1);
201 #endif
202 for (i=0;i<line_count;i++)
204 rb->fdprintf(fd,"%s%s",&buffer[do_action(ACTION_GET,0,i)],eol);
206 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
207 rb->cpu_boost(0);
208 #endif
209 rb->close(fd);
210 newfile = false;
211 return true;
214 void setup_lists(struct gui_synclist *lists, int sel)
216 rb->gui_synclist_init(lists,list_get_name_cb,0, false, 1, NULL);
217 rb->gui_synclist_set_icon_callback(lists,NULL);
218 rb->gui_synclist_set_nb_items(lists,line_count);
219 rb->gui_synclist_limit_scroll(lists,true);
220 rb->gui_synclist_select_item(lists, sel);
221 rb->gui_synclist_draw(lists);
224 enum {
225 MENU_RET_SAVE = -1,
226 MENU_RET_NO_UPDATE,
227 MENU_RET_UPDATE,
229 int do_item_menu(int cur_sel, char* copy_buffer)
231 int ret = 0;
232 MENUITEM_STRINGLIST(menu, "Line Options", NULL,
233 "Cut/Delete", "Copy",
234 "Insert Above", "Insert Below",
235 "Concat To Above",
236 "Save", "Playback Control");
238 switch (rb->do_menu(&menu, NULL, NULL, false))
240 case 0: /* cut */
241 rb->strcpy(copy_buffer,&buffer[do_action(ACTION_GET,0,cur_sel)]);
242 do_action(ACTION_REMOVE,0,cur_sel);
243 ret = MENU_RET_UPDATE;
244 break;
245 case 1: /* copy */
246 rb->strcpy(copy_buffer,&buffer[do_action(ACTION_GET,0,cur_sel)]);
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 playback_control(NULL);
277 ret = MENU_RET_NO_UPDATE;
278 break;
279 default:
280 ret = MENU_RET_NO_UPDATE;
281 break;
283 return ret;
286 #ifdef HAVE_LCD_COLOR
287 /* in misc.h but no need to polute the api */
288 #define toupper(c) (((c >= 'a') && (c <= 'z'))?c+'A':c)
289 #define isxdigit(c) ((c>='a' && c<= 'f') || (c>='A' && c<= 'F') \
290 || (c>='0' && c<= '9'))
291 #define hex2dec(c) (((c) >= '0' && ((c) <= '9')) ? (toupper(c)) - '0' : \
292 (toupper(c)) - 'A' + 10)
293 int hex_to_rgb(const char* hex, int* color)
294 { int ok = 1;
295 int i;
296 int red, green, blue;
298 if (rb->strlen(hex) == 6) {
299 for (i=0; i < 6; i++ ) {
300 if (!isxdigit(hex[i])) {
301 ok=0;
302 break;
306 if (ok) {
307 red = (hex2dec(hex[0]) << 4) | hex2dec(hex[1]);
308 green = (hex2dec(hex[2]) << 4) | hex2dec(hex[3]);
309 blue = (hex2dec(hex[4]) << 4) | hex2dec(hex[5]);
310 *color = LCD_RGBPACK(red,green,blue);
311 return 0;
315 return -1;
317 #endif /* HAVE_LCD_COLOR */
319 /* this is the plugin entry point */
320 enum plugin_status plugin_start(const void* parameter)
322 int fd;
323 static char temp_line[MAX_LINE_LEN];
325 struct gui_synclist lists;
326 bool exit = false;
327 int button;
328 bool changed = false;
329 int cur_sel=0;
330 static char copy_buffer[MAX_LINE_LEN];
331 #ifdef HAVE_LCD_COLOR
332 bool edit_colors_file = false;
333 #endif
335 copy_buffer[0]='\0';
337 #if LCD_DEPTH > 1
338 rb->lcd_set_backdrop(NULL);
339 #endif
341 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
342 rb->cpu_boost(1);
343 #endif
344 if (parameter)
346 #ifdef HAVE_LCD_COLOR
347 char *c = NULL;
348 #endif
349 rb->strcpy(filename,(char*)parameter);
350 get_eol_string(filename);
351 fd = rb->open(filename,O_RDONLY);
352 if (fd<0)
354 rb->splashf(HZ*2,"Couldnt open file: %s",(char*)parameter);
355 return PLUGIN_ERROR;
357 #ifdef HAVE_LCD_COLOR
358 c = rb->strrchr(filename, '.');
359 if (c && !rb->strcmp(c, ".colours"))
360 edit_colors_file = true;
361 #endif
362 /* read in the file */
363 while (rb->read_line(fd,temp_line,MAX_LINE_LEN) > 0)
365 if (!do_action(ACTION_INSERT,temp_line,line_count))
367 rb->splashf(HZ*2,"Error reading file: %s",(char*)parameter);
368 rb->close(fd);
369 return PLUGIN_ERROR;
372 rb->close(fd);
373 newfile = false;
375 else
377 rb->strcpy(filename,"/");
378 rb->strcpy(eol,"\n");
379 newfile = true;
382 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
383 rb->cpu_boost(0);
384 #endif
385 /* now dump it in the list */
386 setup_lists(&lists,0);
387 rb->lcd_update();
388 while (!exit)
390 rb->gui_synclist_draw(&lists);
391 cur_sel = rb->gui_synclist_get_sel_pos(&lists);
392 button = rb->get_action(CONTEXT_LIST,TIMEOUT_BLOCK);
393 if (rb->gui_synclist_do_button(&lists,&button,LIST_WRAP_UNLESS_HELD))
394 continue;
395 switch (button)
397 case ACTION_STD_OK:
399 if (line_count)
400 rb->strcpy(temp_line,&buffer[do_action(ACTION_GET,0,cur_sel)]);
401 #ifdef HAVE_LCD_COLOR
402 if (edit_colors_file && line_count)
404 char *name = temp_line, *value = NULL;
405 char extension[MAX_LINE_LEN];
406 int color, old_color;
407 bool temp_changed;
408 rb->settings_parseline(temp_line, &name, &value);
409 if (line_count)
411 MENUITEM_STRINGLIST(menu, "Edit What?", NULL,
412 "Extension", "Colour");
413 rb->strcpy(extension, name);
414 if (value)
415 hex_to_rgb(value, &color);
416 else
417 color = 0;
419 switch (rb->do_menu(&menu, NULL, NULL, false))
421 case 0:
422 temp_changed = !rb->kbd_input(extension,MAX_LINE_LEN);
423 break;
424 case 1:
425 old_color = color;
426 rb->set_color(rb->screens[SCREEN_MAIN], name, &color, -1);
427 temp_changed = (value == NULL) || (color != old_color);
428 break;
429 default:
430 /* Should never happen but makes compiler happy */
431 temp_changed = false;
434 if (temp_changed)
436 rb->snprintf(temp_line, MAX_LINE_LEN, "%s: %02X%02X%02X",
437 extension, RGB_UNPACK_RED(color),
438 RGB_UNPACK_GREEN(color),
439 RGB_UNPACK_BLUE(color));
440 do_action(ACTION_UPDATE, temp_line, cur_sel);
441 changed = true;
445 else
446 #endif
447 if (!rb->kbd_input(temp_line,MAX_LINE_LEN))
449 if (line_count)
450 do_action(ACTION_UPDATE,temp_line,cur_sel);
451 else
452 do_action(ACTION_INSERT,temp_line,cur_sel);
453 changed = true;
456 break;
457 case ACTION_STD_CONTEXT:
458 if (!line_count) break;
459 rb->strcpy(copy_buffer,&buffer[do_action(ACTION_GET,0,cur_sel)]);
460 do_action(ACTION_REMOVE,0,cur_sel);
461 changed = true;
462 break;
463 case ACTION_STD_MENU:
464 { /* do the item menu */
465 switch (do_item_menu(cur_sel, copy_buffer))
467 case MENU_RET_SAVE:
468 if(save_changes(1))
469 changed = false;
470 break;
471 case MENU_RET_UPDATE:
472 changed = true;
473 break;
474 case MENU_RET_NO_UPDATE:
475 break;
478 break;
479 case ACTION_STD_CANCEL:
480 if (changed)
482 MENUITEM_STRINGLIST(menu, "Do What?", NULL,
483 "Return",
484 "Playback Control", "Save Changes",
485 "Save As...", "Save and Exit",
486 "Ignore Changes and Exit");
487 switch (rb->do_menu(&menu, NULL, NULL, false))
489 case 0:
490 break;
491 case 1:
492 playback_control(NULL);
493 break;
494 case 2: //save to disk
495 if(save_changes(1))
496 changed = 0;
497 break;
498 case 3:
499 if(save_changes(0))
500 changed = 0;
501 break;
502 case 4:
503 if(save_changes(1))
504 exit=1;
505 break;
506 case 5:
507 exit=1;
508 break;
511 else exit=1;
512 break;
514 rb->gui_synclist_set_nb_items(&lists,line_count);
515 if(line_count > 0 && line_count <= cur_sel)
516 rb->gui_synclist_select_item(&lists,line_count-1);
518 return PLUGIN_OK;