diff options
-rw-r--r-- | apps/onplay.c | 2 | ||||
-rw-r--r-- | apps/plugins/CATEGORIES | 3 | ||||
-rw-r--r-- | apps/plugins/SOURCES | 1 | ||||
-rw-r--r-- | apps/plugins/SUBDIRS | 1 | ||||
-rw-r--r-- | apps/plugins/shortcuts.c | 550 | ||||
-rw-r--r-- | apps/plugins/shortcuts/Makefile | 90 | ||||
-rw-r--r-- | apps/plugins/shortcuts/shortcuts.h | 90 | ||||
-rw-r--r-- | apps/plugins/shortcuts/shortcuts_append.c | 109 | ||||
-rw-r--r-- | apps/plugins/shortcuts/shortcuts_common.c | 394 | ||||
-rw-r--r-- | apps/plugins/shortcuts/shortcuts_view.c | 238 | ||||
-rw-r--r-- | apps/plugins/viewers.config | 3 |
11 files changed, 927 insertions, 554 deletions
diff --git a/apps/onplay.c b/apps/onplay.c index ba21572c3d..0e5169a007 100644 --- a/apps/onplay.c +++ b/apps/onplay.c | |||
@@ -1082,7 +1082,7 @@ MENUITEM_FUNCTION(set_recdir_item, 0, ID2P(LANG_SET_AS_REC_DIR), | |||
1082 | #endif | 1082 | #endif |
1083 | static bool add_to_faves(void) | 1083 | static bool add_to_faves(void) |
1084 | { | 1084 | { |
1085 | if(PLUGIN_USB_CONNECTED == filetype_load_plugin("shortcuts", | 1085 | if(PLUGIN_USB_CONNECTED == filetype_load_plugin("shortcuts_append", |
1086 | selected_file)) | 1086 | selected_file)) |
1087 | onplay_result = ONPLAY_RELOAD_DIR; | 1087 | onplay_result = ONPLAY_RELOAD_DIR; |
1088 | return false; | 1088 | return false; |
diff --git a/apps/plugins/CATEGORIES b/apps/plugins/CATEGORIES index a9997e60d4..a821fa0d91 100644 --- a/apps/plugins/CATEGORIES +++ b/apps/plugins/CATEGORIES | |||
@@ -58,7 +58,8 @@ rocklife,games | |||
58 | rockpaint,apps | 58 | rockpaint,apps |
59 | search,viewers | 59 | search,viewers |
60 | searchengine,viewers | 60 | searchengine,viewers |
61 | shortcuts,viewers | 61 | shortcuts_view,viewers |
62 | shortcuts_append,viewers | ||
62 | sliding_puzzle,games | 63 | sliding_puzzle,games |
63 | snake2,games | 64 | snake2,games |
64 | snake,games | 65 | snake,games |
diff --git a/apps/plugins/SOURCES b/apps/plugins/SOURCES index 23d3e6becc..31d94d5811 100644 --- a/apps/plugins/SOURCES +++ b/apps/plugins/SOURCES | |||
@@ -13,7 +13,6 @@ random_folder_advance_config.c | |||
13 | rockblox.c | 13 | rockblox.c |
14 | rockbox_flash.c | 14 | rockbox_flash.c |
15 | search.c | 15 | search.c |
16 | shortcuts.c | ||
17 | snow.c | 16 | snow.c |
18 | sort.c | 17 | sort.c |
19 | stats.c | 18 | stats.c |
diff --git a/apps/plugins/SUBDIRS b/apps/plugins/SUBDIRS index 3f0fc9051b..11643a5b2f 100644 --- a/apps/plugins/SUBDIRS +++ b/apps/plugins/SUBDIRS | |||
@@ -1,6 +1,7 @@ | |||
1 | #ifndef IRIVER_IFP7XX_SERIES | 1 | #ifndef IRIVER_IFP7XX_SERIES |
2 | 2 | ||
3 | /* For all targets */ | 3 | /* For all targets */ |
4 | shortcuts | ||
4 | 5 | ||
5 | /* For various targets... */ | 6 | /* For various targets... */ |
6 | 7 | ||
diff --git a/apps/plugins/shortcuts.c b/apps/plugins/shortcuts.c deleted file mode 100644 index af92bf4634..0000000000 --- a/apps/plugins/shortcuts.c +++ /dev/null | |||
@@ -1,550 +0,0 @@ | |||
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 | |||
diff --git a/apps/plugins/shortcuts/Makefile b/apps/plugins/shortcuts/Makefile new file mode 100644 index 0000000000..93089cc140 --- /dev/null +++ b/apps/plugins/shortcuts/Makefile | |||
@@ -0,0 +1,90 @@ | |||
1 | # __________ __ ___. | ||
2 | # Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
3 | # Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
4 | # Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
5 | # Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
6 | # \/ \/ \/ \/ \/ | ||
7 | # $$Id: $$ | ||
8 | # | ||
9 | |||
10 | INCLUDES = -I$(APPSDIR) -I.. -I. $(TARGET_INC) -I$(FIRMDIR)/include -I$(FIRMDIR)/export \ | ||
11 | -I$(FIRMDIR)/common -I$(FIRMDIR)/drivers -I$(OUTDIR) -I$(BUILDDIR) \ | ||
12 | -I$(BUILDDIR)/pluginbitmaps | ||
13 | CFLAGS = $(INCLUDES) $(GCCOPTS) $(TARGET) $(EXTRA_DEFINES) \ | ||
14 | -DTARGET_ID=$(TARGET_ID) -DMEM=${MEMORYSIZE} -DPLUGIN | ||
15 | |||
16 | ifdef APPEXTRA | ||
17 | INCLUDES += $(patsubst %,-I$(APPSDIR)/%,$(subst :, ,$(APPEXTRA))) | ||
18 | endif | ||
19 | |||
20 | LINKFILE := $(OBJDIR)/link.lds | ||
21 | DEPFILE = $(OBJDIR)/dep-shortcuts | ||
22 | |||
23 | SOURCES := shortcuts_common.c shortcuts_view.c shortcuts_append.c | ||
24 | VIEW_OBJS := $(OBJDIR)/shortcuts_common.o $(OBJDIR)/shortcuts_view.o | ||
25 | APPEND_OBJS := $(OBJDIR)/shortcuts_common.o $(OBJDIR)/shortcuts_append.o | ||
26 | DIRS = . | ||
27 | |||
28 | ifndef SIMVER | ||
29 | LDS := ../plugin.lds | ||
30 | endif | ||
31 | |||
32 | OUTPUT = $(OUTDIR)/shortcuts_view.rock $(OUTDIR)/shortcuts_append.rock | ||
33 | |||
34 | all: $(OUTPUT) | ||
35 | |||
36 | ifndef SIMVER | ||
37 | $(OBJDIR)/shortcuts_view.elf: $(VIEW_OBJS) $(LINKFILE) $(BITMAPLIBS) | ||
38 | $(call PRINTS,LD $(@F))$(CC) $(GCCOPTS) -O -nostdlib -o $@ $(VIEW_OBJS) -L$(BUILDDIR) -lplugin -lgcc \ | ||
39 | $(LINKBITMAPS) -T$(LINKFILE) -Wl,-Map,$(OBJDIR)/shortcuts_view.map | ||
40 | |||
41 | $(OUTDIR)/shortcuts_view.rock: $(OBJDIR)/shortcuts_view.elf | ||
42 | $(call PRINTS,OBJCOPY $(@F))$(OC) -O binary $< $@ | ||
43 | |||
44 | $(OBJDIR)/shortcuts_append.elf: $(APPEND_OBJS) $(LINKFILE) $(BITMAPLIBS) | ||
45 | $(call PRINTS,LD $(@F))$(CC) $(GCCOPTS) -O -nostdlib -o $@ $(APPEND_OBJS) -L$(BUILDDIR) -lplugin -lgcc \ | ||
46 | $(LINKBITMAPS) -T$(LINKFILE) -Wl,-Map,$(OBJDIR)/shortcuts_append.map | ||
47 | |||
48 | $(OUTDIR)/shortcuts_append.rock: $(OBJDIR)/shortcuts_append.elf | ||
49 | $(call PRINTS,OBJCOPY $(@F))$(OC) -O binary $< $@ | ||
50 | else | ||
51 | |||
52 | ################################################### | ||
53 | # This is the SDL simulator version | ||
54 | |||
55 | $(OUTDIR)/shortcuts_view.rock: $(VIEW_OBJS) | ||
56 | $(call PRINTS,LD $(@F))$(CC) $(CFLAGS) $(SHARED_FLAG) $(VIEW_OBJS) -L$(BUILDDIR) -lplugin $(LINKBITMAPS) -o $@ | ||
57 | ifeq ($(findstring CYGWIN,$(UNAME)),CYGWIN) | ||
58 | # 'x' must be kept or you'll have "Win32 error 5" | ||
59 | # $ fgrep 5 /usr/include/w32api/winerror.h | head -1 | ||
60 | # #define ERROR_ACCESS_DENIED 5L | ||
61 | else | ||
62 | @chmod -x $@ | ||
63 | endif | ||
64 | |||
65 | $(OUTDIR)/shortcuts_append.rock: $(APPEND_OBJS) | ||
66 | $(call PRINTS,LD $(@F))$(CC) $(CFLAGS) $(SHARED_FLAG) $(APPEND_OBJS) -L$(BUILDDIR) -lplugin $(LINKBITMAPS) -o $@ | ||
67 | ifeq ($(findstring CYGWIN,$(UNAME)),CYGWIN) | ||
68 | # 'x' must be kept or you'll have "Win32 error 5" | ||
69 | # $ fgrep 5 /usr/include/w32api/winerror.h | head -1 | ||
70 | # #define ERROR_ACCESS_DENIED 5L | ||
71 | else | ||
72 | @chmod -x $@ | ||
73 | endif | ||
74 | |||
75 | endif # end of simulator section | ||
76 | |||
77 | |||
78 | include $(TOOLSDIR)/make.inc | ||
79 | |||
80 | # MEMORYSIZE should be passed on to this makefile with the chosen memory size | ||
81 | # given in number of MB | ||
82 | $(LINKFILE): $(LDS) | ||
83 | $(call PRINTS,build $(@F))cat $< | $(CC) -DMEMORYSIZE=$(MEMORYSIZE) $(INCLUDES) $(TARGET) \ | ||
84 | $(DEFINES) -E -P - >$@ | ||
85 | |||
86 | clean: | ||
87 | $(call PRINTS,cleaning shortcuts)rm -rf $(OBJDIR)/shortcuts | ||
88 | $(SILENT)rm -f $(OBJDIR)/shortcuts* $(DEPFILE) | ||
89 | |||
90 | -include $(DEPFILE) | ||
diff --git a/apps/plugins/shortcuts/shortcuts.h b/apps/plugins/shortcuts/shortcuts.h new file mode 100644 index 0000000000..6ccb320a36 --- /dev/null +++ b/apps/plugins/shortcuts/shortcuts.h | |||
@@ -0,0 +1,90 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2007 Bryan Childs | ||
11 | * Copyright (c) 2007 Alexander Levin | ||
12 | * | ||
13 | * All files in this archive are subject to the GNU General Public License. | ||
14 | * See the file COPYING in the source tree root for full license agreement. | ||
15 | * | ||
16 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
17 | * KIND, either express or implied. | ||
18 | * | ||
19 | ****************************************************************************/ | ||
20 | |||
21 | #ifndef _SHORTCUTS_H | ||
22 | #define _SHORTCUTS_H | ||
23 | |||
24 | #include "plugin.h" | ||
25 | |||
26 | #define PATH_SEPARATOR "/" | ||
27 | #define PATH_SEPARATOR_LEN 1 /* strlen(PATH_SEPARATOR) */ | ||
28 | |||
29 | #if defined(DEBUG) || defined(SIMULATOR) | ||
30 | #define SC_DEBUG | ||
31 | #endif | ||
32 | |||
33 | #define SHORTCUTS_FILENAME "/shortcuts.link" | ||
34 | |||
35 | extern struct plugin_api* rb; | ||
36 | |||
37 | typedef struct sc_entry_s | ||
38 | { | ||
39 | char path[MAX_PATH+1]; | ||
40 | char disp[MAX_PATH+1]; | ||
41 | bool explicit_disp; | ||
42 | } sc_entry_t; | ||
43 | |||
44 | typedef struct sc_file_s | ||
45 | { | ||
46 | sc_entry_t *entries; | ||
47 | int max_entries; /* Max allowed number of entries */ | ||
48 | int entry_cnt; /* Current number of entries */ | ||
49 | int show_last_segments; | ||
50 | } sc_file_t; | ||
51 | |||
52 | |||
53 | extern void *memory_buf; | ||
54 | extern long memory_bufsize; | ||
55 | |||
56 | |||
57 | extern sc_file_t sc_file; | ||
58 | |||
59 | |||
60 | /* Allocates a chunk of memory (as much as possible) */ | ||
61 | void allocate_memory(void **buf, size_t *bufsize); | ||
62 | |||
63 | /* Initializes the file */ | ||
64 | void init_sc_file(sc_file_t *file, void *buf, size_t bufsize); | ||
65 | |||
66 | /* Loads shortcuts from the file. Returns true iff succeeded */ | ||
67 | bool load_sc_file(sc_file_t *file, char* filename, bool must_exist, | ||
68 | void *entry_buf, size_t entry_bufsize); | ||
69 | |||
70 | /* Writes contents to the file. File is overwritten. */ | ||
71 | bool dump_sc_file(sc_file_t *file, char *filename); | ||
72 | |||
73 | /* Appends the entry to the file. Entry is copied. Returns true iff succeded. */ | ||
74 | bool append_entry(sc_file_t *file, sc_entry_t *entry); | ||
75 | |||
76 | /* Removes the specified entry (0-based index). Returns true iff succeded. */ | ||
77 | bool remove_entry(sc_file_t *file, int entry_idx); | ||
78 | |||
79 | /* Checks whether the index is a valid one for the file. */ | ||
80 | bool is_valid_index(sc_file_t *file, int entry_idx); | ||
81 | |||
82 | bool file_exists(char *filename); /* Does the specified file exist? */ | ||
83 | bool dir_exists(char *path); /* Does the specified dir exist? */ | ||
84 | |||
85 | |||
86 | #ifdef SC_DEBUG | ||
87 | void print_file(sc_file_t *file); | ||
88 | #endif | ||
89 | |||
90 | #endif | ||
diff --git a/apps/plugins/shortcuts/shortcuts_append.c b/apps/plugins/shortcuts/shortcuts_append.c new file mode 100644 index 0000000000..bc287234dd --- /dev/null +++ b/apps/plugins/shortcuts/shortcuts_append.c | |||
@@ -0,0 +1,109 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2007 Bryan Childs | ||
11 | * Copyright (c) 2007 Alexander Levin | ||
12 | * | ||
13 | * All files in this archive are subject to the GNU General Public License. | ||
14 | * See the file COPYING in the source tree root for full license agreement. | ||
15 | * | ||
16 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
17 | * KIND, either express or implied. | ||
18 | * | ||
19 | ****************************************************************************/ | ||
20 | |||
21 | #include "shortcuts.h" | ||
22 | |||
23 | PLUGIN_HEADER | ||
24 | |||
25 | |||
26 | bool append_entry_to_file(sc_file_t *file, char *path, bool is_dir) | ||
27 | { | ||
28 | sc_entry_t entry; | ||
29 | |||
30 | unsigned int required_len = rb->strlen(path); | ||
31 | if (is_dir) { | ||
32 | required_len += PATH_SEPARATOR_LEN; /* Add 1 for the trailing / */ | ||
33 | } | ||
34 | if (required_len >= sizeof(entry.path)) { | ||
35 | /* no attempt to print it: it will also be so too long for the splash */ | ||
36 | rb->splash(HZ*2, "Can't append shortcut, it's too long"); | ||
37 | return false; | ||
38 | } | ||
39 | entry.explicit_disp = false; | ||
40 | rb->strcpy(entry.path, path); | ||
41 | if (is_dir) { | ||
42 | rb->strcat(entry.path, PATH_SEPARATOR); | ||
43 | } | ||
44 | if (!append_entry(file, &entry)) { | ||
45 | rb->splash(HZ*2, "Too many entries!"); | ||
46 | return false; | ||
47 | } | ||
48 | return true; | ||
49 | } | ||
50 | |||
51 | |||
52 | enum plugin_status plugin_start(struct plugin_api* api, void* void_parameter) | ||
53 | { | ||
54 | rb = api; | ||
55 | bool found; | ||
56 | bool its_a_dir; | ||
57 | |||
58 | /* This is a viewer, so a parameter must have been specified */ | ||
59 | if (void_parameter == NULL) { | ||
60 | rb->splash(HZ*2, "No parameter specified!"); | ||
61 | return PLUGIN_ERROR; | ||
62 | } | ||
63 | char *parameter = (char*)void_parameter; | ||
64 | DEBUGF("Trying to append '%s' to the default link file '%s'...\n", | ||
65 | parameter, SHORTCUTS_FILENAME); | ||
66 | |||
67 | allocate_memory(&memory_buf, &memory_bufsize); | ||
68 | |||
69 | /* Determine if it's a file or a directory. First check | ||
70 | * if it's a dir and then file (not vice versa) since | ||
71 | * open() can also open a dir */ | ||
72 | found = true; | ||
73 | if (dir_exists(parameter)) { | ||
74 | its_a_dir = true; | ||
75 | } else if (file_exists(parameter)) { | ||
76 | its_a_dir = false; | ||
77 | } else { | ||
78 | found = false; | ||
79 | } | ||
80 | /* now we know if it's a file or a directory | ||
81 | * (or something went wrong) */ | ||
82 | |||
83 | if (!found) { | ||
84 | /* Something's gone properly pear shaped - | ||
85 | * we couldn't even find the entry */ | ||
86 | rb->splash(HZ*2, "File/Dir not found: %s", parameter); | ||
87 | return PLUGIN_ERROR; | ||
88 | } | ||
89 | |||
90 | DEBUGF("'%s' is a %s\n", parameter, (its_a_dir ? "dir" : "file")); | ||
91 | |||
92 | if (!load_sc_file(&sc_file, SHORTCUTS_FILENAME, false, | ||
93 | memory_buf, memory_bufsize)) { | ||
94 | DEBUGF("Couldn't load '%s'\n", SHORTCUTS_FILENAME); | ||
95 | return PLUGIN_ERROR; | ||
96 | } | ||
97 | |||
98 | if (!append_entry_to_file(&sc_file, parameter, its_a_dir)) { | ||
99 | DEBUGF("Couldn't append entry (too many entries?)\n"); | ||
100 | return PLUGIN_ERROR; | ||
101 | } | ||
102 | |||
103 | if (!dump_sc_file(&sc_file, SHORTCUTS_FILENAME)) { | ||
104 | DEBUGF("Couldn't write shortcuts to '%s'\n", SHORTCUTS_FILENAME); | ||
105 | return PLUGIN_ERROR; | ||
106 | } | ||
107 | |||
108 | return PLUGIN_OK; | ||
109 | } | ||
diff --git a/apps/plugins/shortcuts/shortcuts_common.c b/apps/plugins/shortcuts/shortcuts_common.c new file mode 100644 index 0000000000..4a097f0a0d --- /dev/null +++ b/apps/plugins/shortcuts/shortcuts_common.c | |||
@@ -0,0 +1,394 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2007 Bryan Childs | ||
11 | * Copyright (c) 2007 Alexander Levin | ||
12 | * | ||
13 | * All files in this archive are subject to the GNU General Public License. | ||
14 | * See the file COPYING in the source tree root for full license agreement. | ||
15 | * | ||
16 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
17 | * KIND, either express or implied. | ||
18 | * | ||
19 | ****************************************************************************/ | ||
20 | |||
21 | #include "shortcuts.h" | ||
22 | MEM_FUNCTION_WRAPPERS(rb); | ||
23 | |||
24 | #define SHORTCUTS_FILENAME "/shortcuts.link" | ||
25 | |||
26 | #define PATH_DISP_SEPARATOR "\t" | ||
27 | #define PATH_DISP_SEPARATOR_LEN 1 /* strlen(PATH_DISP_SEPARATOR) */ | ||
28 | #define CONTROL_PREFIX "#" | ||
29 | #define CONTROL_PREFIX_LEN 1 /* strlen(CONTROL_PREFIX) */ | ||
30 | #define NAME_VALUE_SEPARATOR "=" | ||
31 | #define NAME_VALUE_SEPARATOR_LEN 1 /* strlen(NAME_VALUE_SEPARATOR) */ | ||
32 | |||
33 | #define INSTR_DISPLAY_LAST_SEGMENTS "Display last path segments" | ||
34 | |||
35 | /* Memory (will be used for entries) */ | ||
36 | void *memory_buf; | ||
37 | long memory_bufsize; /* Size of memory_buf in bytes */ | ||
38 | |||
39 | |||
40 | /* The file we're processing */ | ||
41 | sc_file_t sc_file; | ||
42 | |||
43 | bool parse_entry_content(char *line, sc_entry_t *entry, int last_segm); | ||
44 | char *last_segments(char *path, int nsegm); | ||
45 | bool is_control(char *line, sc_file_t *file); | ||
46 | bool starts_with(char *string, char *prefix); | ||
47 | bool parse_name_value(char *line, char *name, int namesize, | ||
48 | char *value, int valuesize); | ||
49 | void write_entry_to_file(int fd, sc_entry_t *entry); | ||
50 | void write_int_instruction_to_file(int fd, char *instr, int value); | ||
51 | |||
52 | |||
53 | void allocate_memory(void **buf, size_t *bufsize) | ||
54 | { | ||
55 | *buf = rb->plugin_get_buffer(bufsize); | ||
56 | DEBUGF("Got %ld bytes of memory\n", *bufsize); | ||
57 | } | ||
58 | |||
59 | |||
60 | void init_sc_file(sc_file_t *file, void *buf, size_t bufsize) | ||
61 | { | ||
62 | file->entries = (sc_entry_t*)buf; | ||
63 | file->max_entries = bufsize / sizeof(sc_entry_t); | ||
64 | DEBUGF("Buffer capacity: %d entries\n", file->max_entries); | ||
65 | file->entry_cnt = 0; | ||
66 | file->show_last_segments = -1; | ||
67 | } | ||
68 | |||
69 | |||
70 | bool load_sc_file(sc_file_t *file, char *filename, bool must_exist, | ||
71 | void *entry_buf, size_t entry_bufsize) | ||
72 | { | ||
73 | int fd = -1; | ||
74 | bool ret_val = false; /* Assume bad case */ | ||
75 | int amountread = 0; | ||
76 | char sc_content[2*MAX_PATH]; | ||
77 | sc_entry_t entry; | ||
78 | |||
79 | /* We start to load a new file -> prepare it */ | ||
80 | init_sc_file(&sc_file, entry_buf, entry_bufsize); | ||
81 | |||
82 | fd = rb->open(filename, O_RDONLY); | ||
83 | if (fd < 0) { | ||
84 | /* The file didn't exist on disk */ | ||
85 | if (!must_exist) { | ||
86 | DEBUGF("Trying to create link file '%s'...\n", filename); | ||
87 | fd = rb->creat(filename); | ||
88 | if (fd < 0){ | ||
89 | /* For some reason we couldn't create the file, | ||
90 | * so return an error message and exit */ | ||
91 | rb->splash(HZ*2, "Couldn't create the shortcuts file %s", | ||
92 | filename); | ||
93 | goto end_of_proc; | ||
94 | } | ||
95 | /* File created, but there's nothing in it, so just exit */ | ||
96 | ret_val = true; | ||
97 | goto end_of_proc; | ||
98 | } else { | ||
99 | rb->splash(HZ, "Couldn't open %s", filename); | ||
100 | goto end_of_proc; | ||
101 | } | ||
102 | } | ||
103 | |||
104 | while ((amountread=rb->read_line(fd,sc_content, sizeof(sc_content)))) { | ||
105 | if (is_control(sc_content, file)) { | ||
106 | continue; | ||
107 | } | ||
108 | if (file->entry_cnt >= file->max_entries) { | ||
109 | rb->splash(HZ*2, "Too many entries in the file, max allowed: %d", | ||
110 | file->max_entries); | ||
111 | goto end_of_proc; | ||
112 | } | ||
113 | if (!parse_entry_content(sc_content, &entry,file->show_last_segments)) { | ||
114 | /* Could not parse the entry (too long path?) -> ignore */ | ||
115 | DEBUGF("Could not parse '%s' -> ignored\n", sc_content); | ||
116 | continue; | ||
117 | } | ||
118 | DEBUGF("Parsed entry: path=%s, disp=%s\n", entry.path, entry.disp); | ||
119 | append_entry(file, &entry); | ||
120 | } | ||
121 | |||
122 | #ifdef SC_DEBUG | ||
123 | print_file(file); | ||
124 | #endif | ||
125 | |||
126 | ret_val = true; /* Everything went ok */ | ||
127 | |||
128 | end_of_proc: | ||
129 | if (fd >= 0) { | ||
130 | rb->close(fd); | ||
131 | fd = -1; | ||
132 | } | ||
133 | return ret_val; | ||
134 | } | ||
135 | |||
136 | #ifdef SC_DEBUG | ||
137 | void print_file(sc_file_t *file) | ||
138 | { | ||
139 | DEBUGF("Number of entries : %d\n", file->entry_cnt); | ||
140 | DEBUGF("Show Last Segments: %d\n", file->show_last_segments); | ||
141 | int i; | ||
142 | sc_entry_t *entry; | ||
143 | for (i=0, entry=file->entries; i<file->entry_cnt; i++,entry++) { | ||
144 | if (entry->explicit_disp) { | ||
145 | DEBUGF("%2d. '%s', show as '%s'\n", i+1, entry->path, entry->disp); | ||
146 | } else { | ||
147 | DEBUGF("%2d. '%s' (%s)\n", i+1, entry->path, entry->disp); | ||
148 | } | ||
149 | } | ||
150 | } | ||
151 | #endif | ||
152 | |||
153 | |||
154 | bool append_entry(sc_file_t *file, sc_entry_t *entry) | ||
155 | { | ||
156 | if (file->entry_cnt >= file->max_entries) { | ||
157 | return false; | ||
158 | } | ||
159 | rb->memcpy(file->entries+file->entry_cnt, entry, sizeof(*entry)); | ||
160 | file->entry_cnt++; | ||
161 | return true; | ||
162 | } | ||
163 | |||
164 | |||
165 | bool remove_entry(sc_file_t *file, int entry_idx) | ||
166 | { | ||
167 | if ((entry_idx<0) || (entry_idx>=file->entry_cnt)) { | ||
168 | return false; | ||
169 | } | ||
170 | sc_entry_t *start = file->entries + entry_idx; | ||
171 | rb->memmove(start, start + 1, | ||
172 | (file->entry_cnt-entry_idx-1) * sizeof(sc_entry_t)); | ||
173 | file->entry_cnt--; | ||
174 | return true; | ||
175 | } | ||
176 | |||
177 | |||
178 | bool is_valid_index(sc_file_t *file, int entry_idx) | ||
179 | { | ||
180 | return (entry_idx >= 0) && (entry_idx < file->entry_cnt); | ||
181 | } | ||
182 | |||
183 | |||
184 | bool parse_entry_content(char *line, sc_entry_t *entry, int last_segm) | ||
185 | { | ||
186 | char *sep; | ||
187 | char *path, *disp; | ||
188 | unsigned int path_len, disp_len; | ||
189 | bool expl; | ||
190 | |||
191 | sep = rb->strcasestr(line, PATH_DISP_SEPARATOR); | ||
192 | expl = (sep != NULL); | ||
193 | if (expl) { | ||
194 | /* Explicit name for the entry is specified -> use it */ | ||
195 | path = line; | ||
196 | path_len = sep - line; | ||
197 | disp = sep + PATH_DISP_SEPARATOR_LEN; | ||
198 | disp_len = rb->strlen(disp); | ||
199 | } else { | ||
200 | /* No special name to display */ | ||
201 | path = line; | ||
202 | path_len = rb->strlen(line); | ||
203 | if (last_segm <= 0) { | ||
204 | disp = path; | ||
205 | } else { | ||
206 | disp = last_segments(line, last_segm); | ||
207 | } | ||
208 | disp_len = rb->strlen(disp); | ||
209 | } | ||
210 | |||
211 | if (path_len >= sizeof(entry->path) || disp_len >= sizeof(entry->disp)) { | ||
212 | DEBUGF("Bad entry: pathlen=%d, displen=%d\n", path_len, disp_len); | ||
213 | return false; | ||
214 | } | ||
215 | rb->strncpy(entry->path, path, path_len); | ||
216 | entry->path[path_len] = '\0'; | ||
217 | rb->strcpy(entry->disp, disp); /* Safe since we've checked the length */ | ||
218 | entry->explicit_disp = expl; | ||
219 | return true; | ||
220 | } | ||
221 | |||
222 | |||
223 | char *last_segments(char *path, int nsegm) | ||
224 | { | ||
225 | char *p = rb->strrchr(path, PATH_SEPARATOR[0]); /* Hack */ | ||
226 | int seg_cnt; | ||
227 | if (p == NULL) | ||
228 | return path; /* No separator??? */ | ||
229 | seg_cnt = 0; | ||
230 | while ((p > path) && (seg_cnt < nsegm)) { | ||
231 | p--; | ||
232 | if (!starts_with(p, PATH_SEPARATOR)) { | ||
233 | continue; | ||
234 | } | ||
235 | seg_cnt++; | ||
236 | if (seg_cnt == nsegm && p > path) { | ||
237 | p++; /* Eat the '/' to show that something has been truncated */ | ||
238 | } | ||
239 | } | ||
240 | return p; | ||
241 | } | ||
242 | |||
243 | |||
244 | bool is_control(char *line, sc_file_t *file) | ||
245 | { | ||
246 | char name[MAX_PATH], value[MAX_PATH]; | ||
247 | if (!starts_with(line, CONTROL_PREFIX)) { | ||
248 | return false; | ||
249 | } | ||
250 | line += CONTROL_PREFIX_LEN; | ||
251 | |||
252 | if (!parse_name_value(line, name, sizeof(name), | ||
253 | value, sizeof(value))) { | ||
254 | DEBUGF("Bad processing instruction: '%s'\n", line); | ||
255 | return true; | ||
256 | } | ||
257 | |||
258 | /* Process control instruction */ | ||
259 | if (rb->strcasestr(name, INSTR_DISPLAY_LAST_SEGMENTS)) { | ||
260 | file->show_last_segments = rb->atoi(value); | ||
261 | DEBUGF("Set show last segms to %d\n", file->show_last_segments); | ||
262 | } else { | ||
263 | /* Unknown instruction -> ignore */ | ||
264 | DEBUGF("Unknown processing instruction: '%s'\n", name); | ||
265 | } | ||
266 | |||
267 | return true; | ||
268 | } | ||
269 | |||
270 | |||
271 | bool starts_with(char *string, char *prefix) | ||
272 | { | ||
273 | unsigned int pfx_len = rb->strlen(prefix); | ||
274 | if (rb->strlen(string) < pfx_len) | ||
275 | return false; | ||
276 | return (rb->strncmp(string, prefix, pfx_len) == 0); | ||
277 | } | ||
278 | |||
279 | |||
280 | bool parse_name_value(char *line, char *name, int namesize, | ||
281 | char *value, int valuesize) | ||
282 | { | ||
283 | char *sep; | ||
284 | int name_len, val_len; | ||
285 | name[0] = value[0] = '\0'; | ||
286 | |||
287 | sep = rb->strcasestr(line, NAME_VALUE_SEPARATOR); | ||
288 | if (sep == NULL) { | ||
289 | /* No separator char -> weird instruction */ | ||
290 | return false; | ||
291 | } | ||
292 | name_len = sep - line; | ||
293 | if (name_len >= namesize) { | ||
294 | /* Too long name */ | ||
295 | return false; | ||
296 | } | ||
297 | rb->strncpy(name, line, name_len); | ||
298 | name[name_len] = '\0'; | ||
299 | |||
300 | val_len = rb->strlen(line) - name_len - NAME_VALUE_SEPARATOR_LEN; | ||
301 | if (val_len >= valuesize) { | ||
302 | /* Too long value */ | ||
303 | return false; | ||
304 | } | ||
305 | rb->strncpy(value, sep+NAME_VALUE_SEPARATOR_LEN, val_len+1); | ||
306 | return true; | ||
307 | } | ||
308 | |||
309 | |||
310 | bool dump_sc_file(sc_file_t *file, char *filename) | ||
311 | { | ||
312 | DEBUGF("Dumping shortcuts to the file '%s'\n", filename); | ||
313 | int fd; | ||
314 | |||
315 | /* ideally, we should just write a new | ||
316 | * entry to the file, but I'm going to | ||
317 | * be lazy, and just re-write the whole | ||
318 | * thing. */ | ||
319 | fd = rb->open(filename, O_WRONLY|O_TRUNC); | ||
320 | if (fd < 0) { | ||
321 | rb->splash(HZ*2, "Could not open shortcuts file %s for writing", | ||
322 | filename); | ||
323 | return false; | ||
324 | } | ||
325 | |||
326 | /* | ||
327 | * Write instructions | ||
328 | */ | ||
329 | /* Always dump the 'display last segms' settings (even it it's | ||
330 | * not active) so that it can be easily changed without having | ||
331 | * to remember the setting name */ | ||
332 | write_int_instruction_to_file(fd, | ||
333 | INSTR_DISPLAY_LAST_SEGMENTS, file->show_last_segments); | ||
334 | |||
335 | int i; | ||
336 | sc_entry_t *entry; | ||
337 | for (i=0, entry=file->entries; i<file->entry_cnt; i++,entry++) { | ||
338 | write_entry_to_file(fd, entry); | ||
339 | } | ||
340 | |||
341 | rb->close(fd); | ||
342 | DEBUGF("Dumped %d entries to the file '%s'\n", file->entry_cnt, filename); | ||
343 | |||
344 | return true; | ||
345 | } | ||
346 | |||
347 | |||
348 | void write_int_instruction_to_file(int fd, char *instr, int value) | ||
349 | { | ||
350 | rb->fdprintf(fd, "%s%s%s%d\n", CONTROL_PREFIX, instr, | ||
351 | NAME_VALUE_SEPARATOR, value); | ||
352 | } | ||
353 | |||
354 | |||
355 | void write_entry_to_file(int fd, sc_entry_t *entry) | ||
356 | { | ||
357 | rb->fdprintf(fd, "%s", entry->path); | ||
358 | if (entry->explicit_disp) { | ||
359 | rb->fdprintf(fd, "%s%s", PATH_DISP_SEPARATOR, entry->disp); | ||
360 | } | ||
361 | rb->fdprintf(fd, "\n"); | ||
362 | } | ||
363 | |||
364 | |||
365 | bool file_exists(char *filename) | ||
366 | { | ||
367 | int fd = rb->open(filename, O_RDONLY); | ||
368 | bool retval; | ||
369 | if (fd >= 0) { | ||
370 | rb->close(fd); | ||
371 | retval = true; | ||
372 | } else { | ||
373 | retval = false; | ||
374 | } | ||
375 | DEBUGF("Checked existence of the file '%s': %s\n", | ||
376 | filename, (retval ? "found" : "NOT FOUND")); | ||
377 | return retval; | ||
378 | } | ||
379 | |||
380 | |||
381 | bool dir_exists(char *path) | ||
382 | { | ||
383 | DIR* d = rb->opendir(path); | ||
384 | bool retval; | ||
385 | if (d != NULL) { | ||
386 | rb->closedir(d); | ||
387 | retval = true; | ||
388 | } else { | ||
389 | retval = false; | ||
390 | } | ||
391 | DEBUGF("Checked existence of the dir '%s': %s\n", | ||
392 | path, (retval ? "found" : "NOT FOUND")); | ||
393 | return retval; | ||
394 | } | ||
diff --git a/apps/plugins/shortcuts/shortcuts_view.c b/apps/plugins/shortcuts/shortcuts_view.c new file mode 100644 index 0000000000..3b28b4b34c --- /dev/null +++ b/apps/plugins/shortcuts/shortcuts_view.c | |||
@@ -0,0 +1,238 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2007 Bryan Childs | ||
11 | * Copyright (c) 2007 Alexander Levin | ||
12 | * | ||
13 | * All files in this archive are subject to the GNU General Public License. | ||
14 | * See the file COPYING in the source tree root for full license agreement. | ||
15 | * | ||
16 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
17 | * KIND, either express or implied. | ||
18 | * | ||
19 | ****************************************************************************/ | ||
20 | |||
21 | #include "shortcuts.h" | ||
22 | |||
23 | PLUGIN_HEADER | ||
24 | |||
25 | enum sc_list_action_type | ||
26 | { | ||
27 | SCLA_NONE, | ||
28 | SCLA_SELECT, | ||
29 | SCLA_DELETE, | ||
30 | SCLA_USB, | ||
31 | }; | ||
32 | |||
33 | |||
34 | static char *link_filename; | ||
35 | static bool user_file; | ||
36 | static int gselected_item; | ||
37 | static bool usb_connected = false; | ||
38 | |||
39 | enum sc_list_action_type draw_sc_list(struct gui_synclist gui_sc); | ||
40 | |||
41 | /* Will be passed sc_file* as data */ | ||
42 | char* build_sc_list(int selected_item, void *data, char *buffer); | ||
43 | |||
44 | /* Returns true iff we should leave the main loop */ | ||
45 | bool list_sc(bool is_editable); | ||
46 | |||
47 | bool goto_entry(char *file_or_dir); | ||
48 | bool ends_with(char *str, char *suffix); | ||
49 | |||
50 | |||
51 | enum sc_list_action_type draw_sc_list(struct gui_synclist gui_sc) | ||
52 | { | ||
53 | int button; | ||
54 | |||
55 | rb->gui_synclist_draw(&gui_sc); | ||
56 | |||
57 | while (true) { | ||
58 | /* draw the statusbar, should be done often */ | ||
59 | rb->gui_syncstatusbar_draw(rb->statusbars, true); | ||
60 | /* user input */ | ||
61 | button = rb->get_action(CONTEXT_LIST, TIMEOUT_BLOCK); | ||
62 | if (rb->gui_synclist_do_button(&gui_sc, button, | ||
63 | LIST_WRAP_UNLESS_HELD)) { | ||
64 | /* automatic handling of user input. | ||
65 | * _UNLESS_HELD can be _ON or _OFF also | ||
66 | * selection changed, so redraw */ | ||
67 | continue; | ||
68 | } | ||
69 | switch (button) { /* process the user input */ | ||
70 | case ACTION_STD_OK: | ||
71 | gselected_item = rb->gui_synclist_get_sel_pos(&gui_sc); | ||
72 | return SCLA_SELECT; | ||
73 | case ACTION_STD_MENU: | ||
74 | /* Only allow delete entries in the default file | ||
75 | * since entries can be appended (with a plugin) | ||
76 | * to the default file only. The behaviour is thus | ||
77 | * symmetric in this respect. */ | ||
78 | if (!user_file) { | ||
79 | gselected_item = rb->gui_synclist_get_sel_pos(&gui_sc); | ||
80 | return SCLA_DELETE; | ||
81 | } | ||
82 | break; | ||
83 | case ACTION_STD_CANCEL: | ||
84 | return SCLA_NONE; | ||
85 | default: | ||
86 | if (rb->default_event_handler(button) == SYS_USB_CONNECTED) { | ||
87 | return SCLA_USB; | ||
88 | } | ||
89 | } | ||
90 | } | ||
91 | } | ||
92 | |||
93 | |||
94 | char* build_sc_list(int selected_item, void *data, char *buffer) | ||
95 | { | ||
96 | char text_buffer[MAX_PATH]; | ||
97 | sc_file_t *file = (sc_file_t*)data; | ||
98 | |||
99 | if (!is_valid_index(file, selected_item)) { | ||
100 | return NULL; | ||
101 | } | ||
102 | sc_entry_t *entry = file->entries + selected_item; | ||
103 | rb->snprintf(text_buffer, sizeof(text_buffer), "%s", entry->disp); | ||
104 | rb->strcpy(buffer, text_buffer); | ||
105 | return buffer; | ||
106 | } | ||
107 | |||
108 | |||
109 | bool list_sc(bool is_editable) | ||
110 | { | ||
111 | int selected_item = 0; | ||
112 | char selected_dir[MAX_PATH]; | ||
113 | enum sc_list_action_type action = SCLA_NONE; | ||
114 | struct gui_synclist gui_sc; | ||
115 | |||
116 | rb->memset(selected_dir, 0, sizeof(selected_dir)); | ||
117 | |||
118 | /* Setup the GUI list object, draw it to the screen, | ||
119 | * and then handle the user input to it */ | ||
120 | rb->gui_synclist_init(&gui_sc, &build_sc_list, &sc_file, false, 1); | ||
121 | rb->gui_synclist_set_title(&gui_sc, | ||
122 | (is_editable?"Shortcuts (editable)":"Shortcuts (sealed)"), NOICON); | ||
123 | rb->gui_synclist_set_nb_items(&gui_sc, sc_file.entry_cnt); | ||
124 | rb->gui_synclist_limit_scroll(&gui_sc, false); | ||
125 | rb->gui_synclist_select_item(&gui_sc, 0); | ||
126 | |||
127 | /* Draw the prepared widget to the LCD now */ | ||
128 | action = draw_sc_list(gui_sc); | ||
129 | if (action == SCLA_USB) { | ||
130 | usb_connected = true; | ||
131 | return true; | ||
132 | } | ||
133 | |||
134 | /* which item do we action? */ | ||
135 | selected_item = gselected_item; | ||
136 | |||
137 | if (!is_valid_index(&sc_file, selected_item)) { | ||
138 | /* This should never happen */ | ||
139 | rb->splash(HZ*2, "Bad entry selected!"); | ||
140 | return true; | ||
141 | } | ||
142 | |||
143 | /* perform the following actions if the user "selected" | ||
144 | * the item in the list (i.e. they want to go there | ||
145 | * in the filebrowser tree */ | ||
146 | switch(action) { | ||
147 | case SCLA_SELECT: | ||
148 | return goto_entry(sc_file.entries[selected_item].path); | ||
149 | case SCLA_DELETE: | ||
150 | rb->splash(HZ, "Deleting %s", sc_file.entries[selected_item].disp); | ||
151 | remove_entry(&sc_file, selected_item); | ||
152 | dump_sc_file(&sc_file, link_filename); | ||
153 | return (sc_file.entry_cnt == 0); | ||
154 | default: | ||
155 | return true; | ||
156 | } | ||
157 | } | ||
158 | |||
159 | |||
160 | bool goto_entry(char *file_or_dir) | ||
161 | { | ||
162 | DEBUGF("Trying to go to '%s'...\n", file_or_dir); | ||
163 | |||
164 | bool is_dir = ends_with(file_or_dir, PATH_SEPARATOR); | ||
165 | bool exists; | ||
166 | char *what; | ||
167 | if (is_dir) { | ||
168 | what = "Directory"; | ||
169 | exists = dir_exists(file_or_dir); | ||
170 | } else { | ||
171 | what = "File"; | ||
172 | exists = file_exists(file_or_dir); | ||
173 | } | ||
174 | |||
175 | if (!exists) { | ||
176 | rb->splash(HZ*2, "%s %s no longer exists on disk", what, file_or_dir); | ||
177 | return false; | ||
178 | } | ||
179 | /* Set the browsers dirfilter to the global setting | ||
180 | * This is required in case the plugin was launched | ||
181 | * from the plugins browser, in which case the | ||
182 | * dirfilter is set to only display .rock files */ | ||
183 | rb->set_dirfilter(rb->global_settings->dirfilter); | ||
184 | |||
185 | /* Change directory to the entry selected by the user */ | ||
186 | rb->set_current_file(file_or_dir); | ||
187 | return true; | ||
188 | } | ||
189 | |||
190 | |||
191 | bool ends_with(char *string, char *suffix) | ||
192 | { | ||
193 | unsigned int str_len = rb->strlen(string); | ||
194 | unsigned int sfx_len = rb->strlen(suffix); | ||
195 | if (str_len < sfx_len) | ||
196 | return false; | ||
197 | return (rb->strncmp(string + str_len - sfx_len, suffix, sfx_len) == 0); | ||
198 | } | ||
199 | |||
200 | |||
201 | enum plugin_status plugin_start(struct plugin_api* api, void* void_parameter) | ||
202 | { | ||
203 | rb = api; | ||
204 | bool leave_loop; | ||
205 | |||
206 | /* This is a viewer, so a parameter must have been specified */ | ||
207 | if (void_parameter == NULL) { | ||
208 | rb->splash(HZ*2, "No parameter specified!"); | ||
209 | return PLUGIN_ERROR; | ||
210 | } | ||
211 | link_filename = (char*)void_parameter; | ||
212 | user_file = (rb->strcmp(link_filename, SHORTCUTS_FILENAME) != 0); | ||
213 | |||
214 | allocate_memory(&memory_buf, &memory_bufsize); | ||
215 | |||
216 | if (!load_sc_file(&sc_file, link_filename, true, | ||
217 | memory_buf, memory_bufsize)) { | ||
218 | DEBUGF("Could not load %s\n", link_filename); | ||
219 | return PLUGIN_ERROR; | ||
220 | } | ||
221 | if (sc_file.entry_cnt==0) { | ||
222 | rb->splash(HZ*2, "No shortcuts in the file!"); | ||
223 | return PLUGIN_OK; | ||
224 | } else if ((sc_file.entry_cnt==1) && user_file) { | ||
225 | /* if there's only one entry in the user .link file, | ||
226 | * go straight to it without displaying the menu | ||
227 | * thus allowing 'quick links' */ | ||
228 | goto_entry(sc_file.entries[0].path); | ||
229 | return PLUGIN_OK; | ||
230 | } | ||
231 | |||
232 | do { | ||
233 | /* Display a menu to choose between the entries */ | ||
234 | leave_loop = list_sc(!user_file); | ||
235 | } while (!leave_loop); | ||
236 | |||
237 | return usb_connected ? PLUGIN_USB_CONNECTED : PLUGIN_OK; | ||
238 | } | ||
diff --git a/apps/plugins/viewers.config b/apps/plugins/viewers.config index 342af3a5e0..27fae18b4c 100644 --- a/apps/plugins/viewers.config +++ b/apps/plugins/viewers.config | |||
@@ -36,4 +36,5 @@ z80,viewers/zxbox,12 | |||
36 | *,viewers/properties,- | 36 | *,viewers/properties,- |
37 | colours,apps/text_editor,11 | 37 | colours,apps/text_editor,11 |
38 | ssg,games/superdom,- | 38 | ssg,games/superdom,- |
39 | link,viewers/shortcuts,- | 39 | link,viewers/shortcuts_view,- |
40 | *,viewers/shortcuts_append,- | ||