summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter D'Hoye <peter.dhoye@gmail.com>2007-09-03 22:24:26 +0000
committerPeter D'Hoye <peter.dhoye@gmail.com>2007-09-03 22:24:26 +0000
commit946a815cd4166f8761fac0c9cba0092b871cf900 (patch)
tree043b27178fdb6822abb18abeb1f9aa6a6c1bd265
parent9b3be37f84286e24a14ac29b2ab0105d6171e188 (diff)
downloadrockbox-946a815cd4166f8761fac0c9cba0092b871cf900.tar.gz
rockbox-946a815cd4166f8761fac0c9cba0092b871cf900.zip
Accept FS #7667 by Alexander Levin with minor fixes by me. Splits the shortcuts plugin into two, one for adding and one for viewing. Removes hard-coded file extension and allows to link from one shortcut file to another.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@14599 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r--apps/onplay.c2
-rw-r--r--apps/plugins/CATEGORIES3
-rw-r--r--apps/plugins/SOURCES1
-rw-r--r--apps/plugins/SUBDIRS1
-rw-r--r--apps/plugins/shortcuts.c550
-rw-r--r--apps/plugins/shortcuts/Makefile90
-rw-r--r--apps/plugins/shortcuts/shortcuts.h90
-rw-r--r--apps/plugins/shortcuts/shortcuts_append.c109
-rw-r--r--apps/plugins/shortcuts/shortcuts_common.c394
-rw-r--r--apps/plugins/shortcuts/shortcuts_view.c238
-rw-r--r--apps/plugins/viewers.config3
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
1083static bool add_to_faves(void) 1083static 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
58rockpaint,apps 58rockpaint,apps
59search,viewers 59search,viewers
60searchengine,viewers 60searchengine,viewers
61shortcuts,viewers 61shortcuts_view,viewers
62shortcuts_append,viewers
62sliding_puzzle,games 63sliding_puzzle,games
63snake2,games 64snake2,games
64snake,games 65snake,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
13rockblox.c 13rockblox.c
14rockbox_flash.c 14rockbox_flash.c
15search.c 15search.c
16shortcuts.c
17snow.c 16snow.c
18sort.c 17sort.c
19stats.c 18stats.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 */
4shortcuts
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
22PLUGIN_HEADER
23
24static struct plugin_api* rb;
25
26#define SHORTCUTS_FILENAME "/shortcuts.link"
27#define MAX_SHORTCUTS 50
28
29MEM_FUNCTION_WRAPPERS(rb);
30
31typedef struct sc_file_s
32{
33 int readsize;
34 char* filebuf;
35} sc_file_t;
36
37typedef 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
44enum shortcut_type {
45 SCTYPE_NONE,
46 SCTYPE_FILE,
47 SCTYPE_DIR,
48};
49
50enum sc_list_action_type {
51 SCLA_NONE,
52 SCLA_SELECT,
53 SCLA_DELETE,
54};
55
56void sc_alloc_init(void);
57void* sc_malloc(unsigned int size);
58bool sc_init(void);
59enum sc_list_action_type draw_sc_list(struct gui_synclist gui_sc);
60char* build_sc_list(int selected_item, void* data, char* buffer);
61void delete_sc(int sc_num);
62bool load_sc_file(void);
63bool load_user_sc_file(char* filename);
64bool exists(char* filename);
65enum plugin_status list_sc(void);
66enum plugin_status write_sc_file(char* directory_name,enum shortcut_type st);
67
68char str_dirname[MAX_PATH];
69ssize_t bufleft;
70long mem_ptr;
71long bufsize;
72unsigned char* mallocbuf;
73bool its_a_dir = false;
74bool user_file = false;
75sc_file_t the_file;
76sc_entries_t* shortcuts = 0;
77sc_entries_t* lastentry = 0;
78int total_entries = 0;
79int gselected_item = 0;
80
81void 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
92void* 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
106bool 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
122bool sc_init(void) {
123 return load_sc_file();
124}
125
126enum 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
164char* 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
181void 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
219enum 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
277bool 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
341bool 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
398enum 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
454enum 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
10INCLUDES = -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
13CFLAGS = $(INCLUDES) $(GCCOPTS) $(TARGET) $(EXTRA_DEFINES) \
14 -DTARGET_ID=$(TARGET_ID) -DMEM=${MEMORYSIZE} -DPLUGIN
15
16ifdef APPEXTRA
17 INCLUDES += $(patsubst %,-I$(APPSDIR)/%,$(subst :, ,$(APPEXTRA)))
18endif
19
20LINKFILE := $(OBJDIR)/link.lds
21DEPFILE = $(OBJDIR)/dep-shortcuts
22
23SOURCES := shortcuts_common.c shortcuts_view.c shortcuts_append.c
24VIEW_OBJS := $(OBJDIR)/shortcuts_common.o $(OBJDIR)/shortcuts_view.o
25APPEND_OBJS := $(OBJDIR)/shortcuts_common.o $(OBJDIR)/shortcuts_append.o
26DIRS = .
27
28ifndef SIMVER
29 LDS := ../plugin.lds
30endif
31
32OUTPUT = $(OUTDIR)/shortcuts_view.rock $(OUTDIR)/shortcuts_append.rock
33
34all: $(OUTPUT)
35
36ifndef 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 $< $@
50else
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 $@
57ifeq ($(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
61else
62 @chmod -x $@
63endif
64
65$(OUTDIR)/shortcuts_append.rock: $(APPEND_OBJS)
66 $(call PRINTS,LD $(@F))$(CC) $(CFLAGS) $(SHARED_FLAG) $(APPEND_OBJS) -L$(BUILDDIR) -lplugin $(LINKBITMAPS) -o $@
67ifeq ($(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
71else
72 @chmod -x $@
73endif
74
75endif # end of simulator section
76
77
78include $(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
86clean:
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
35extern struct plugin_api* rb;
36
37typedef 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
44typedef 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
53extern void *memory_buf;
54extern long memory_bufsize;
55
56
57extern sc_file_t sc_file;
58
59
60/* Allocates a chunk of memory (as much as possible) */
61void allocate_memory(void **buf, size_t *bufsize);
62
63/* Initializes the file */
64void init_sc_file(sc_file_t *file, void *buf, size_t bufsize);
65
66/* Loads shortcuts from the file. Returns true iff succeeded */
67bool 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. */
71bool dump_sc_file(sc_file_t *file, char *filename);
72
73/* Appends the entry to the file. Entry is copied. Returns true iff succeded. */
74bool append_entry(sc_file_t *file, sc_entry_t *entry);
75
76/* Removes the specified entry (0-based index). Returns true iff succeded. */
77bool remove_entry(sc_file_t *file, int entry_idx);
78
79/* Checks whether the index is a valid one for the file. */
80bool is_valid_index(sc_file_t *file, int entry_idx);
81
82bool file_exists(char *filename); /* Does the specified file exist? */
83bool dir_exists(char *path); /* Does the specified dir exist? */
84
85
86#ifdef SC_DEBUG
87void 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
23PLUGIN_HEADER
24
25
26bool 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
52enum 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"
22MEM_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) */
36void *memory_buf;
37long memory_bufsize; /* Size of memory_buf in bytes */
38
39
40/* The file we're processing */
41sc_file_t sc_file;
42
43bool parse_entry_content(char *line, sc_entry_t *entry, int last_segm);
44char *last_segments(char *path, int nsegm);
45bool is_control(char *line, sc_file_t *file);
46bool starts_with(char *string, char *prefix);
47bool parse_name_value(char *line, char *name, int namesize,
48 char *value, int valuesize);
49void write_entry_to_file(int fd, sc_entry_t *entry);
50void write_int_instruction_to_file(int fd, char *instr, int value);
51
52
53void 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
60void 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
70bool 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
128end_of_proc:
129 if (fd >= 0) {
130 rb->close(fd);
131 fd = -1;
132 }
133 return ret_val;
134}
135
136#ifdef SC_DEBUG
137void 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
154bool 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
165bool 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
178bool is_valid_index(sc_file_t *file, int entry_idx)
179{
180 return (entry_idx >= 0) && (entry_idx < file->entry_cnt);
181}
182
183
184bool 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
223char *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
244bool 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
271bool 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
280bool 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
310bool 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
348void 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
355void 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
365bool 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
381bool 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
23PLUGIN_HEADER
24
25enum sc_list_action_type
26{
27 SCLA_NONE,
28 SCLA_SELECT,
29 SCLA_DELETE,
30 SCLA_USB,
31};
32
33
34static char *link_filename;
35static bool user_file;
36static int gselected_item;
37static bool usb_connected = false;
38
39enum sc_list_action_type draw_sc_list(struct gui_synclist gui_sc);
40
41/* Will be passed sc_file* as data */
42char* build_sc_list(int selected_item, void *data, char *buffer);
43
44/* Returns true iff we should leave the main loop */
45bool list_sc(bool is_editable);
46
47bool goto_entry(char *file_or_dir);
48bool ends_with(char *str, char *suffix);
49
50
51enum 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
94char* 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
109bool 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
160bool 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
191bool 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
201enum 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,-
37colours,apps/text_editor,11 37colours,apps/text_editor,11
38ssg,games/superdom,- 38ssg,games/superdom,-
39link,viewers/shortcuts,- 39link,viewers/shortcuts_view,-
40*,viewers/shortcuts_append,-