first shot at dir split up.
[kugel-rb.git] / apps / plugins / text_editor.c
blobf18e7134e387ddb9c4bc8789d587c91c025e6c58
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
25 PLUGIN_HEADER
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 #define ACTION_INSERT 0
36 #define ACTION_GET 1
37 #define ACTION_REMOVE 2
38 #define ACTION_UPDATE 3
39 #define ACTION_CONCAT 4
41 char* _do_action(int action, char* str, int line);
42 #ifndef HAVE_ADJUSTABLE_CPU_FREQ
43 #define do_action _do_action
44 #else
45 char* do_action(int action, char* str, int line)
47 char *r;
48 rb->cpu_boost(1);
49 r = _do_action(action,str,line);
50 rb->cpu_boost(0);
51 return r;
53 #endif
55 char* _do_action(int action, char* str, int line)
57 int len, lennew;
58 int i=0,c=0;
59 if (line>=last_action_line)
61 i = last_action_line;
62 c = last_char_index;
64 while (i<line && i<line_count)
66 c += rb->strlen(&buffer[c])+1;
67 i++;
69 switch (action)
71 case ACTION_INSERT:
72 len = rb->strlen(str)+1;
73 if ( char_count+ len > buffer_size )
74 return NULL;
75 rb->memmove(&buffer[c+len],&buffer[c],char_count-c);
76 rb->strcpy(&buffer[c],str);
77 char_count += len;
78 line_count++;
79 break;
80 case ACTION_GET:
81 if (line > line_count)
82 return &buffer[0];
83 break;
84 case ACTION_REMOVE:
85 if (line > line_count)
86 return NULL;
87 len = rb->strlen(&buffer[c])+1;
88 rb->memmove(&buffer[c],&buffer[c+len],char_count-c-len);
89 char_count -= len;
90 line_count--;
91 break;
92 case ACTION_UPDATE:
93 if (line > line_count)
94 return NULL;
95 len = rb->strlen(&buffer[c])+1;
96 lennew = rb->strlen(str)+1;
97 if ( char_count+ lennew-len > buffer_size )
98 return NULL;
99 rb->memmove(&buffer[c+lennew],&buffer[c+len],char_count-c-len);
100 rb->strcpy(&buffer[c],str);
101 char_count += lennew-len;
102 break;
103 case ACTION_CONCAT:
104 if (line > line_count)
105 return NULL;
106 rb->memmove(&buffer[c-1],&buffer[c],char_count-c);
107 char_count--;
108 line_count--;
109 break;
110 default:
111 return NULL;
113 last_action_line = i;
114 last_char_index = c;
115 return &buffer[c];
117 static const char* list_get_name_cb(int selected_item, void* data,
118 char* buf, size_t buf_len)
120 (void)data;
121 char *b = do_action(ACTION_GET, 0, selected_item);
122 /* strlcpy(dst, src, siz) returns strlen(src) */
123 if (rb->strlcpy(buf, b, buf_len) >= buf_len)
125 rb->strcpy(&buf[buf_len-10], " ...");
127 return buf;
130 char filename[MAX_PATH];
131 char eol[3];
132 bool newfile;
133 void get_eol_string(char* fn)
135 int fd;
136 char t;
138 /* assume LF first */
139 rb->strcpy(eol,"\n");
141 if (!fn || !fn[0])
142 return;
143 fd = rb->open(fn,O_RDONLY);
144 if (fd<0)
145 return;
147 while (1)
149 if (!rb->read(fd,&t,1) || t == '\n')
151 break;
153 if (t == '\r')
155 if (rb->read(fd,&t,1) && t=='\n')
156 rb->strcpy(eol,"\r\n");
157 else
158 rb->strcpy(eol,"\r");
159 break;
162 rb->close(fd);
163 return;
166 bool save_changes(int overwrite)
168 int fd;
169 int i;
171 if (newfile || !overwrite)
173 if(rb->kbd_input(filename,MAX_PATH) < 0)
175 newfile = true;
176 return false;
180 fd = rb->open(filename,O_WRONLY|O_CREAT|O_TRUNC, 0666);
181 if (fd < 0)
183 newfile = true;
184 rb->splash(HZ*2, "Changes NOT saved");
185 return false;
188 if (!overwrite)
189 /* current directory may have changed */
190 rb->reload_directory();
192 rb->lcd_clear_display();
193 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
194 rb->cpu_boost(1);
195 #endif
196 for (i=0;i<line_count;i++)
198 rb->fdprintf(fd,"%s%s", do_action(ACTION_GET, 0, i), eol);
200 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
201 rb->cpu_boost(0);
202 #endif
203 rb->close(fd);
204 newfile = false;
205 return true;
208 void setup_lists(struct gui_synclist *lists, int sel)
210 rb->gui_synclist_init(lists,list_get_name_cb,0, false, 1, NULL);
211 rb->gui_synclist_set_icon_callback(lists,NULL);
212 rb->gui_synclist_set_nb_items(lists,line_count);
213 rb->gui_synclist_limit_scroll(lists,true);
214 rb->gui_synclist_select_item(lists, sel);
215 rb->gui_synclist_draw(lists);
218 enum {
219 MENU_RET_SAVE = -1,
220 MENU_RET_NO_UPDATE,
221 MENU_RET_UPDATE,
223 int do_item_menu(int cur_sel, char* copy_buffer)
225 int ret = MENU_RET_NO_UPDATE;
226 MENUITEM_STRINGLIST(menu, "Line Options", NULL,
227 "Cut/Delete", "Copy",
228 "Insert Above", "Insert Below",
229 "Concat To Above",
230 "Save", "Playback Control");
232 switch (rb->do_menu(&menu, NULL, NULL, false))
234 case 0: /* cut */
235 rb->strlcpy(copy_buffer, do_action(ACTION_GET, 0, cur_sel),
236 MAX_LINE_LEN);
237 do_action(ACTION_REMOVE, 0, cur_sel);
238 ret = MENU_RET_UPDATE;
239 break;
240 case 1: /* copy */
241 rb->strlcpy(copy_buffer, do_action(ACTION_GET, 0, cur_sel),
242 MAX_LINE_LEN);
243 ret = MENU_RET_NO_UPDATE;
244 break;
245 case 2: /* insert above */
246 if (!rb->kbd_input(copy_buffer,MAX_LINE_LEN))
248 do_action(ACTION_INSERT,copy_buffer,cur_sel);
249 copy_buffer[0]='\0';
250 ret = MENU_RET_UPDATE;
252 break;
253 case 3: /* insert below */
254 if (!rb->kbd_input(copy_buffer,MAX_LINE_LEN))
256 do_action(ACTION_INSERT,copy_buffer,cur_sel+1);
257 copy_buffer[0]='\0';
258 ret = MENU_RET_UPDATE;
260 break;
261 case 4: /* cat to above */
262 if (cur_sel>0)
264 do_action(ACTION_CONCAT,0,cur_sel);
265 ret = MENU_RET_UPDATE;
267 break;
268 case 5: /* save */
269 ret = MENU_RET_SAVE;
270 break;
271 case 6: /* playback menu */
272 if (!audio_buf)
273 playback_control(NULL);
274 else
275 rb->splash(HZ, "Cannot restart playback");
276 ret = MENU_RET_NO_UPDATE;
277 break;
278 default:
279 ret = MENU_RET_NO_UPDATE;
280 break;
282 return ret;
285 #ifdef HAVE_LCD_COLOR
286 /* in misc.h but no need to polute the api */
287 #define toupper(c) (((c >= 'a') && (c <= 'z'))?c+'A':c)
288 #define isxdigit(c) ((c>='a' && c<= 'f') || (c>='A' && c<= 'F') \
289 || (c>='0' && c<= '9'))
290 #define hex2dec(c) (((c) >= '0' && ((c) <= '9')) ? (toupper(c)) - '0' : \
291 (toupper(c)) - 'A' + 10)
292 int hex_to_rgb(const char* hex, int* color)
293 { int ok = 1;
294 int i;
295 int red, green, blue;
297 if (rb->strlen(hex) == 6) {
298 for (i=0; i < 6; i++ ) {
299 if (!isxdigit(hex[i])) {
300 ok=0;
301 break;
305 if (ok) {
306 red = (hex2dec(hex[0]) << 4) | hex2dec(hex[1]);
307 green = (hex2dec(hex[2]) << 4) | hex2dec(hex[3]);
308 blue = (hex2dec(hex[4]) << 4) | hex2dec(hex[5]);
309 *color = LCD_RGBPACK(red,green,blue);
310 return 0;
314 return -1;
316 #endif /* HAVE_LCD_COLOR */
318 /* this is the plugin entry point */
319 enum plugin_status plugin_start(const void* parameter)
321 int fd;
322 static char temp_line[MAX_LINE_LEN];
324 struct gui_synclist lists;
325 bool exit = false;
326 int button;
327 bool changed = false;
328 int cur_sel=0;
329 static char copy_buffer[MAX_LINE_LEN];
330 #ifdef HAVE_LCD_COLOR
331 bool edit_colors_file = false;
332 #endif
334 copy_buffer[0]='\0';
336 #if LCD_DEPTH > 1
337 rb->lcd_set_backdrop(NULL);
338 #endif
339 buffer = rb->plugin_get_buffer(&buffer_size);
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->strlcpy(filename, (char*)parameter, MAX_PATH);
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", filename);
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 if (buffer_size <= (size_t)rb->filesize(fd) + 0x400)
364 buffer = rb->plugin_get_audio_buffer(&buffer_size);
365 audio_buf = true;
367 /* read in the file */
368 while (rb->read_line(fd,temp_line,MAX_LINE_LEN) > 0)
370 if (!do_action(ACTION_INSERT,temp_line,line_count))
372 rb->splashf(HZ*2,"Error reading file: %s",(char*)parameter);
373 rb->close(fd);
374 return PLUGIN_ERROR;
377 rb->close(fd);
378 newfile = false;
380 else
382 rb->strcpy(filename,"/");
383 rb->strcpy(eol,"\n");
384 newfile = true;
387 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
388 rb->cpu_boost(0);
389 #endif
390 /* now dump it in the list */
391 setup_lists(&lists,0);
392 rb->lcd_update();
393 while (!exit)
395 rb->gui_synclist_draw(&lists);
396 cur_sel = rb->gui_synclist_get_sel_pos(&lists);
397 button = rb->get_action(CONTEXT_LIST,TIMEOUT_BLOCK);
398 if (rb->gui_synclist_do_button(&lists,&button,LIST_WRAP_UNLESS_HELD))
399 continue;
400 switch (button)
402 case ACTION_STD_OK:
404 if (line_count)
405 rb->strlcpy(temp_line, do_action(ACTION_GET, 0, cur_sel),
406 MAX_LINE_LEN);
407 #ifdef HAVE_LCD_COLOR
408 if (edit_colors_file && line_count)
410 char *name = temp_line, *value = NULL;
411 char extension[MAX_LINE_LEN];
412 int color, old_color;
413 bool temp_changed = false;
414 rb->settings_parseline(temp_line, &name, &value);
415 if (line_count)
417 MENUITEM_STRINGLIST(menu, "Edit What?", NULL,
418 "Extension", "Colour");
419 rb->strcpy(extension, name);
420 if (value)
421 hex_to_rgb(value, &color);
422 else
423 color = 0;
425 switch (rb->do_menu(&menu, NULL, NULL, false))
427 case 0:
428 temp_changed = !rb->kbd_input(extension,MAX_LINE_LEN);
429 break;
430 case 1:
431 old_color = color;
432 rb->set_color(rb->screens[SCREEN_MAIN], name, &color, -1);
433 temp_changed = (value == NULL) || (color != old_color);
434 break;
437 if (temp_changed)
439 rb->snprintf(temp_line, MAX_LINE_LEN, "%s: %02X%02X%02X",
440 extension, RGB_UNPACK_RED(color),
441 RGB_UNPACK_GREEN(color),
442 RGB_UNPACK_BLUE(color));
443 do_action(ACTION_UPDATE, temp_line, cur_sel);
444 changed = true;
448 else
449 #endif
450 if (!rb->kbd_input(temp_line,MAX_LINE_LEN))
452 if (line_count)
453 do_action(ACTION_UPDATE,temp_line,cur_sel);
454 else
455 do_action(ACTION_INSERT,temp_line,cur_sel);
456 changed = true;
459 break;
460 case ACTION_STD_CONTEXT:
461 if (!line_count) break;
462 rb->strlcpy(copy_buffer, do_action(ACTION_GET, 0, cur_sel),
463 MAX_LINE_LEN);
464 do_action(ACTION_REMOVE, 0, cur_sel);
465 changed = true;
466 break;
467 case ACTION_STD_MENU:
468 { /* do the item menu */
469 switch (do_item_menu(cur_sel, copy_buffer))
471 case MENU_RET_SAVE:
472 if(save_changes(1))
473 changed = false;
474 break;
475 case MENU_RET_UPDATE:
476 changed = true;
477 break;
478 case MENU_RET_NO_UPDATE:
479 break;
482 break;
483 case ACTION_STD_CANCEL:
484 if (changed)
486 MENUITEM_STRINGLIST(menu, "Do What?", NULL,
487 "Return",
488 "Playback Control", "Save Changes",
489 "Save As...", "Save and Exit",
490 "Ignore Changes and Exit");
491 switch (rb->do_menu(&menu, NULL, NULL, false))
493 case 0:
494 break;
495 case 1:
496 if (!audio_buf)
497 playback_control(NULL);
498 else
499 rb->splash(HZ, "Cannot restart playback");
500 break;
501 case 2: //save to disk
502 if(save_changes(1))
503 changed = 0;
504 break;
505 case 3:
506 if(save_changes(0))
507 changed = 0;
508 break;
509 case 4:
510 if(save_changes(1))
511 exit=1;
512 break;
513 case 5:
514 exit=1;
515 break;
518 else exit=1;
519 break;
521 rb->gui_synclist_set_nb_items(&lists,line_count);
522 if(line_count > 0 && line_count <= cur_sel)
523 rb->gui_synclist_select_item(&lists,line_count-1);
525 return PLUGIN_OK;