Decrease the text buffer a tiny bit to make it fit again on archos. This really shoul...
[kugel-rb.git] / apps / plugins / text_editor.c
blob06dc0985017c4070474416b47f245e87b9f245ff
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 0x5FE0 /* a bit less than 24 kiB */
28 #endif
29 #define MAX_LINE_LEN 2048
30 PLUGIN_HEADER
32 static char buffer[MAX_CHARS];
33 static char eol[3];
34 static int char_count = 0;
35 static int line_count = 0;
36 static int last_action_line = 0;
37 static int last_char_index = 0;
39 #define ACTION_INSERT 0
40 #define ACTION_GET 1
41 #define ACTION_REMOVE 2
42 #define ACTION_UPDATE 3
43 #define ACTION_CONCAT 4
45 int _do_action(int action, char* str, int line);
46 #ifndef HAVE_ADJUSTABLE_CPU_FREQ
47 #define do_action _do_action
48 #else
49 int do_action(int action, char* str, int line)
51 int r;
52 rb->cpu_boost(1);
53 r = _do_action(action,str,line);
54 rb->cpu_boost(0);
55 return r;
57 #endif
59 int _do_action(int action, char* str, int line)
61 int len, lennew;
62 int i=0,c=0;
63 if (line>=last_action_line)
65 i = last_action_line;
66 c = last_char_index;
68 while (i<line && i<line_count)
70 c += rb->strlen(&buffer[c])+1;
71 i++;
73 switch (action)
75 case ACTION_INSERT:
76 len = rb->strlen(str)+1;
77 if ( char_count+ len > MAX_CHARS )
78 return 0;
79 rb->memmove(&buffer[c+len],&buffer[c],char_count-c);
80 rb->strcpy(&buffer[c],str);
81 char_count += len;
82 line_count++;
83 break;
84 case ACTION_GET:
85 if (line > line_count)
86 return 0;
87 last_action_line = i;
88 last_char_index = c;
89 return c;
90 break;
91 case ACTION_REMOVE:
92 if (line > line_count)
93 return 0;
94 len = rb->strlen(&buffer[c])+1;
95 rb->memmove(&buffer[c],&buffer[c+len],char_count-c-len);
96 char_count -= len;
97 line_count--;
98 break;
99 case ACTION_UPDATE:
100 if (line > line_count)
101 return 0;
102 len = rb->strlen(&buffer[c])+1;
103 lennew = rb->strlen(str)+1;
104 if ( char_count+ lennew-len > MAX_CHARS )
105 return 0;
106 rb->memmove(&buffer[c+lennew],&buffer[c+len],char_count-c-len);
107 rb->strcpy(&buffer[c],str);
108 char_count += lennew-len;
109 break;
110 case ACTION_CONCAT:
111 if (line > line_count)
112 return 0;
113 rb->memmove(&buffer[c-1],&buffer[c],char_count-c);
114 char_count--;
115 line_count--;
116 break;
117 default:
118 return 0;
120 last_action_line = i;
121 last_char_index = c;
122 return 1;
124 char *list_get_name_cb(int selected_item, void* data,
125 char* buf, size_t buf_len)
127 (void)data;
128 char *b = &buffer[do_action(ACTION_GET,0,selected_item)];
129 if (rb->strlen(b) >= buf_len)
131 char t = b[buf_len-10];
132 b[buf_len-10] = '\0';
133 rb->snprintf(buf , buf_len, "%s ...", b);
134 b[buf_len-10] = t;
136 else rb->strlcpy(buf, b, buf_len);
137 return buf;
140 char filename[MAX_PATH];
141 bool newfile;
142 int get_eol_string(char* fn)
144 int fd, result;
145 char t;
147 if (!fn || !fn[0])
148 return 0;
149 fd = rb->open(fn,O_RDONLY);
150 if (fd<0)
151 return 0;
153 eol[0] = '\0';
154 result = 1;
155 while (!eol[0])
157 if (!rb->read(fd,&t,1))
159 rb->strcpy(eol,"\n");
160 result = 0;
162 if (t == '\r')
164 if (rb->read(fd,&t,1) && t=='\n')
165 rb->strcpy(eol,"\r\n");
166 else
167 rb->strcpy(eol,"\r");
169 else if (t == '\n')
171 rb->strcpy(eol,"\n");
174 rb->close(fd);
175 return result;
178 bool save_changes(int overwrite)
180 int fd;
181 int i;
183 if (newfile || !overwrite)
185 if(rb->kbd_input(filename,MAX_PATH))
187 newfile = true;
188 rb->splash(HZ, "Cancelled");
189 return false;
193 fd = rb->open(filename,O_WRONLY|O_CREAT|O_TRUNC);
194 if (fd < 0)
196 newfile = true;
197 rb->splash(HZ*2, "Changes NOT saved");
198 return false;
201 if (!overwrite)
202 /* current directory may have changed */
203 rb->reload_directory();
205 rb->lcd_clear_display();
206 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
207 rb->cpu_boost(1);
208 #endif
209 for (i=0;i<line_count;i++)
211 rb->fdprintf(fd,"%s%s",&buffer[do_action(ACTION_GET,0,i)],eol);
213 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
214 rb->cpu_boost(0);
215 #endif
216 rb->close(fd);
217 newfile = false;
218 return true;
221 void setup_lists(struct gui_synclist *lists, int sel)
223 rb->gui_synclist_init(lists,list_get_name_cb,0, false, 1, NULL);
224 rb->gui_synclist_set_icon_callback(lists,NULL);
225 rb->gui_synclist_set_nb_items(lists,line_count);
226 rb->gui_synclist_limit_scroll(lists,true);
227 rb->gui_synclist_select_item(lists, sel);
228 rb->gui_synclist_draw(lists);
230 enum {
231 MENU_RET_SAVE = -1,
232 MENU_RET_NO_UPDATE,
233 MENU_RET_UPDATE,
235 int do_item_menu(int cur_sel, char* copy_buffer)
237 int ret = 0;
238 MENUITEM_STRINGLIST(menu, "Line Options", NULL,
239 "Cut/Delete", "Copy",
240 "Insert Above", "Insert Below",
241 "Concat To Above", "Save",
242 "Show Playback Menu",);
244 switch (rb->do_menu(&menu, NULL, NULL, false))
246 case 0: /* cut */
247 rb->strcpy(copy_buffer,&buffer[do_action(ACTION_GET,0,cur_sel)]);
248 do_action(ACTION_REMOVE,0,cur_sel);
249 ret = MENU_RET_UPDATE;
250 break;
251 case 1: /* copy */
252 rb->strcpy(copy_buffer,&buffer[do_action(ACTION_GET,0,cur_sel)]);
253 ret = MENU_RET_NO_UPDATE;
254 break;
255 case 2: /* insert above */
256 if (!rb->kbd_input(copy_buffer,MAX_LINE_LEN))
258 do_action(ACTION_INSERT,copy_buffer,cur_sel);
259 copy_buffer[0]='\0';
260 ret = MENU_RET_UPDATE;
262 break;
263 case 3: /* insert below */
264 if (!rb->kbd_input(copy_buffer,MAX_LINE_LEN))
266 do_action(ACTION_INSERT,copy_buffer,cur_sel+1);
267 copy_buffer[0]='\0';
268 ret = MENU_RET_UPDATE;
270 break;
271 case 4: /* cat to above */
272 if (cur_sel>0)
274 do_action(ACTION_CONCAT,0,cur_sel);
275 ret = MENU_RET_UPDATE;
277 break;
278 case 5: /* save */
279 ret = MENU_RET_SAVE;
280 break;
281 case 6: /* playback menu */
282 playback_control(NULL);
283 ret = MENU_RET_NO_UPDATE;
284 break;
285 default:
286 ret = MENU_RET_NO_UPDATE;
287 break;
289 return ret;
292 #ifdef HAVE_LCD_COLOR
293 /* in misc.h but no need to polute the api */
294 #define toupper(c) (((c >= 'a') && (c <= 'z'))?c+'A':c)
295 #define isxdigit(c) ((c>='a' && c<= 'f') || (c>='A' && c<= 'F') \
296 || (c>='0' && c<= '9'))
297 #define hex2dec(c) (((c) >= '0' && ((c) <= '9')) ? (toupper(c)) - '0' : \
298 (toupper(c)) - 'A' + 10)
299 int hex_to_rgb(const char* hex, int* color)
300 { int ok = 1;
301 int i;
302 int red, green, blue;
304 if (rb->strlen(hex) == 6) {
305 for (i=0; i < 6; i++ ) {
306 if (!isxdigit(hex[i])) {
307 ok=0;
308 break;
312 if (ok) {
313 red = (hex2dec(hex[0]) << 4) | hex2dec(hex[1]);
314 green = (hex2dec(hex[2]) << 4) | hex2dec(hex[3]);
315 blue = (hex2dec(hex[4]) << 4) | hex2dec(hex[5]);
316 *color = LCD_RGBPACK(red,green,blue);
317 return 0;
321 return -1;
323 #endif /* HAVE_LCD_COLOR */
325 /* this is the plugin entry point */
326 enum plugin_status plugin_start(const void* parameter)
328 int fd;
329 static char temp_line[MAX_LINE_LEN];
331 struct gui_synclist lists;
332 bool exit = false;
333 int button;
334 bool changed = false;
335 int cur_sel=0;
336 static char copy_buffer[MAX_LINE_LEN];
337 #ifdef HAVE_LCD_COLOR
338 bool edit_colors_file = false;
339 #endif
341 copy_buffer[0]='\0';
343 #if LCD_DEPTH > 1
344 rb->lcd_set_backdrop(NULL);
345 #endif
347 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
348 rb->cpu_boost(1);
349 #endif
350 if (parameter)
352 #ifdef HAVE_LCD_COLOR
353 char *c = NULL;
354 #endif
355 rb->strcpy(filename,(char*)parameter);
356 if (!get_eol_string(filename))
358 rb->strcpy(eol,"\n");
360 fd = rb->open(filename,O_RDONLY);
361 if (fd<0)
363 rb->splashf(HZ*2,"Couldnt open file: %s",(char*)parameter);
364 return PLUGIN_ERROR;
366 #ifdef HAVE_LCD_COLOR
367 c = rb->strrchr(filename, '.');
368 if (c && !rb->strcmp(c, ".colours"))
369 edit_colors_file = true;
370 #endif
371 /* read in the file */
372 while (rb->read_line(fd,temp_line,MAX_LINE_LEN))
374 if (!do_action(ACTION_INSERT,temp_line,line_count))
376 rb->splashf(HZ*2,"Error reading file: %s",(char*)parameter);
377 rb->close(fd);
378 return PLUGIN_ERROR;
381 rb->close(fd);
382 newfile = false;
384 else
386 rb->strcpy(filename,"/");
387 rb->strcpy(eol,"\n");
388 newfile = true;
390 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
391 rb->cpu_boost(0);
392 #endif
393 /* now dump it in the list */
394 setup_lists(&lists,0);
395 rb->lcd_update();
396 while (!exit)
398 rb->gui_synclist_draw(&lists);
399 cur_sel = rb->gui_synclist_get_sel_pos(&lists);
400 button = rb->get_action(CONTEXT_LIST,TIMEOUT_BLOCK);
401 if (rb->gui_synclist_do_button(&lists,&button,LIST_WRAP_UNLESS_HELD))
402 continue;
403 switch (button)
405 case ACTION_STD_OK:
407 if (line_count)
408 rb->strcpy(temp_line,&buffer[do_action(ACTION_GET,0,cur_sel)]);
409 #ifdef HAVE_LCD_COLOR
410 if (edit_colors_file && line_count)
412 char *name = temp_line, *value = NULL;
413 char extension[MAX_LINE_LEN];
414 int color, old_color;
415 bool temp_changed;
416 rb->settings_parseline(temp_line, &name, &value);
417 if (line_count)
419 MENUITEM_STRINGLIST(menu, "Edit What?", NULL,
420 "Extension", "Colour",);
421 rb->strcpy(extension, name);
422 if (value)
423 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,MAX_LINE_LEN);
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;
437 default:
438 /* Should never happen but makes compiler happy */
439 temp_changed = false;
442 if (temp_changed)
444 rb->snprintf(temp_line, MAX_LINE_LEN, "%s: %02X%02X%02X",
445 extension, RGB_UNPACK_RED(color),
446 RGB_UNPACK_GREEN(color),
447 RGB_UNPACK_BLUE(color));
448 do_action(ACTION_UPDATE, temp_line, cur_sel);
449 changed = true;
453 else
454 #endif
455 if (!rb->kbd_input(temp_line,MAX_LINE_LEN))
457 if (line_count)
458 do_action(ACTION_UPDATE,temp_line,cur_sel);
459 else
460 do_action(ACTION_INSERT,temp_line,cur_sel);
461 changed = true;
464 break;
465 case ACTION_STD_CONTEXT:
466 if (!line_count) break;
467 rb->strcpy(copy_buffer,&buffer[do_action(ACTION_GET,0,cur_sel)]);
468 do_action(ACTION_REMOVE,0,cur_sel);
469 changed = true;
470 break;
471 case ACTION_STD_MENU:
472 { /* do the item menu */
473 switch (do_item_menu(cur_sel, copy_buffer))
475 case MENU_RET_SAVE:
476 if(save_changes(1))
477 changed = false;
478 break;
479 case MENU_RET_UPDATE:
480 changed = true;
481 break;
482 case MENU_RET_NO_UPDATE:
483 break;
486 break;
487 case ACTION_STD_CANCEL:
488 if (changed)
490 MENUITEM_STRINGLIST(menu, "Do What?", NULL,
491 "Return",
492 "Show Playback Menu", "Save Changes",
493 "Save As...", "Save and Exit",
494 "Ignore Changes and Exit");
495 switch (rb->do_menu(&menu, NULL, NULL, false))
497 case 0:
498 break;
499 case 1:
500 playback_control(NULL);
501 break;
502 case 2: //save to disk
503 if(save_changes(1))
504 changed = 0;
505 break;
506 case 3:
507 if(save_changes(0))
508 changed = 0;
509 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;