diff options
Diffstat (limited to 'apps/plugins/shortcuts.c')
-rw-r--r-- | apps/plugins/shortcuts.c | 550 |
1 files changed, 550 insertions, 0 deletions
diff --git a/apps/plugins/shortcuts.c b/apps/plugins/shortcuts.c new file mode 100644 index 0000000000..af92bf4634 --- /dev/null +++ b/apps/plugins/shortcuts.c | |||
@@ -0,0 +1,550 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2007 Bryan Childs | ||
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 | |||
20 | #include "plugin.h" | ||
21 | |||
22 | PLUGIN_HEADER | ||
23 | |||
24 | static struct plugin_api* rb; | ||
25 | |||
26 | #define SHORTCUTS_FILENAME "/shortcuts.link" | ||
27 | #define MAX_SHORTCUTS 50 | ||
28 | |||
29 | MEM_FUNCTION_WRAPPERS(rb); | ||
30 | |||
31 | typedef struct sc_file_s | ||
32 | { | ||
33 | int readsize; | ||
34 | char* filebuf; | ||
35 | } sc_file_t; | ||
36 | |||
37 | typedef struct sc_entries_s | ||
38 | { | ||
39 | char shortcut[MAX_PATH+1]; | ||
40 | int sc_len; | ||
41 | struct sc_entries_s* next; | ||
42 | } sc_entries_t; | ||
43 | |||
44 | enum shortcut_type { | ||
45 | SCTYPE_NONE, | ||
46 | SCTYPE_FILE, | ||
47 | SCTYPE_DIR, | ||
48 | }; | ||
49 | |||
50 | enum sc_list_action_type { | ||
51 | SCLA_NONE, | ||
52 | SCLA_SELECT, | ||
53 | SCLA_DELETE, | ||
54 | }; | ||
55 | |||
56 | void sc_alloc_init(void); | ||
57 | void* sc_malloc(unsigned int size); | ||
58 | bool sc_init(void); | ||
59 | enum sc_list_action_type draw_sc_list(struct gui_synclist gui_sc); | ||
60 | char* build_sc_list(int selected_item, void* data, char* buffer); | ||
61 | void delete_sc(int sc_num); | ||
62 | bool load_sc_file(void); | ||
63 | bool load_user_sc_file(char* filename); | ||
64 | bool exists(char* filename); | ||
65 | enum plugin_status list_sc(void); | ||
66 | enum plugin_status write_sc_file(char* directory_name,enum shortcut_type st); | ||
67 | |||
68 | char str_dirname[MAX_PATH]; | ||
69 | ssize_t bufleft; | ||
70 | long mem_ptr; | ||
71 | long bufsize; | ||
72 | unsigned char* mallocbuf; | ||
73 | bool its_a_dir = false; | ||
74 | bool user_file = false; | ||
75 | sc_file_t the_file; | ||
76 | sc_entries_t* shortcuts = 0; | ||
77 | sc_entries_t* lastentry = 0; | ||
78 | int total_entries = 0; | ||
79 | int gselected_item = 0; | ||
80 | |||
81 | void sc_alloc_init(void){ | ||
82 | mem_ptr=0; | ||
83 | |||
84 | mallocbuf = rb->plugin_get_buffer(&bufleft); | ||
85 | bufsize = (long)bufleft; | ||
86 | |||
87 | rb->memset(mallocbuf,0,bufsize); | ||
88 | |||
89 | return; | ||
90 | } | ||
91 | |||
92 | void* sc_malloc(unsigned int size) { | ||
93 | void* x; | ||
94 | |||
95 | if(mem_ptr + (long)size > bufsize) { | ||
96 | rb->splash(HZ*2,"OUT OF MEMORY"); | ||
97 | return NULL; | ||
98 | } | ||
99 | |||
100 | x=&mallocbuf[mem_ptr]; | ||
101 | mem_ptr+=(size+3)&~3; /* Keep memory 32-bit aligned */ | ||
102 | |||
103 | return x; | ||
104 | } | ||
105 | |||
106 | bool exists(char* filename){ | ||
107 | int fd = 0; | ||
108 | /*strip trailing slashes */ | ||
109 | char* ptr = rb->strrchr((char*)filename, '/') + 1; | ||
110 | int dirlen = (ptr - (char*)filename); | ||
111 | rb->strncpy(str_dirname, (char*)filename, dirlen); | ||
112 | str_dirname[dirlen] = 0; | ||
113 | |||
114 | fd = rb->open(str_dirname,O_RDONLY); | ||
115 | if (!fd) { | ||
116 | return false; | ||
117 | } | ||
118 | rb->close(fd); | ||
119 | return true; | ||
120 | } | ||
121 | |||
122 | bool sc_init(void) { | ||
123 | return load_sc_file(); | ||
124 | } | ||
125 | |||
126 | enum sc_list_action_type draw_sc_list(struct gui_synclist gui_sc) { | ||
127 | int button; | ||
128 | |||
129 | rb->gui_synclist_draw(&gui_sc); | ||
130 | |||
131 | while (true) { | ||
132 | /* draw the statusbar, should be done often */ | ||
133 | rb->gui_syncstatusbar_draw(rb->statusbars, true); | ||
134 | /* user input */ | ||
135 | button = rb->get_action(CONTEXT_LIST,TIMEOUT_BLOCK); | ||
136 | if (rb->gui_synclist_do_button(&gui_sc,button, | ||
137 | LIST_WRAP_UNLESS_HELD)) { | ||
138 | /* automatic handling of user input. | ||
139 | * _UNLESS_HELD can be _ON or _OFF also | ||
140 | * selection changed, so redraw */ | ||
141 | continue; | ||
142 | } | ||
143 | switch (button) { /* process the user input */ | ||
144 | case ACTION_STD_OK: | ||
145 | gselected_item = rb->gui_synclist_get_sel_pos(&gui_sc); | ||
146 | return SCLA_SELECT; | ||
147 | break; | ||
148 | case ACTION_STD_MENU: | ||
149 | if(!user_file){ | ||
150 | gselected_item = rb->gui_synclist_get_sel_pos(&gui_sc); | ||
151 | rb->splash(HZ,"Deleting item"); | ||
152 | return SCLA_DELETE; | ||
153 | } else { | ||
154 | return SCLA_NONE; | ||
155 | } | ||
156 | break; | ||
157 | case ACTION_STD_CANCEL: | ||
158 | return SCLA_NONE; | ||
159 | break; | ||
160 | } | ||
161 | } | ||
162 | } | ||
163 | |||
164 | char* build_sc_list(int selected_item, void* data, char* buffer) { | ||
165 | int i; | ||
166 | sc_entries_t* temp_node = (sc_entries_t*)data; | ||
167 | char text_buffer[MAX_PATH]; | ||
168 | |||
169 | for (i=0;i<selected_item && temp_node != NULL;i++){ | ||
170 | temp_node = temp_node->next; | ||
171 | } | ||
172 | if (temp_node == NULL){ | ||
173 | return NULL; | ||
174 | } | ||
175 | rb->snprintf(text_buffer, MAX_PATH, "%s", temp_node->shortcut); | ||
176 | |||
177 | rb->strcpy(buffer, text_buffer); | ||
178 | return buffer; | ||
179 | } | ||
180 | |||
181 | void delete_sc(int sc_num){ | ||
182 | /* Note: This function is a nasty hack and I should probably | ||
183 | * be shot for doing it this way.*/ | ||
184 | int i; | ||
185 | sc_entries_t* current = shortcuts; | ||
186 | sc_entries_t* previous = shortcuts; | ||
187 | |||
188 | if(total_entries==1){ | ||
189 | /* This is the only item in the file | ||
190 | * so just set the whole shortcuts list | ||
191 | * to zero */ | ||
192 | shortcuts=0; | ||
193 | } else { | ||
194 | if(sc_num!=0){ | ||
195 | for (i=0;i<sc_num && current != NULL;i++){ | ||
196 | /* keep previous pointing at the prior | ||
197 | * item in the list */ | ||
198 | if(previous!=current){ | ||
199 | previous = current; | ||
200 | } | ||
201 | current=current->next; | ||
202 | } | ||
203 | /* current should now be pointing at the item | ||
204 | * to be deleted, so update the previous item | ||
205 | * to point to whatever current is pointing to | ||
206 | * as next item */ | ||
207 | if(current){ | ||
208 | previous->next = current->next; | ||
209 | }else{ | ||
210 | previous->next = 0; | ||
211 | } | ||
212 | }else{ | ||
213 | shortcuts = shortcuts->next; | ||
214 | } | ||
215 | } | ||
216 | return; | ||
217 | } | ||
218 | |||
219 | enum plugin_status list_sc(void) { | ||
220 | int selected_item = 0; | ||
221 | char selected_dir[MAX_PATH]; | ||
222 | enum sc_list_action_type action = SCLA_NONE; | ||
223 | struct gui_synclist gui_sc; | ||
224 | |||
225 | rb->memset(selected_dir,0,MAX_PATH); | ||
226 | |||
227 | /* Setup the GUI list object, draw it to the screen, | ||
228 | * and then handle the user input to it */ | ||
229 | rb->gui_synclist_init(&gui_sc,&build_sc_list,shortcuts,false,1); | ||
230 | rb->gui_synclist_set_title(&gui_sc,"Shortcuts",NOICON); | ||
231 | rb->gui_synclist_set_nb_items(&gui_sc,total_entries); | ||
232 | rb->gui_synclist_limit_scroll(&gui_sc,false); | ||
233 | rb->gui_synclist_select_item(&gui_sc,0); | ||
234 | |||
235 | /* Draw the prepared widget to the LCD now */ | ||
236 | action = draw_sc_list(gui_sc); | ||
237 | |||
238 | /* which item do we action? */ | ||
239 | selected_item = gselected_item; | ||
240 | |||
241 | /* Find out which option the user selected. | ||
242 | * Handily, the callback function which is used | ||
243 | * to populate the list is equally useful here! */ | ||
244 | build_sc_list(selected_item,(void*)shortcuts,selected_dir); | ||
245 | |||
246 | /* perform the following actions if the user "selected" | ||
247 | * the item in the list (i.e. they want to go there | ||
248 | * in the filebrowser tree */ | ||
249 | switch(action) { | ||
250 | case SCLA_SELECT: | ||
251 | /* Check to see if the directory referenced still exists */ | ||
252 | if(!exists(selected_dir)){ | ||
253 | rb->splash(HZ*2,"File / Directory no longer exists on disk"); | ||
254 | return PLUGIN_ERROR; | ||
255 | } | ||
256 | |||
257 | /* Set the browsers dirfilter to the global setting | ||
258 | * This is required in case the plugin was launched | ||
259 | * from the plugins browser, in which case the | ||
260 | * dirfilter is set to only display .rock files */ | ||
261 | rb->set_dirfilter(rb->global_settings->dirfilter); | ||
262 | |||
263 | /* Change directory to the entry selected by the user */ | ||
264 | rb->set_current_file(selected_dir); | ||
265 | break; | ||
266 | case SCLA_DELETE: | ||
267 | delete_sc(selected_item); | ||
268 | return write_sc_file(0,SCTYPE_NONE); | ||
269 | break; | ||
270 | case SCLA_NONE: | ||
271 | return PLUGIN_OK; | ||
272 | break; | ||
273 | } | ||
274 | return PLUGIN_OK; | ||
275 | } | ||
276 | |||
277 | bool load_sc_file(void){ | ||
278 | int fd = 0; | ||
279 | int amountread = 0; | ||
280 | char sc_content[MAX_PATH]; | ||
281 | sc_entries_t* entry = 0; | ||
282 | |||
283 | fd = rb->open(SHORTCUTS_FILENAME,O_RDONLY); | ||
284 | if(fd<0){ | ||
285 | /* The shortcuts.link file didn't exist on disk | ||
286 | * so create an empty one. | ||
287 | */ | ||
288 | fd = rb->creat(SHORTCUTS_FILENAME); | ||
289 | if(fd<0){ | ||
290 | /* For some reason we couldn't create a new shortcuts.link | ||
291 | * file, so return an error message and exit | ||
292 | */ | ||
293 | rb->splash(HZ*2,"Couldn't create the shortcuts file"); | ||
294 | return false; | ||
295 | } | ||
296 | /* File created, but there's nothing in it | ||
297 | * so just exit */ | ||
298 | rb->close(fd); | ||
299 | return true; | ||
300 | } | ||
301 | |||
302 | /* if we get to here, the file already exists, and has been opened | ||
303 | * successfully, so we can start reading it | ||
304 | */ | ||
305 | while((amountread=rb->read_line(fd,sc_content,MAX_PATH))){ | ||
306 | if(!(entry = (sc_entries_t*)sc_malloc(sizeof(sc_entries_t)))){ | ||
307 | rb->splash(HZ*2,"Couldn't get memory for a new entry"); | ||
308 | rb->close(fd); | ||
309 | return false; | ||
310 | } | ||
311 | if(shortcuts==NULL) { | ||
312 | /* This is the first entry created, so set | ||
313 | * shortcuts to point to it | ||
314 | */ | ||
315 | shortcuts=entry; | ||
316 | } | ||
317 | if(lastentry!=NULL) { | ||
318 | /* This isn't the first item in the list | ||
319 | * so update the previous item in the list | ||
320 | * to point to this new item. | ||
321 | */ | ||
322 | lastentry->next = entry; | ||
323 | } | ||
324 | |||
325 | total_entries++; | ||
326 | rb->snprintf(entry->shortcut,amountread,"%s",sc_content); | ||
327 | entry->sc_len = amountread-1; | ||
328 | |||
329 | /* Make sure the 'next' pointer is null */ | ||
330 | entry->next=0; | ||
331 | |||
332 | /* Now we can make last look at this entry, | ||
333 | * ready for the next one | ||
334 | */ | ||
335 | lastentry = entry; | ||
336 | } | ||
337 | rb->close(fd); | ||
338 | return true; | ||
339 | } | ||
340 | |||
341 | bool load_user_sc_file(char* filename){ | ||
342 | int fd = 0; | ||
343 | int amountread = 0; | ||
344 | char sc_content[MAX_PATH]; | ||
345 | sc_entries_t* entry = 0; | ||
346 | |||
347 | /* user has chosen to open a non-default .link file | ||
348 | * so overwrite current memory contents */ | ||
349 | shortcuts = 0; | ||
350 | lastentry = 0; | ||
351 | total_entries = 0; | ||
352 | |||
353 | fd = rb->open(filename,O_RDONLY); | ||
354 | if(fd<0){ | ||
355 | /* The shortcuts.link file didn't exist on disk | ||
356 | * so create an empty one. | ||
357 | */ | ||
358 | rb->splash(HZ,"Couldn't open %s",filename); | ||
359 | return false; | ||
360 | } | ||
361 | |||
362 | while((amountread=rb->read_line(fd,sc_content,MAX_PATH))){ | ||
363 | if(!(entry = (sc_entries_t*)sc_malloc(sizeof(sc_entries_t)))){ | ||
364 | rb->splash(HZ*2,"Couldn't get memory for a new entry"); | ||
365 | rb->close(fd); | ||
366 | return false; | ||
367 | } | ||
368 | if(shortcuts==NULL) { | ||
369 | /* This is the first entry created, so set | ||
370 | * shortcuts to point to it | ||
371 | */ | ||
372 | shortcuts=entry; | ||
373 | } | ||
374 | if(lastentry!=NULL) { | ||
375 | /* This isn't the first item in the list | ||
376 | * so update the previous item in the list | ||
377 | * to point to this new item. | ||
378 | */ | ||
379 | lastentry->next = entry; | ||
380 | } | ||
381 | |||
382 | total_entries++; | ||
383 | rb->snprintf(entry->shortcut,amountread,"%s",sc_content); | ||
384 | entry->sc_len = amountread-1; | ||
385 | |||
386 | /* Make sure the 'next' pointer is null */ | ||
387 | entry->next=0; | ||
388 | |||
389 | /* Now we can make last look at this entry, | ||
390 | * ready for the next one | ||
391 | */ | ||
392 | lastentry = entry; | ||
393 | } | ||
394 | rb->close(fd); | ||
395 | return true; | ||
396 | } | ||
397 | |||
398 | enum plugin_status write_sc_file(char* directory_name, enum shortcut_type st) { | ||
399 | int fd; | ||
400 | int i; | ||
401 | sc_entries_t *temp_node = shortcuts; | ||
402 | char text_buffer[MAX_PATH]; | ||
403 | |||
404 | if(total_entries>=MAX_SHORTCUTS) { | ||
405 | /* too many entries in the file already | ||
406 | * so don't add this one, and give the | ||
407 | * user an error */ | ||
408 | rb->splash(HZ*2,"Shortcuts file is full"); | ||
409 | return PLUGIN_ERROR; | ||
410 | } | ||
411 | |||
412 | /* ideally, we should just write a new | ||
413 | * entry to the file, but I'm going to | ||
414 | * be lazy, and just re-write the whole | ||
415 | * thing. */ | ||
416 | fd = rb->open(SHORTCUTS_FILENAME,O_RDWR); | ||
417 | if(fd<0){ | ||
418 | rb->splash(HZ*2,"Error writing to shortcuts file"); | ||
419 | return PLUGIN_ERROR; | ||
420 | } | ||
421 | |||
422 | /* truncate the current file, since we're writing it | ||
423 | * all over again */ | ||
424 | rb->ftruncate(fd,0); | ||
425 | |||
426 | /* Check to see that the list is not empty */ | ||
427 | if(temp_node){ | ||
428 | for (i=0;i<MAX_SHORTCUTS && temp_node != NULL;i++){ | ||
429 | rb->snprintf(text_buffer,temp_node->sc_len+2, | ||
430 | "%s\n",temp_node->shortcut); | ||
431 | rb->write(fd,text_buffer,temp_node->sc_len+1); | ||
432 | temp_node = temp_node->next; | ||
433 | } | ||
434 | } | ||
435 | /* Reached the end of the existing entries, so check to | ||
436 | * see if we need to add one more for the new entry | ||
437 | */ | ||
438 | if(st!=SCTYPE_NONE){ | ||
439 | if(st==SCTYPE_FILE) { | ||
440 | rb->snprintf(text_buffer,rb->strlen(directory_name)+2, /*+2 is \n and 0x00 */ | ||
441 | "%s\n",directory_name); | ||
442 | rb->write(fd,text_buffer,rb->strlen(directory_name)+1); | ||
443 | } else if(st==SCTYPE_DIR){ | ||
444 | rb->snprintf(text_buffer,rb->strlen(directory_name)+3, /*+3 is /, \n and 0x00 */ | ||
445 | "%s/\n",directory_name); | ||
446 | rb->write(fd,text_buffer,rb->strlen(directory_name)+2); | ||
447 | } | ||
448 | } | ||
449 | rb->close(fd); | ||
450 | |||
451 | return PLUGIN_OK; | ||
452 | } | ||
453 | |||
454 | enum plugin_status plugin_start(struct plugin_api* api, void* parameter) { | ||
455 | rb = api; | ||
456 | bool found = false; | ||
457 | |||
458 | DIR* dir; | ||
459 | struct dirent* entry; | ||
460 | |||
461 | /* Initialise the plugin buffer */ | ||
462 | sc_alloc_init(); | ||
463 | |||
464 | if(!sc_init()) | ||
465 | return PLUGIN_ERROR; | ||
466 | |||
467 | /* Were we passed a parameter at startup? */ | ||
468 | if(parameter) { | ||
469 | /* determine if it's a file or a directory */ | ||
470 | char* ptr = rb->strrchr((char*)parameter, '/') + 1; | ||
471 | int dirlen = (ptr - (char*)parameter); | ||
472 | rb->strncpy(str_dirname, (char*)parameter, dirlen); | ||
473 | str_dirname[dirlen] = 0; | ||
474 | |||
475 | dir = rb->opendir(str_dirname); | ||
476 | if (dir) { | ||
477 | while(0 != (entry = rb->readdir(dir))) { | ||
478 | if(!rb->strcmp(entry->d_name, parameter+dirlen)) { | ||
479 | its_a_dir = entry->attribute & ATTR_DIRECTORY ? | ||
480 | true : false; | ||
481 | found = true; | ||
482 | break; | ||
483 | } | ||
484 | } | ||
485 | rb->closedir(dir); | ||
486 | } | ||
487 | /* now we know if it's a file or a directory | ||
488 | * (or something went wrong) */ | ||
489 | |||
490 | if(!found) { | ||
491 | /* Something's gone properly pear shaped - | ||
492 | * we couldn't even find the entry */ | ||
493 | rb->splash(HZ*2,"File / Directory not found : %s", | ||
494 | (char*)parameter); | ||
495 | return PLUGIN_ERROR; | ||
496 | } | ||
497 | |||
498 | if(!its_a_dir) { | ||
499 | /* Don't add the shortcuts.link file to itself */ | ||
500 | if(rb->strcmp((char*)parameter,SHORTCUTS_FILENAME)==0){ | ||
501 | return list_sc(); | ||
502 | } | ||
503 | /* this section handles user created .link files */ | ||
504 | if(rb->strcasestr((char*)parameter,".link")){ | ||
505 | if(!load_user_sc_file((char*)parameter)){ | ||
506 | return PLUGIN_ERROR; | ||
507 | } | ||
508 | user_file = true; | ||
509 | if(total_entries==1){ | ||
510 | /* if there's only one entry in the user .link file, | ||
511 | * go straight to it without displaying the menu */ | ||
512 | char selected_dir[MAX_PATH]; | ||
513 | /* go to entry immediately */ | ||
514 | build_sc_list(0,(void*)shortcuts,selected_dir); | ||
515 | |||
516 | /* Check to see if the directory referenced still exists */ | ||
517 | if(!exists(selected_dir)){ | ||
518 | rb->splash(HZ*2,"File / Directory no longer exists on disk"); | ||
519 | return PLUGIN_ERROR; | ||
520 | } | ||
521 | |||
522 | /* Set the browsers dirfilter to the global setting */ | ||
523 | rb->set_dirfilter(rb->global_settings->dirfilter); | ||
524 | |||
525 | /* Change directory to the entry selected by the user */ | ||
526 | rb->set_current_file(selected_dir); | ||
527 | return PLUGIN_OK; | ||
528 | }else{ | ||
529 | /* This user created link file has multiple entries in it | ||
530 | * so display a menu to choose between them */ | ||
531 | return list_sc(); | ||
532 | } | ||
533 | } else { | ||
534 | /* This isn't a .link file so add it to the shortcuts.link | ||
535 | * file as a new entry */ | ||
536 | return write_sc_file((char*)parameter,SCTYPE_FILE); | ||
537 | } | ||
538 | }else{ | ||
539 | /* This is a directory and should be added to | ||
540 | * the shortcuts.link file */ | ||
541 | return write_sc_file((char*)parameter,SCTYPE_DIR); | ||
542 | } | ||
543 | } | ||
544 | else { /* We weren't passed a parameter, so open the | ||
545 | * shortcuts file directly */ | ||
546 | return list_sc(); | ||
547 | } | ||
548 | return PLUGIN_OK; | ||
549 | } | ||
550 | |||