summaryrefslogtreecommitdiff
path: root/apps/plugins/text_editor.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/text_editor.c')
-rw-r--r--apps/plugins/text_editor.c447
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
67PLUGIN_HEADER
68static struct plugin_api* rb;
69
70struct 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
76struct LineStruct lines[MAX_LINES];
77int line_count = 0;
78int first = -1, last = -1;
79int indicies[MAX_LINES];
80/**************************** stuff for the linked lists ***************/
81int 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
99int 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
114int 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
151void 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}
161char *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}
167char filename[1024];
168void 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
196void 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}
206enum {
207 MENU_RET_SAVE = -1,
208 MENU_RET_NO_UPDATE,
209 MENU_RET_UPDATE,
210};
211int 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 */
284enum 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}