New makefile solution: A single invocation of 'make' to build the entire tree. Fully...
[kugel-rb.git] / apps / plugins / text_editor.c
blobc9d973fe2270a85c3a47742cb1948009ed064bb8
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 /* 128 kiB */
26 #else
27 #define MAX_CHARS 0x6000 /* 24 kiB */
28 #endif
29 #define MAX_LINE_LEN 2048
30 PLUGIN_HEADER
31 static const struct plugin_api* rb;
33 static char buffer[MAX_CHARS];
34 static char eol[3];
35 static int char_count = 0;
36 static int line_count = 0;
37 static int last_action_line = 0;
38 static int last_char_index = 0;
40 #define ACTION_INSERT 0
41 #define ACTION_GET 1
42 #define ACTION_REMOVE 2
43 #define ACTION_UPDATE 3
44 #define ACTION_CONCAT 4
46 int _do_action(int action, char* str, int line);
47 #ifndef HAVE_ADJUSTABLE_CPU_FREQ
48 #define do_action _do_action
49 #else
50 int do_action(int action, char* str, int line)
52 int r;
53 rb->cpu_boost(1);
54 r = _do_action(action,str,line);
55 rb->cpu_boost(0);
56 return r;
58 #endif
60 int _do_action(int action, char* str, int line)
62 int len;
63 int i=0,c=0;
64 if (line>=last_action_line)
66 i = last_action_line;
67 c = last_char_index;
69 while (i<line && i<line_count)
71 c += rb->strlen(&buffer[c])+1;
72 i++;
74 switch (action)
76 case ACTION_INSERT:
77 len = rb->strlen(str)+1;
78 if ( char_count+ len > MAX_CHARS )
79 return 0;
80 rb->memmove(&buffer[c+len],&buffer[c],char_count);
81 rb->strcpy(&buffer[c],str);
82 char_count += len;
83 line_count++;
84 break;
85 case ACTION_GET:
86 if (line > line_count)
87 return 0;
88 last_action_line = i;
89 last_char_index = c;
90 return c;
91 break;
92 case ACTION_REMOVE:
93 if (line > line_count)
94 return 0;
95 len = rb->strlen(&buffer[c])+1;
96 char_count -= len;
97 rb->memmove(&buffer[c],&buffer[c+len],char_count);
98 line_count--;
99 break;
100 case ACTION_UPDATE:
101 if (line > line_count)
102 return 0;
103 len = rb->strlen(&buffer[c])+1;
104 rb->memmove(&buffer[c+rb->strlen(str)+1],&buffer[c+len],char_count);
105 rb->strcpy(&buffer[c],str);
106 char_count += rb->strlen(str)+1-len;
107 break;
108 case ACTION_CONCAT:
109 if (line > line_count)
110 return 0;
111 rb->memmove(&buffer[c-1],&buffer[c],char_count);
112 break;
113 default:
114 return 0;
116 last_action_line = i;
117 last_char_index = c;
118 return 1;
120 char *list_get_name_cb(int selected_item, void* data,
121 char* buf, size_t buf_len)
123 (void)data;
124 char *b = &buffer[do_action(ACTION_GET,0,selected_item)];
125 if (rb->strlen(b) >= buf_len)
127 char t = b[buf_len-10];
128 b[buf_len-10] = '\0';
129 rb->snprintf(buf , buf_len, "%s ...", b);
130 b[buf_len-10] = t;
132 else rb->strncpy(buf, b, buf_len);
133 return buf;
135 char filename[MAX_PATH];
136 int get_eol_string(char* fn)
138 int fd=-1;
139 char t;
140 if (!fn)
141 return 0;
142 else if (!fn[0])
143 return 0;
144 fd = rb->PREFIX(open(fn,O_RDONLY));
145 if (fd<0)
146 return 0;
147 eol[0] = '\0';
148 while (!eol[0])
150 if (!rb->read(fd,&t,1))
152 rb->strcpy(eol,"\n");
153 return 0;
155 if (t == '\r')
157 if (rb->read(fd,&t,1) && t=='\n')
158 rb->strcpy(eol,"\r\n");
159 else rb->strcpy(eol,"\r");
161 else if (t == '\n')
163 rb->strcpy(eol,"\n");
166 rb->close(fd);
167 return 1;
170 void save_changes(int overwrite)
172 int fd;
173 int i;
175 if (!filename[0] || !overwrite)
177 rb->strcpy(filename,"/");
178 rb->kbd_input(filename,MAX_PATH);
181 fd = rb->open(filename,O_WRONLY|O_CREAT|O_TRUNC);
182 if (fd < 0)
184 rb->splash(HZ*2, "Changes NOT saved");
185 return;
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",&buffer[do_action(ACTION_GET,0,i)],eol);
200 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
201 rb->cpu_boost(0);
202 #endif
203 rb->close(fd);
206 void setup_lists(struct gui_synclist *lists, int sel)
208 rb->gui_synclist_init(lists,list_get_name_cb,0, false, 1, NULL);
209 rb->gui_synclist_set_icon_callback(lists,NULL);
210 rb->gui_synclist_set_nb_items(lists,line_count);
211 rb->gui_synclist_limit_scroll(lists,true);
212 rb->gui_synclist_select_item(lists, sel);
213 rb->gui_synclist_draw(lists);
215 enum {
216 MENU_RET_SAVE = -1,
217 MENU_RET_NO_UPDATE,
218 MENU_RET_UPDATE,
220 int do_item_menu(int cur_sel, char* copy_buffer)
222 int ret = 0;
223 MENUITEM_STRINGLIST(menu, "Line Options", NULL,
224 "Cut/Delete", "Copy",
225 "Insert Above", "Insert Below",
226 "Concat To Above", "Save",
227 "Show Playback Menu",);
229 switch (rb->do_menu(&menu, NULL, NULL, false))
231 case 0: /* cut */
232 rb->strcpy(copy_buffer,&buffer[do_action(ACTION_GET,0,cur_sel)]);
233 do_action(ACTION_REMOVE,0,cur_sel);
234 ret = MENU_RET_UPDATE;
235 break;
236 case 1: /* copy */
237 rb->strcpy(copy_buffer,&buffer[do_action(ACTION_GET,0,cur_sel)]);
238 ret = MENU_RET_NO_UPDATE;
239 break;
240 case 2: /* insert above */
241 if (!rb->kbd_input(copy_buffer,MAX_LINE_LEN))
243 do_action(ACTION_INSERT,copy_buffer,cur_sel);
244 copy_buffer[0]='\0';
245 ret = MENU_RET_UPDATE;
247 break;
248 case 3: /* insert below */
249 if (!rb->kbd_input(copy_buffer,MAX_LINE_LEN))
251 do_action(ACTION_INSERT,copy_buffer,cur_sel+1);
252 copy_buffer[0]='\0';
253 ret = MENU_RET_UPDATE;
255 break;
256 case 4: /* cat to above */
257 if (cur_sel>0)
259 do_action(ACTION_CONCAT,0,cur_sel);
260 ret = MENU_RET_UPDATE;
262 break;
263 case 5: /* save */
264 ret = MENU_RET_SAVE;
265 break;
266 case 6: /* playback menu */
267 playback_control(rb, NULL);
268 ret = MENU_RET_UPDATE;
269 break;
270 default:
271 ret = MENU_RET_NO_UPDATE;
272 break;
274 return ret;
277 #ifdef HAVE_LCD_COLOR
278 /* in misc.h but no need to polute the api */
279 #define toupper(c) (((c >= 'a') && (c <= 'z'))?c+'A':c)
280 #define isxdigit(c) ((c>='a' && c<= 'f') || (c>='A' && c<= 'F') \
281 || (c>='0' && c<= '9'))
282 #define hex2dec(c) (((c) >= '0' && ((c) <= '9')) ? (toupper(c)) - '0' : \
283 (toupper(c)) - 'A' + 10)
284 int hex_to_rgb(const char* hex, int* color)
285 { int ok = 1;
286 int i;
287 int red, green, blue;
289 if (rb->strlen(hex) == 6) {
290 for (i=0; i < 6; i++ ) {
291 if (!isxdigit(hex[i])) {
292 ok=0;
293 break;
297 if (ok) {
298 red = (hex2dec(hex[0]) << 4) | hex2dec(hex[1]);
299 green = (hex2dec(hex[2]) << 4) | hex2dec(hex[3]);
300 blue = (hex2dec(hex[4]) << 4) | hex2dec(hex[5]);
301 *color = LCD_RGBPACK(red,green,blue);
302 return 0;
306 return -1;
308 #endif /* HAVE_LCD_COLOR */
310 /* this is the plugin entry point */
311 enum plugin_status plugin_start(const struct plugin_api* api, const void* parameter)
313 int fd;
314 static char temp_line[MAX_LINE_LEN];
316 struct gui_synclist lists;
317 bool exit = false;
318 int button;
319 bool changed = false;
320 int cur_sel=0;
321 static char copy_buffer[MAX_LINE_LEN];
322 bool prev_show_statusbar;
323 #ifdef HAVE_LCD_COLOR
324 bool edit_colors_file = false;
325 #endif
327 rb = api;
329 copy_buffer[0]='\0';
330 prev_show_statusbar = rb->global_settings->statusbar;
331 rb->global_settings->statusbar = false;
333 #if LCD_DEPTH > 1
334 rb->lcd_set_backdrop(NULL);
335 #endif
337 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
338 rb->cpu_boost(1);
339 #endif
340 if (parameter)
342 #ifdef HAVE_LCD_COLOR
343 char *c = NULL;
344 #endif
345 rb->strcpy(filename,(char*)parameter);
346 if (!get_eol_string(filename))
348 rb->strcpy(eol,"\n");
350 fd = rb->open(filename,O_RDONLY);
351 if (fd<0)
353 rb->splashf(HZ*2,"Couldnt open file: %s",(char*)parameter);
354 return PLUGIN_ERROR;
356 #ifdef HAVE_LCD_COLOR
357 c = rb->strrchr(filename, '.');
358 if (c && !rb->strcmp(c, ".colours"))
359 edit_colors_file = true;
360 #endif
361 /* read in the file */
362 while (rb->read_line(fd,temp_line,MAX_LINE_LEN))
364 if (!do_action(ACTION_INSERT,temp_line,line_count))
366 rb->splashf(HZ*2,"Error reading file: %s",(char*)parameter);
367 rb->close(fd);
368 return PLUGIN_ERROR;
371 rb->close(fd);
373 else
375 filename[0] = '\0';
376 rb->strcpy(eol,"\n");
378 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
379 rb->cpu_boost(0);
380 #endif
381 /* now dump it in the list */
382 setup_lists(&lists,0);
383 rb->lcd_update();
384 while (!exit)
386 rb->gui_synclist_draw(&lists);
387 cur_sel = rb->gui_synclist_get_sel_pos(&lists);
388 button = rb->get_action(CONTEXT_LIST,TIMEOUT_BLOCK);
389 if (rb->gui_synclist_do_button(&lists,&button,LIST_WRAP_UNLESS_HELD))
390 continue;
391 switch (button)
393 case ACTION_STD_OK:
395 bool edit_text = true;
396 #ifdef HAVE_LCD_COLOR
397 int color;
398 #endif
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)
404 char *name = temp_line, *value = NULL;
405 char extension[MAX_LINE_LEN];
406 rb->settings_parseline(temp_line, &name, &value);
407 if (line_count)
409 MENUITEM_STRINGLIST(menu, "Edit What?", NULL,
410 "Extension", "Color",);
411 switch (rb->do_menu(&menu, NULL, NULL, false))
413 case 0:
414 edit_text = true;
415 break;
416 case 1:
417 edit_text = false;
418 if (value)
419 hex_to_rgb(value, &color);
420 else color = 0;
421 rb->strcpy(extension, name);
422 rb->set_color(rb->screens[SCREEN_MAIN], name, &color, -1);
423 rb->snprintf(temp_line, MAX_LINE_LEN, "%s: %02X%02X%02X",
424 extension, RGB_UNPACK_RED(color),
425 RGB_UNPACK_GREEN(color),
426 RGB_UNPACK_BLUE(color));
427 if (line_count)
429 do_action(ACTION_UPDATE,temp_line,cur_sel);
431 else do_action(ACTION_INSERT,temp_line,cur_sel);
432 changed = true;
433 break;
437 #endif
438 if (edit_text &&!rb->kbd_input(temp_line,MAX_LINE_LEN))
440 if (line_count)
442 do_action(ACTION_UPDATE,temp_line,cur_sel);
444 else do_action(ACTION_INSERT,temp_line,cur_sel);
445 changed = true;
448 break;
449 case ACTION_STD_CONTEXT:
450 if (!line_count) break;
451 rb->strcpy(copy_buffer,&buffer[do_action(ACTION_GET,0,cur_sel)]);
452 do_action(ACTION_REMOVE,0,cur_sel);
453 changed = true;
454 break;
455 case ACTION_STD_MENU:
456 { /* do the item menu */
457 switch (do_item_menu(cur_sel, copy_buffer))
459 case MENU_RET_SAVE:
460 save_changes(1);
461 changed = false;
462 break;
463 case MENU_RET_UPDATE:
464 changed = true;
465 break;
466 case MENU_RET_NO_UPDATE:
467 break;
470 break;
471 case ACTION_STD_CANCEL:
472 if (changed)
474 MENUITEM_STRINGLIST(menu, "Do What?", NULL,
475 "Return",
476 "Show Playback Menu", "Save Changes",
477 "Save As...", "Save and Exit",
478 "Ignore Changes and Exit");
479 switch (rb->do_menu(&menu, NULL, NULL, false))
481 case 0:
482 break;
483 case 1:
484 playback_control(rb, NULL);
485 break;
486 case 2: //save to disk
487 save_changes(1);
488 changed = 0;
489 break;
490 case 3:
491 save_changes(0);
492 changed = 0;
493 break;
495 case 4:
496 save_changes(1);
497 exit=1;
498 break;
499 case 5:
500 exit=1;
501 break;
504 else exit=1;
505 break;
507 rb->gui_synclist_set_nb_items(&lists,line_count);
509 rb->global_settings->statusbar = prev_show_statusbar;
510 return PLUGIN_OK;