diff options
Diffstat (limited to 'apps/plugins/text_editor.c')
-rw-r--r-- | apps/plugins/text_editor.c | 447 |
1 files changed, 447 insertions, 0 deletions
diff --git a/apps/plugins/text_editor.c b/apps/plugins/text_editor.c new file mode 100644 index 0000000000..6767940938 --- /dev/null +++ b/apps/plugins/text_editor.c | |||
@@ -0,0 +1,447 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2006 Jonathan Gordon | ||
11 | * | ||
12 | * All files in this archive are subject to the GNU General Public License. | ||
13 | * See the file COPYING in the source tree root for full license agreement. | ||
14 | * | ||
15 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
16 | * KIND, either express or implied. | ||
17 | * | ||
18 | ****************************************************************************/ | ||
19 | #include "plugin.h" | ||
20 | /* button definitions, every keypad must only have select,menu and cancel */ | ||
21 | #if CONFIG_KEYPAD == RECORDER_PAD | ||
22 | #define TEXT_EDITOR_SELECT BUTTON_PLAY | ||
23 | #define TEXT_EDITOR_CANCEL BUTTON_OFF | ||
24 | #define TEXT_EDITOR_ITEM_MENU BUTTON_F1 | ||
25 | |||
26 | #elif CONFIG_KEYPAD == ONDIO_PAD | ||
27 | #define TEXT_EDITOR_SELECT_PRE BUTTON_MENU | ||
28 | #define TEXT_EDITOR_SELECT (BUTTON_MENU|BUTTON_REL) | ||
29 | #define TEXT_EDITOR_CANCEL BUTTON_OFF | ||
30 | #define TEXT_EDITOR_ITEM_MENU BUTTON_MENU|BUTTON_REPEAT | ||
31 | |||
32 | #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || (CONFIG_KEYPAD == IRIVER_H300_PAD) | ||
33 | #define TEXT_EDITOR_SELECT BUTTON_SELECT | ||
34 | #define TEXT_EDITOR_CANCEL BUTTON_OFF | ||
35 | #define TEXT_EDITOR_DELETE BUTTON_REC | ||
36 | #define TEXT_EDITOR_ITEM_MENU BUTTON_MODE | ||
37 | |||
38 | #elif (CONFIG_KEYPAD == IPOD_3G_PAD) || (CONFIG_KEYPAD == IPOD_4G_PAD) | ||
39 | #define TEXT_EDITOR_SELECT_PRE BUTTON_SELECT | ||
40 | #define TEXT_EDITOR_SELECT ( BUTTON_SELECT | BUTTON_REL) | ||
41 | #define TEXT_EDITOR_CANCEL_PRE BUTTON_SELECT | ||
42 | #define TEXT_EDITOR_CANCEL (BUTTON_SELECT | BUTTON_MENU) | ||
43 | #define TEXT_EDITOR_DELETE (BUTTON_LEFT) | ||
44 | #define TEXT_EDITOR_ITEM_MENU (BUTTON_MENU) | ||
45 | |||
46 | #elif CONFIG_KEYPAD == IRIVER_IFP7XX_PAD | ||
47 | |||
48 | #elif CONFIG_KEYPAD == IAUDIO_X5_PAD | ||
49 | #define TEXT_EDITOR_SELECT BUTTON_SELECT | ||
50 | #define TEXT_EDITOR_CANCEL BUTTON_POWER | ||
51 | #define TEXT_EDITOR_ITEM_MENU BUTTON_PLAY | ||
52 | |||
53 | #elif CONFIG_KEYPAD == GIGABEAT_PAD | ||
54 | |||
55 | #else | ||
56 | #error TEXT_EDITOR: Unsupported keypad | ||
57 | #endif | ||
58 | |||
59 | #define MAX_LINE_LEN 128 | ||
60 | |||
61 | #if PLUGIN_BUFFER_SIZE > 0x45000 | ||
62 | #define MAX_LINES 2048 | ||
63 | #else | ||
64 | #define MAX_LINES 128 | ||
65 | #endif | ||
66 | |||
67 | PLUGIN_HEADER | ||
68 | static struct plugin_api* rb; | ||
69 | |||
70 | struct LineStruct { | ||
71 | char line[MAX_LINE_LEN]; | ||
72 | int prev; /* index to prev item, or -1 */ | ||
73 | int next; /* index to next item, or -1 */ | ||
74 | }; | ||
75 | |||
76 | struct LineStruct lines[MAX_LINES]; | ||
77 | int line_count = 0; | ||
78 | int first = -1, last = -1; | ||
79 | int indicies[MAX_LINES]; | ||
80 | /**************************** stuff for the linked lists ***************/ | ||
81 | int build_indicies(void) | ||
82 | { | ||
83 | int i=0, index = first; | ||
84 | struct LineStruct *line; | ||
85 | if (first==-1) | ||
86 | return 0; | ||
87 | while (i<line_count) | ||
88 | { | ||
89 | indicies[i++] = index; | ||
90 | DEBUGF("%d,",index); | ||
91 | line = &lines[index]; | ||
92 | index = line->next; | ||
93 | |||
94 | } | ||
95 | DEBUGF("\n"); | ||
96 | return 1; | ||
97 | } | ||
98 | |||
99 | int find_first_free(int start) | ||
100 | { | ||
101 | int i; | ||
102 | if ((start <0) || (start >=MAX_LINES)) | ||
103 | start = 0; | ||
104 | i = start; | ||
105 | do | ||
106 | { | ||
107 | if (lines[i].line[0] == '\0') | ||
108 | return i; | ||
109 | i = (i+1)%MAX_LINES; | ||
110 | } while (i!=start); | ||
111 | return -1; | ||
112 | } | ||
113 | |||
114 | int add_line(char *line, int idx_after_me) | ||
115 | { | ||
116 | struct LineStruct *temp; | ||
117 | int next; | ||
118 | int this_idx = find_first_free(idx_after_me); | ||
119 | if ((line_count >= MAX_LINES) || (this_idx == -1)) | ||
120 | return -1; | ||
121 | DEBUGF("line:%s ,idx_after_me=%d\n",line,idx_after_me); | ||
122 | if (idx_after_me == -1) /* add as the first item */ | ||
123 | { | ||
124 | rb->strcpy(lines[this_idx].line,line); | ||
125 | lines[this_idx].prev = -1; | ||
126 | if (first != -1) | ||
127 | lines[first].prev = this_idx; | ||
128 | lines[this_idx].next = first; | ||
129 | first = this_idx; | ||
130 | if (last == idx_after_me) | ||
131 | last = this_idx; | ||
132 | line_count++; | ||
133 | return 1; | ||
134 | } | ||
135 | |||
136 | temp = &lines[idx_after_me]; | ||
137 | next = lines[idx_after_me].next; | ||
138 | temp->next = this_idx; | ||
139 | rb->strcpy(lines[this_idx].line,line); | ||
140 | temp = &lines[this_idx]; | ||
141 | temp->next = next; | ||
142 | temp->prev = idx_after_me; | ||
143 | if (last == idx_after_me) | ||
144 | last = this_idx; | ||
145 | if (first == -1) | ||
146 | first = this_idx; | ||
147 | line_count ++; | ||
148 | return this_idx; | ||
149 | } | ||
150 | |||
151 | void del_line(int line) | ||
152 | { | ||
153 | int idx_prev, idx_next; | ||
154 | idx_prev = (&lines[line])->prev; | ||
155 | idx_next = (&lines[line])->next; | ||
156 | lines[line].line[0] = '\0'; | ||
157 | lines[idx_prev].next = idx_next; | ||
158 | lines[idx_next].prev = idx_prev; | ||
159 | line_count --; | ||
160 | } | ||
161 | char *list_get_name_cb(int selected_item,void* data,char* buf) | ||
162 | { | ||
163 | (void)data; | ||
164 | rb->strcpy(buf,lines[indicies[selected_item]].line); | ||
165 | return buf; | ||
166 | } | ||
167 | char filename[1024]; | ||
168 | void save_changes(int overwrite) | ||
169 | { | ||
170 | int fd; | ||
171 | int i; | ||
172 | |||
173 | if (!filename[0] || overwrite) | ||
174 | { | ||
175 | rb->strcpy(filename,"/"); | ||
176 | rb->kbd_input(filename,1024); | ||
177 | } | ||
178 | |||
179 | fd = rb->open(filename,O_WRONLY|O_CREAT); | ||
180 | if (!fd) | ||
181 | { | ||
182 | rb->splash(HZ*2,1,"Changes NOT saved"); | ||
183 | return; | ||
184 | } | ||
185 | |||
186 | rb->lcd_clear_display(); | ||
187 | build_indicies(); | ||
188 | for (i=0;i<line_count;i++) | ||
189 | { | ||
190 | rb->fdprintf(fd,"%s\n",lines[indicies[i]].line); | ||
191 | } | ||
192 | |||
193 | rb->close(fd); | ||
194 | } | ||
195 | |||
196 | void setup_lists(struct gui_synclist *lists) | ||
197 | { | ||
198 | build_indicies(); | ||
199 | rb->gui_synclist_init(lists,list_get_name_cb,0); | ||
200 | rb->gui_synclist_set_icon_callback(lists,NULL); | ||
201 | rb->gui_synclist_set_nb_items(lists,line_count); | ||
202 | rb->gui_synclist_limit_scroll(lists,true); | ||
203 | rb->gui_synclist_select_item(lists, 0); | ||
204 | rb->gui_synclist_draw(lists); | ||
205 | } | ||
206 | enum { | ||
207 | MENU_RET_SAVE = -1, | ||
208 | MENU_RET_NO_UPDATE, | ||
209 | MENU_RET_UPDATE, | ||
210 | }; | ||
211 | int do_item_menu(int cur_sel, char* copy_buffer) | ||
212 | { | ||
213 | int m, ret = 0; | ||
214 | static const struct menu_item items[] = { | ||
215 | { "Cut", NULL }, | ||
216 | { "Copy", NULL }, | ||
217 | { "", NULL }, | ||
218 | { "Insert Above", NULL }, | ||
219 | { "Insert Below", NULL }, | ||
220 | { "", NULL }, | ||
221 | { "Cat To Above",NULL }, | ||
222 | /* { "Split Line",NULL }, */ | ||
223 | { "", NULL }, | ||
224 | { "Save", NULL }, | ||
225 | }; | ||
226 | m = rb->menu_init(items, sizeof(items) / sizeof(*items), | ||
227 | NULL, NULL, NULL, NULL); | ||
228 | |||
229 | switch (rb->menu_show(m)) | ||
230 | { | ||
231 | case 0: /* cut */ | ||
232 | rb->strcpy(copy_buffer,lines[indicies[cur_sel]].line); | ||
233 | del_line(indicies[cur_sel]); | ||
234 | ret = MENU_RET_UPDATE; | ||
235 | break; | ||
236 | case 1: /* copy */ | ||
237 | rb->strcpy(copy_buffer,lines[indicies[cur_sel]].line); | ||
238 | ret = MENU_RET_NO_UPDATE; | ||
239 | break; | ||
240 | case 2: /* blank */ | ||
241 | ret = MENU_RET_NO_UPDATE; | ||
242 | break; | ||
243 | |||
244 | case 3: /* insert above */ | ||
245 | if (!rb->kbd_input(copy_buffer,MAX_LINE_LEN)) | ||
246 | { | ||
247 | add_line(copy_buffer,lines[indicies[cur_sel]].prev); | ||
248 | copy_buffer[0]='\0'; | ||
249 | ret = MENU_RET_UPDATE; | ||
250 | } | ||
251 | break; | ||
252 | case 4: /* insert below */ | ||
253 | if (!rb->kbd_input(copy_buffer,MAX_LINE_LEN)) | ||
254 | { | ||
255 | add_line(copy_buffer,indicies[cur_sel]); | ||
256 | copy_buffer[0]='\0'; | ||
257 | ret = MENU_RET_UPDATE; | ||
258 | } | ||
259 | break; | ||
260 | case 5: /* blank */ | ||
261 | ret = MENU_RET_NO_UPDATE; | ||
262 | break; | ||
263 | case 6: /* cat to above */ | ||
264 | if (cur_sel>0) | ||
265 | { | ||
266 | rb->strcat(lines[indicies[cur_sel-1]].line,lines[indicies[cur_sel]].line); | ||
267 | del_line(indicies[cur_sel]); | ||
268 | ret = MENU_RET_UPDATE; | ||
269 | } | ||
270 | break; | ||
271 | /* case 7: // split line */ | ||
272 | |||
273 | case 7: /* save */ | ||
274 | ret = MENU_RET_SAVE; | ||
275 | break; | ||
276 | default: | ||
277 | ret = MENU_RET_NO_UPDATE; | ||
278 | break; | ||
279 | } | ||
280 | rb->menu_exit(m); | ||
281 | return ret; | ||
282 | } | ||
283 | /* this is the plugin entry point */ | ||
284 | enum plugin_status plugin_start(struct plugin_api* api, void* parameter) | ||
285 | { | ||
286 | int fd; | ||
287 | char temp_line[MAX_LINE_LEN]; | ||
288 | |||
289 | struct gui_synclist lists; | ||
290 | bool exit = false; | ||
291 | int button, last_button = BUTTON_NONE; | ||
292 | bool changed = false; | ||
293 | int cur_sel; | ||
294 | char copy_buffer[MAX_LINE_LEN]; copy_buffer[0]='\0'; | ||
295 | |||
296 | rb = api; | ||
297 | if (parameter) | ||
298 | { | ||
299 | rb->strcpy(filename,(char*)parameter); | ||
300 | fd = rb->open(filename,O_RDONLY); | ||
301 | if (fd<0) | ||
302 | { | ||
303 | rb->splash(HZ*2,true,"Couldnt open file: %s",(char*)parameter); | ||
304 | return PLUGIN_ERROR; | ||
305 | } | ||
306 | /* read in the file */ | ||
307 | while (rb->read_line(fd,temp_line,MAX_LINE_LEN)) | ||
308 | { | ||
309 | if (add_line(temp_line,last) < 0) | ||
310 | { | ||
311 | rb->splash(HZ*2,true,"Error reading file: %s",(char*)parameter); | ||
312 | rb->close(fd); | ||
313 | return PLUGIN_ERROR; | ||
314 | } | ||
315 | } | ||
316 | rb->close(fd); | ||
317 | } | ||
318 | else filename[0] = '\0'; | ||
319 | /* now dump it in the list */ | ||
320 | setup_lists(&lists); | ||
321 | while (!exit) | ||
322 | { | ||
323 | rb->gui_synclist_draw(&lists); | ||
324 | cur_sel = rb->gui_synclist_get_sel_pos(&lists); | ||
325 | button = rb->button_get(true); | ||
326 | if (rb->gui_synclist_do_button(&lists,button)) | ||
327 | continue; | ||
328 | switch (button) | ||
329 | { | ||
330 | case TEXT_EDITOR_SELECT: | ||
331 | { | ||
332 | #ifdef TEXT_EDITOR_SELECT_PRE | ||
333 | if (last_button != TEXT_EDITOR_SELECT_PRE) | ||
334 | break; | ||
335 | #endif | ||
336 | char buf[MAX_LINE_LEN];buf[0]='\0'; | ||
337 | |||
338 | if (line_count) | ||
339 | rb->strcpy(buf,lines[indicies[cur_sel]].line); | ||
340 | if (!rb->kbd_input(buf,MAX_LINE_LEN)) | ||
341 | { | ||
342 | if (line_count) | ||
343 | rb->strcpy(lines[indicies[cur_sel]].line,buf); | ||
344 | else { add_line(buf, first); setup_lists(&lists); } | ||
345 | changed = true; | ||
346 | } | ||
347 | } | ||
348 | break; | ||
349 | #ifdef TEXT_EDITOR_DELETE | ||
350 | case TEXT_EDITOR_DELETE: | ||
351 | #ifdef TEXT_EDITOR_DELETE_PRE | ||
352 | if (last_button != TEXT_EDITOR_DELETE_PRE) | ||
353 | break; | ||
354 | #endif | ||
355 | if (!line_count) break; | ||
356 | rb->strcpy(copy_buffer,lines[indicies[cur_sel]].line); | ||
357 | del_line(indicies[cur_sel]); | ||
358 | changed = true; | ||
359 | setup_lists(&lists); | ||
360 | break; | ||
361 | #endif | ||
362 | #ifdef TEXT_EDITOR_ITEM_MENU | ||
363 | case TEXT_EDITOR_ITEM_MENU: | ||
364 | #ifdef TEXT_EDITOR_RC_ITEM_MENU | ||
365 | case TEXT_EDITOR_RC_ITEM_MENU: | ||
366 | #endif | ||
367 | #ifdef TEXT_EDITOR_ITEM_MENU_PRE | ||
368 | if (lastbutton != TEXT_EDITOR_ITEM_MENU_PRE | ||
369 | #ifdef TEXT_EDITOR_RC_ITEM_MENU_PRE | ||
370 | && lastbutton != TEXT_EDITOR_RC_ITEM_MENU_PRE | ||
371 | #endif | ||
372 | ) | ||
373 | break; | ||
374 | #endif | ||
375 | { /* do the item menu */ | ||
376 | switch (do_item_menu(cur_sel, copy_buffer)) | ||
377 | { | ||
378 | case MENU_RET_SAVE: | ||
379 | save_changes(1); | ||
380 | changed = false; | ||
381 | break; | ||
382 | case MENU_RET_UPDATE: | ||
383 | changed = true; | ||
384 | setup_lists(&lists); | ||
385 | break; | ||
386 | case MENU_RET_NO_UPDATE: | ||
387 | break; | ||
388 | } | ||
389 | } | ||
390 | break; | ||
391 | #endif /* TEXT_EDITOR_ITEM_MENU */ | ||
392 | case TEXT_EDITOR_CANCEL: | ||
393 | #ifdef TEXT_EDITOR_CANCEL_PRE | ||
394 | if (last_button != TEXT_EDITOR_CANCEL_PRE) | ||
395 | break; | ||
396 | #endif | ||
397 | if (changed) | ||
398 | { | ||
399 | int m; | ||
400 | int result; | ||
401 | |||
402 | static const struct menu_item items[] = { | ||
403 | { "Return", NULL }, | ||
404 | { " ", NULL }, | ||
405 | { "Save Changes", NULL }, | ||
406 | { "Save As...", NULL }, | ||
407 | { " ", NULL }, | ||
408 | { "Save and Exit", NULL }, | ||
409 | { "Ignore Changes and Exit", NULL }, | ||
410 | }; | ||
411 | |||
412 | m = rb->menu_init(items, sizeof(items) / sizeof(*items), | ||
413 | NULL, NULL, NULL, NULL); | ||
414 | |||
415 | result=rb->menu_show(m); | ||
416 | |||
417 | switch (result) | ||
418 | { | ||
419 | case 0: | ||
420 | break; | ||
421 | case 2: //save to disk | ||
422 | save_changes(1); | ||
423 | changed = 0; | ||
424 | break; | ||
425 | case 3: | ||
426 | save_changes(0); | ||
427 | changed = 0; | ||
428 | break; | ||
429 | |||
430 | case 5: | ||
431 | save_changes(1); | ||
432 | exit=1; | ||
433 | break; | ||
434 | case 6: | ||
435 | exit=1; | ||
436 | break; | ||
437 | } | ||
438 | rb->menu_exit(m); | ||
439 | } | ||
440 | else exit=1; | ||
441 | break; | ||
442 | } | ||
443 | last_button = button; | ||
444 | } | ||
445 | |||
446 | return PLUGIN_OK; | ||
447 | } | ||