/me tired, forgot to add the actual plugin
[kugel-rb.git] / apps / plugins / shortcuts.c
blobaf92bf4634a71a0b96fac9644dacc414f8704a66
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2007 Bryan Childs
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.
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
18 ****************************************************************************/
20 #include "plugin.h"
22 PLUGIN_HEADER
24 static struct plugin_api* rb;
26 #define SHORTCUTS_FILENAME "/shortcuts.link"
27 #define MAX_SHORTCUTS 50
29 MEM_FUNCTION_WRAPPERS(rb);
31 typedef struct sc_file_s
33 int readsize;
34 char* filebuf;
35 } sc_file_t;
37 typedef struct sc_entries_s
39 char shortcut[MAX_PATH+1];
40 int sc_len;
41 struct sc_entries_s* next;
42 } sc_entries_t;
44 enum shortcut_type {
45 SCTYPE_NONE,
46 SCTYPE_FILE,
47 SCTYPE_DIR,
50 enum sc_list_action_type {
51 SCLA_NONE,
52 SCLA_SELECT,
53 SCLA_DELETE,
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);
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;
81 void sc_alloc_init(void){
82 mem_ptr=0;
84 mallocbuf = rb->plugin_get_buffer(&bufleft);
85 bufsize = (long)bufleft;
87 rb->memset(mallocbuf,0,bufsize);
89 return;
92 void* sc_malloc(unsigned int size) {
93 void* x;
95 if(mem_ptr + (long)size > bufsize) {
96 rb->splash(HZ*2,"OUT OF MEMORY");
97 return NULL;
100 x=&mallocbuf[mem_ptr];
101 mem_ptr+=(size+3)&~3; /* Keep memory 32-bit aligned */
103 return x;
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;
114 fd = rb->open(str_dirname,O_RDONLY);
115 if (!fd) {
116 return false;
118 rb->close(fd);
119 return true;
122 bool sc_init(void) {
123 return load_sc_file();
126 enum sc_list_action_type draw_sc_list(struct gui_synclist gui_sc) {
127 int button;
129 rb->gui_synclist_draw(&gui_sc);
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;
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;
156 break;
157 case ACTION_STD_CANCEL:
158 return SCLA_NONE;
159 break;
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];
169 for (i=0;i<selected_item && temp_node != NULL;i++){
170 temp_node = temp_node->next;
172 if (temp_node == NULL){
173 return NULL;
175 rb->snprintf(text_buffer, MAX_PATH, "%s", temp_node->shortcut);
177 rb->strcpy(buffer, text_buffer);
178 return buffer;
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;
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;
201 current=current->next;
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;
212 }else{
213 shortcuts = shortcuts->next;
216 return;
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;
225 rb->memset(selected_dir,0,MAX_PATH);
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);
235 /* Draw the prepared widget to the LCD now */
236 action = draw_sc_list(gui_sc);
238 /* which item do we action? */
239 selected_item = gselected_item;
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);
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;
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);
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;
274 return PLUGIN_OK;
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;
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.
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
293 rb->splash(HZ*2,"Couldn't create the shortcuts file");
294 return false;
296 /* File created, but there's nothing in it
297 * so just exit */
298 rb->close(fd);
299 return true;
302 /* if we get to here, the file already exists, and has been opened
303 * successfully, so we can start reading it
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;
311 if(shortcuts==NULL) {
312 /* This is the first entry created, so set
313 * shortcuts to point to it
315 shortcuts=entry;
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.
322 lastentry->next = entry;
325 total_entries++;
326 rb->snprintf(entry->shortcut,amountread,"%s",sc_content);
327 entry->sc_len = amountread-1;
329 /* Make sure the 'next' pointer is null */
330 entry->next=0;
332 /* Now we can make last look at this entry,
333 * ready for the next one
335 lastentry = entry;
337 rb->close(fd);
338 return true;
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;
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;
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.
358 rb->splash(HZ,"Couldn't open %s",filename);
359 return false;
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;
368 if(shortcuts==NULL) {
369 /* This is the first entry created, so set
370 * shortcuts to point to it
372 shortcuts=entry;
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.
379 lastentry->next = entry;
382 total_entries++;
383 rb->snprintf(entry->shortcut,amountread,"%s",sc_content);
384 entry->sc_len = amountread-1;
386 /* Make sure the 'next' pointer is null */
387 entry->next=0;
389 /* Now we can make last look at this entry,
390 * ready for the next one
392 lastentry = entry;
394 rb->close(fd);
395 return true;
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];
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;
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;
422 /* truncate the current file, since we're writing it
423 * all over again */
424 rb->ftruncate(fd,0);
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;
435 /* Reached the end of the existing entries, so check to
436 * see if we need to add one more for the new entry
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);
449 rb->close(fd);
451 return PLUGIN_OK;
454 enum plugin_status plugin_start(struct plugin_api* api, void* parameter) {
455 rb = api;
456 bool found = false;
458 DIR* dir;
459 struct dirent* entry;
461 /* Initialise the plugin buffer */
462 sc_alloc_init();
464 if(!sc_init())
465 return PLUGIN_ERROR;
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;
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;
485 rb->closedir(dir);
487 /* now we know if it's a file or a directory
488 * (or something went wrong) */
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;
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();
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;
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);
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;
522 /* Set the browsers dirfilter to the global setting */
523 rb->set_dirfilter(rb->global_settings->dirfilter);
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();
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);
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);
544 else { /* We weren't passed a parameter, so open the
545 * shortcuts file directly */
546 return list_sc();
548 return PLUGIN_OK;