1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
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 ****************************************************************************/
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
37 typedef struct sc_entries_s
39 char shortcut
[MAX_PATH
+1];
41 struct sc_entries_s
* next
;
50 enum sc_list_action_type
{
56 void sc_alloc_init(void);
57 void* sc_malloc(unsigned int size
);
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
];
72 unsigned char* mallocbuf
;
73 bool its_a_dir
= false;
74 bool user_file
= false;
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){
84 mallocbuf
= rb
->plugin_get_buffer(&bufleft
);
85 bufsize
= (long)bufleft
;
87 rb
->memset(mallocbuf
,0,bufsize
);
92 void* sc_malloc(unsigned int size
) {
95 if(mem_ptr
+ (long)size
> bufsize
) {
96 rb
->splash(HZ
*2,"OUT OF MEMORY");
100 x
=&mallocbuf
[mem_ptr
];
101 mem_ptr
+=(size
+3)&~3; /* Keep memory 32-bit aligned */
106 bool exists(char* filename
){
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
);
123 return load_sc_file();
126 enum sc_list_action_type
draw_sc_list(struct gui_synclist gui_sc
) {
129 rb
->gui_synclist_draw(&gui_sc
);
132 /* draw the statusbar, should be done often */
133 rb
->gui_syncstatusbar_draw(rb
->statusbars
, true);
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 */
143 switch (button
) { /* process the user input */
145 gselected_item
= rb
->gui_synclist_get_sel_pos(&gui_sc
);
148 case ACTION_STD_MENU
:
150 gselected_item
= rb
->gui_synclist_get_sel_pos(&gui_sc
);
151 rb
->splash(HZ
,"Deleting item");
157 case ACTION_STD_CANCEL
:
164 char* build_sc_list(int selected_item
, void* data
, char* buffer
) {
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
){
175 rb
->snprintf(text_buffer
, MAX_PATH
, "%s", temp_node
->shortcut
);
177 rb
->strcpy(buffer
, text_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.*/
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
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
){
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
208 previous
->next
= current
->next
;
213 shortcuts
= shortcuts
->next
;
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 */
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");
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
);
267 delete_sc(selected_item
);
268 return write_sc_file(0,SCTYPE_NONE
);
277 bool load_sc_file(void){
280 char sc_content
[MAX_PATH
];
281 sc_entries_t
* entry
= 0;
283 fd
= rb
->open(SHORTCUTS_FILENAME
,O_RDONLY
);
285 /* The shortcuts.link file didn't exist on disk
286 * so create an empty one.
288 fd
= rb
->creat(SHORTCUTS_FILENAME
);
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");
296 /* File created, but there's nothing in it
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");
311 if(shortcuts
==NULL
) {
312 /* This is the first entry created, so set
313 * shortcuts to point to it
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
;
326 rb
->snprintf(entry
->shortcut
,amountread
,"%s",sc_content
);
327 entry
->sc_len
= amountread
-1;
329 /* Make sure the 'next' pointer is null */
332 /* Now we can make last look at this entry,
333 * ready for the next one
341 bool load_user_sc_file(char* filename
){
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 */
353 fd
= rb
->open(filename
,O_RDONLY
);
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
);
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");
368 if(shortcuts
==NULL
) {
369 /* This is the first entry created, so set
370 * shortcuts to point to it
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
;
383 rb
->snprintf(entry
->shortcut
,amountread
,"%s",sc_content
);
384 entry
->sc_len
= amountread
-1;
386 /* Make sure the 'next' pointer is null */
389 /* Now we can make last look at this entry,
390 * ready for the next one
398 enum plugin_status
write_sc_file(char* directory_name
, enum shortcut_type st
) {
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
408 rb
->splash(HZ
*2,"Shortcuts file is full");
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
416 fd
= rb
->open(SHORTCUTS_FILENAME
,O_RDWR
);
418 rb
->splash(HZ
*2,"Error writing to shortcuts file");
422 /* truncate the current file, since we're writing it
426 /* Check to see that the list is not empty */
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
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);
454 enum plugin_status
plugin_start(struct plugin_api
* api
, void* parameter
) {
459 struct dirent
* entry
;
461 /* Initialise the plugin buffer */
467 /* Were we passed a parameter at startup? */
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
);
477 while(0 != (entry
= rb
->readdir(dir
))) {
478 if(!rb
->strcmp(entry
->d_name
, parameter
+dirlen
)) {
479 its_a_dir
= entry
->attribute
& ATTR_DIRECTORY
?
487 /* now we know if it's a file or a directory
488 * (or something went wrong) */
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",
499 /* Don't add the shortcuts.link file to itself */
500 if(rb
->strcmp((char*)parameter
,SHORTCUTS_FILENAME
)==0){
503 /* this section handles user created .link files */
504 if(rb
->strcasestr((char*)parameter
,".link")){
505 if(!load_user_sc_file((char*)parameter
)){
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");
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
);
529 /* This user created link file has multiple entries in it
530 * so display a menu to choose between them */
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
);
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 */