Prepare new maemo release
[maemo-rb.git] / apps / filetree.c
blob2edcaf3a03dfca5b220e697df84e20a744c85fc6
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2005 by Björn Stenberg
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
21 #include <stdlib.h>
22 #include <file.h>
23 #include <dir.h>
24 #include <string.h>
25 #include <kernel.h>
26 #include <lcd.h>
27 #include <debug.h>
28 #include <font.h>
29 #include <limits.h>
30 #include "bookmark.h"
31 #include "tree.h"
32 #include "core_alloc.h"
33 #include "settings.h"
34 #include "filetypes.h"
35 #include "talk.h"
36 #include "playlist.h"
37 #include "lang.h"
38 #include "language.h"
39 #include "screens.h"
40 #include "plugin.h"
41 #include "rolo.h"
42 #include "splash.h"
43 #include "cuesheet.h"
44 #include "filetree.h"
45 #include "misc.h"
46 #include "strnatcmp.h"
47 #ifdef HAVE_LCD_BITMAP
48 #include "keyboard.h"
49 #endif
51 #if CONFIG_TUNER
52 #include "radio.h"
53 #endif
54 #include "wps.h"
56 static int compare_sort_dir; /* qsort key for sorting directories */
58 int ft_build_playlist(struct tree_context* c, int start_index)
60 int i;
61 int start=start_index;
63 tree_lock_cache(c);
64 struct entry *entries = tree_get_entries(c);
66 for(i = 0;i < c->filesindir;i++)
68 if((entries[i].attr & FILE_ATTR_MASK) == FILE_ATTR_AUDIO)
70 if (playlist_add(entries[i].name) < 0)
71 break;
73 else
75 /* Adjust the start index when se skip non-MP3 entries */
76 if(i < start)
77 start_index--;
81 tree_unlock_cache(c);
83 return start_index;
86 /* Start playback of a playlist, checking for bookmark autoload, modified
87 * playlists, etc., as required. Returns false if playback wasn't started,
88 * or started via bookmark autoload, true otherwise.
90 * Pointers to both the full pathname and the separated parts needed to
91 * avoid allocating yet another path buffer on the stack (and save some
92 * code; the caller typically needs to create the full pathname anyway)...
94 bool ft_play_playlist(char* pathname, char* dirname, char* filename)
96 if (global_settings.party_mode && audio_status())
98 splash(HZ, ID2P(LANG_PARTY_MODE));
99 return false;
102 if (bookmark_autoload(pathname))
104 return false;
107 splash(0, ID2P(LANG_WAIT));
109 /* about to create a new current playlist...
110 allow user to cancel the operation */
111 if (!warn_on_pl_erase())
112 return false;
114 if (playlist_create(dirname, filename) != -1)
116 if (global_settings.playlist_shuffle)
118 playlist_shuffle(current_tick, -1);
121 playlist_start(0, 0);
122 return true;
125 return false;
128 /* walk a directory and check all entries if a .talk file exists */
129 static void check_file_thumbnails(struct tree_context* c)
131 int i;
132 struct dirent *entry;
133 struct entry* entries;
134 DIR *dir;
136 dir = opendir(c->currdir);
137 if(!dir)
138 return;
139 /* mark all files as non talking, except the .talk ones */
140 entries = tree_get_entries(c);
141 tree_lock_cache(c);
142 for (i=0; i < c->filesindir; i++)
144 if (entries[i].attr & ATTR_DIRECTORY)
145 continue; /* we're not touching directories */
147 if (strcasecmp(file_thumbnail_ext,
148 &entries[i].name[strlen(entries[i].name)
149 - strlen(file_thumbnail_ext)]))
150 { /* no .talk file */
151 entries[i].attr &= ~FILE_ATTR_THUMBNAIL; /* clear */
153 else
154 { /* .talk file, we later let them speak themselves */
155 entries[i].attr |= FILE_ATTR_THUMBNAIL; /* set */
159 while((entry = readdir(dir)) != 0) /* walk directory */
161 int ext_pos;
162 struct dirinfo info = dir_get_info(dir, entry);
163 ext_pos = strlen((char *)entry->d_name) - strlen(file_thumbnail_ext);
164 if (ext_pos <= 0 /* too short to carry ".talk" */
165 || (info.attribute & ATTR_DIRECTORY) /* no file */
166 || strcasecmp((char *)&entry->d_name[ext_pos], file_thumbnail_ext))
167 { /* or doesn't end with ".talk", no candidate */
168 continue;
171 /* terminate the (disposable) name in dir buffer,
172 this truncates off the ".talk" without needing an extra buffer */
173 entry->d_name[ext_pos] = '\0';
175 /* search corresponding file in dir cache */
176 for (i=0; i < c->filesindir; i++)
178 if (!strcasecmp(entries[i].name, (char *)entry->d_name))
179 { /* match */
180 entries[i].attr |= FILE_ATTR_THUMBNAIL; /* set the flag */
181 break; /* exit search loop, because we found it */
185 tree_unlock_cache(c);
186 closedir(dir);
189 /* support function for qsort() */
190 static int compare(const void* p1, const void* p2)
192 struct entry* e1 = (struct entry*)p1;
193 struct entry* e2 = (struct entry*)p2;
194 int criteria;
196 if (e1->attr & ATTR_DIRECTORY && e2->attr & ATTR_DIRECTORY)
197 { /* two directories */
198 criteria = compare_sort_dir;
200 #ifdef HAVE_MULTIVOLUME
201 if (e1->attr & ATTR_VOLUME || e2->attr & ATTR_VOLUME)
202 { /* a volume identifier is involved */
203 if (e1->attr & ATTR_VOLUME && e2->attr & ATTR_VOLUME)
204 criteria = SORT_ALPHA; /* two volumes: sort alphabetically */
205 else /* only one is a volume: volume first */
206 return (e2->attr & ATTR_VOLUME) - (e1->attr & ATTR_VOLUME);
208 #endif
211 else if (!(e1->attr & ATTR_DIRECTORY) && !(e2->attr & ATTR_DIRECTORY))
212 { /* two files */
213 criteria = global_settings.sort_file;
215 else /* dir and file, dir goes first */
216 return (e2->attr & ATTR_DIRECTORY) - (e1->attr & ATTR_DIRECTORY);
218 switch(criteria)
220 case SORT_TYPE:
221 case SORT_TYPE_REVERSED:
223 int t1 = e1->attr & FILE_ATTR_MASK;
224 int t2 = e2->attr & FILE_ATTR_MASK;
226 if (!t1) /* unknown type */
227 t1 = INT_MAX; /* gets a high number, to sort after known */
228 if (!t2) /* unknown type */
229 t2 = INT_MAX; /* gets a high number, to sort after known */
231 if (t1 != t2) /* if different */
232 return (t1 - t2) * (criteria == SORT_TYPE_REVERSED ? -1 : 1);
233 /* else fall through to alphabetical sorting */
236 case SORT_DATE:
237 case SORT_DATE_REVERSED:
238 /* Ignore SORT_TYPE */
239 if (criteria == SORT_DATE || criteria == SORT_DATE_REVERSED)
241 if (e1->time_write != e2->time_write)
242 return (e1->time_write - e2->time_write)
243 * (criteria == SORT_DATE_REVERSED ? -1 : 1);
244 /* else fall through to alphabetical sorting */
247 case SORT_ALPHA:
248 case SORT_ALPHA_REVERSED:
250 if (global_settings.sort_case)
252 if (global_settings.interpret_numbers == SORT_INTERPRET_AS_NUMBER)
253 return strnatcmp(e1->name, e2->name)
254 * (criteria == SORT_ALPHA_REVERSED ? -1 : 1);
255 else
256 return strncmp(e1->name, e2->name, MAX_PATH)
257 * (criteria == SORT_ALPHA_REVERSED ? -1 : 1);
259 else
261 if (global_settings.interpret_numbers == SORT_INTERPRET_AS_NUMBER)
262 return strnatcasecmp(e1->name, e2->name)
263 * (criteria == SORT_ALPHA_REVERSED ? -1 : 1);
264 else
265 return strncasecmp(e1->name, e2->name, MAX_PATH)
266 * (criteria == SORT_ALPHA_REVERSED ? -1 : 1);
271 return 0; /* never reached */
274 /* load and sort directory into the tree's cache. returns NULL on failure. */
275 int ft_load(struct tree_context* c, const char* tempdir)
277 int files_in_dir = 0;
278 int name_buffer_used = 0;
279 struct dirent *entry;
280 bool (*callback_show_item)(char *, int, struct tree_context *) = NULL;
281 DIR *dir;
283 if (tempdir)
284 dir = opendir(tempdir);
285 else
287 dir = opendir(c->currdir);
288 callback_show_item = c->browse? c->browse->callback_show_item: NULL;
290 if(!dir)
291 return -1; /* not a directory */
293 c->dirsindir = 0;
294 c->dirfull = false;
296 tree_lock_cache(c);
297 while ((entry = readdir(dir))) {
298 int len;
299 struct dirinfo info;
300 struct entry* dptr = tree_get_entry_at(c, files_in_dir);
301 if (!entry)
302 break;
304 info = dir_get_info(dir, entry);
305 len = strlen((char *)entry->d_name);
307 /* skip directories . and .. */
308 if ((info.attribute & ATTR_DIRECTORY) &&
309 (((len == 1) && (!strncmp((char *)entry->d_name, ".", 1))) ||
310 ((len == 2) && (!strncmp((char *)entry->d_name, "..", 2))))) {
311 continue;
314 /* Skip FAT volume ID */
315 if (info.attribute & ATTR_VOLUME_ID) {
316 continue;
319 /* filter out dotfiles and hidden files */
320 if (*c->dirfilter != SHOW_ALL &&
321 ((entry->d_name[0]=='.') ||
322 (info.attribute & ATTR_HIDDEN))) {
323 continue;
326 dptr->attr = info.attribute;
328 /* check for known file types */
329 if ( !(dptr->attr & ATTR_DIRECTORY) )
330 dptr->attr |= filetype_get_attr((char *)entry->d_name);
332 /* filter out non-visible files */
333 if ((!(dptr->attr & ATTR_DIRECTORY) && (
334 (*c->dirfilter == SHOW_PLAYLIST &&
335 (dptr->attr & FILE_ATTR_MASK) != FILE_ATTR_M3U) ||
336 ((*c->dirfilter == SHOW_MUSIC &&
337 (dptr->attr & FILE_ATTR_MASK) != FILE_ATTR_AUDIO) &&
338 (dptr->attr & FILE_ATTR_MASK) != FILE_ATTR_M3U) ||
339 (*c->dirfilter == SHOW_SUPPORTED && !filetype_supported(dptr->attr)))) ||
340 (*c->dirfilter == SHOW_WPS && (dptr->attr & FILE_ATTR_MASK) != FILE_ATTR_WPS) ||
341 #ifdef HAVE_LCD_BITMAP
342 (*c->dirfilter == SHOW_FONT && (dptr->attr & FILE_ATTR_MASK) != FILE_ATTR_FONT) ||
343 (*c->dirfilter == SHOW_SBS && (dptr->attr & FILE_ATTR_MASK) != FILE_ATTR_SBS) ||
344 #if CONFIG_TUNER
345 (*c->dirfilter == SHOW_FMS && (dptr->attr & FILE_ATTR_MASK) != FILE_ATTR_FMS) ||
346 #endif
347 #endif
348 #ifdef HAVE_REMOTE_LCD
349 (*c->dirfilter == SHOW_RWPS && (dptr->attr & FILE_ATTR_MASK) != FILE_ATTR_RWPS) ||
350 (*c->dirfilter == SHOW_RSBS && (dptr->attr & FILE_ATTR_MASK) != FILE_ATTR_RSBS) ||
351 #if CONFIG_TUNER
352 (*c->dirfilter == SHOW_RFMS && (dptr->attr & FILE_ATTR_MASK) != FILE_ATTR_RFMS) ||
353 #endif
354 #endif
355 #if CONFIG_TUNER
356 (*c->dirfilter == SHOW_FMR && (dptr->attr & FILE_ATTR_MASK) != FILE_ATTR_FMR) ||
357 #endif
358 (*c->dirfilter == SHOW_M3U && (dptr->attr & FILE_ATTR_MASK) != FILE_ATTR_M3U) ||
359 (*c->dirfilter == SHOW_CFG && (dptr->attr & FILE_ATTR_MASK) != FILE_ATTR_CFG) ||
360 (*c->dirfilter == SHOW_LNG && (dptr->attr & FILE_ATTR_MASK) != FILE_ATTR_LNG) ||
361 (*c->dirfilter == SHOW_MOD && (dptr->attr & FILE_ATTR_MASK) != FILE_ATTR_MOD) ||
362 (*c->dirfilter == SHOW_PLUGINS && (dptr->attr & FILE_ATTR_MASK) != FILE_ATTR_ROCK &&
363 (dptr->attr & FILE_ATTR_MASK) != FILE_ATTR_LUA) ||
364 (callback_show_item && !callback_show_item(entry->d_name, dptr->attr, c)))
366 continue;
369 if ((len > c->cache.name_buffer_size - name_buffer_used - 1) ||
370 (files_in_dir >= c->cache.max_entries)) {
371 /* Tell the world that we ran out of buffer space */
372 c->dirfull = true;
373 break;
376 ++files_in_dir;
378 dptr->name = core_get_data(c->cache.name_buffer_handle)+name_buffer_used;
379 dptr->time_write =
380 (long)info.wrtdate<<16 |
381 (long)info.wrttime; /* in one # */
382 strcpy(dptr->name, (char *)entry->d_name);
383 name_buffer_used += len + 1;
385 if (dptr->attr & ATTR_DIRECTORY) /* count the remaining dirs */
386 c->dirsindir++;
388 c->filesindir = files_in_dir;
389 c->dirlength = files_in_dir;
390 closedir(dir);
392 compare_sort_dir = c->sort_dir;
393 qsort(tree_get_entries(c), files_in_dir, sizeof(struct entry), compare);
395 /* If thumbnail talking is enabled, make an extra run to mark files with
396 associated thumbnails, so we don't do unsuccessful spinups later. */
397 if (global_settings.talk_file_clip)
398 check_file_thumbnails(c); /* map .talk to ours */
400 tree_unlock_cache(c);
401 return 0;
403 #ifdef HAVE_LCD_BITMAP
404 static void ft_load_font(char *file)
406 int current_font_id;
407 enum screen_type screen = SCREEN_MAIN;
408 #if NB_SCREENS > 1
409 MENUITEM_STRINGLIST(menu, ID2P(LANG_CUSTOM_FONT), NULL,
410 ID2P(LANG_MAIN_SCREEN), ID2P(LANG_REMOTE_SCREEN))
411 switch (do_menu(&menu, NULL, NULL, false))
413 case 0: /* main lcd */
414 screen = SCREEN_MAIN;
415 set_file(file, (char *)global_settings.font_file, MAX_FILENAME);
416 break;
417 case 1: /* remote */
418 screen = SCREEN_REMOTE;
419 set_file(file, (char *)global_settings.remote_font_file, MAX_FILENAME);
420 break;
422 #else
423 set_file(file, (char *)global_settings.font_file, MAX_FILENAME);
424 #endif
425 splash(0, ID2P(LANG_WAIT));
426 current_font_id = screens[screen].getuifont();
427 if (current_font_id >= 0)
428 font_unload(current_font_id);
429 screens[screen].setuifont(
430 font_load_ex(file,0,global_settings.glyphs_to_cache));
431 viewportmanager_theme_changed(THEME_UI_VIEWPORT);
433 #endif
435 int ft_enter(struct tree_context* c)
437 int rc = GO_TO_PREVIOUS;
438 char buf[MAX_PATH];
439 struct entry* file = tree_get_entry_at(c, c->selected_item);
440 int file_attr = file->attr;
442 if (c->currdir[1])
443 snprintf(buf,sizeof(buf),"%s/%s",c->currdir, file->name);
444 else
445 snprintf(buf,sizeof(buf),"/%s",file->name);
447 if (file_attr & ATTR_DIRECTORY) {
448 memcpy(c->currdir, buf, sizeof(c->currdir));
449 if ( c->dirlevel < MAX_DIR_LEVELS )
450 c->selected_item_history[c->dirlevel] = c->selected_item;
451 c->dirlevel++;
452 c->selected_item=0;
454 else {
455 int seed = current_tick;
456 bool play = false;
457 int start_index=0;
459 switch ( file_attr & FILE_ATTR_MASK ) {
460 case FILE_ATTR_M3U:
461 play = ft_play_playlist(buf, c->currdir, file->name);
463 if (play)
465 start_index = 0;
468 break;
470 case FILE_ATTR_AUDIO:
471 if (bookmark_autoload(c->currdir))
472 break;
474 splash(0, ID2P(LANG_WAIT));
476 /* about to create a new current playlist...
477 allow user to cancel the operation */
478 if (!warn_on_pl_erase())
479 break;
481 if (global_settings.party_mode && audio_status())
483 playlist_insert_track(NULL, buf,
484 PLAYLIST_INSERT_LAST, true, true);
485 splash(HZ, ID2P(LANG_QUEUE_LAST));
487 else if (playlist_create(c->currdir, NULL) != -1)
489 start_index = ft_build_playlist(c, c->selected_item);
490 if (global_settings.playlist_shuffle)
492 start_index = playlist_shuffle(seed, start_index);
494 /* when shuffling dir.: play all files
495 even if the file selected by user is
496 not the first one */
497 if (!global_settings.play_selected)
498 start_index = 0;
501 playlist_start(start_index, 0);
502 play = true;
504 break;
506 #if CONFIG_TUNER
507 /* fmr preset file */
508 case FILE_ATTR_FMR:
509 splash(0, ID2P(LANG_WAIT));
511 /* Preset inside the default folder. */
512 if(!strncasecmp(FMPRESET_PATH, buf, strlen(FMPRESET_PATH)))
514 set_file(buf, global_settings.fmr_file, MAX_FILENAME);
515 radio_load_presets(global_settings.fmr_file);
518 * Preset outside default folder, we can choose such only
519 * if we are out of the radio screen, so the check for the
520 * radio status isn't neccessary
522 else
524 radio_load_presets(buf);
526 rc = GO_TO_FM;
528 break;
529 case FILE_ATTR_FMS:
530 splash(0, ID2P(LANG_WAIT));
531 set_file(buf, (char *)global_settings.fms_file, MAX_FILENAME);
532 settings_apply_skins();
533 break;
534 #ifdef HAVE_REMOTE_LCD
535 case FILE_ATTR_RFMS:
536 splash(0, ID2P(LANG_WAIT));
537 set_file(buf, (char *)global_settings.rfms_file, MAX_FILENAME);
538 settings_apply_skins();
539 break;
540 #endif
541 #endif
543 #ifdef HAVE_LCD_BITMAP
544 case FILE_ATTR_SBS:
545 splash(0, ID2P(LANG_WAIT));
546 set_file(buf, (char *)global_settings.sbs_file, MAX_FILENAME);
547 settings_apply_skins();
548 break;
549 #endif
550 #ifdef HAVE_REMOTE_LCD
551 case FILE_ATTR_RSBS:
552 splash(0, ID2P(LANG_WAIT));
553 set_file(buf, (char *)global_settings.rsbs_file, MAX_FILENAME);
554 settings_apply_skins();
555 break;
556 #endif
557 /* wps config file */
558 case FILE_ATTR_WPS:
559 splash(0, ID2P(LANG_WAIT));
560 set_file(buf, (char *)global_settings.wps_file,
561 MAX_FILENAME);
562 settings_apply_skins();
563 break;
565 #if defined(HAVE_REMOTE_LCD) && (NB_SCREENS > 1)
566 /* remote-wps config file */
567 case FILE_ATTR_RWPS:
568 splash(0, ID2P(LANG_WAIT));
569 set_file(buf, (char *)global_settings.rwps_file,
570 MAX_FILENAME);
571 settings_apply_skins();
572 break;
573 #endif
575 case FILE_ATTR_CFG:
576 splash(0, ID2P(LANG_WAIT));
577 if (!settings_load_config(buf,true))
578 break;
579 splash(HZ, ID2P(LANG_SETTINGS_LOADED));
580 break;
582 case FILE_ATTR_BMARK:
583 splash(0, ID2P(LANG_WAIT));
584 bookmark_load(buf, false);
585 rc = GO_TO_FILEBROWSER;
586 break;
588 case FILE_ATTR_LNG:
589 splash(0, ID2P(LANG_WAIT));
590 if (lang_core_load(buf))
592 splash(HZ, ID2P(LANG_FAILED));
593 break;
595 set_file(buf, (char *)global_settings.lang_file,
596 MAX_FILENAME);
597 talk_init(); /* use voice of same language */
598 viewportmanager_theme_changed(THEME_LANGUAGE);
599 settings_apply_skins();
600 splash(HZ, ID2P(LANG_LANGUAGE_LOADED));
601 break;
603 #ifdef HAVE_LCD_BITMAP
604 case FILE_ATTR_FONT:
605 ft_load_font(buf);
606 break;
608 case FILE_ATTR_KBD:
609 splash(0, ID2P(LANG_WAIT));
610 if (!load_kbd(buf))
611 splash(HZ, ID2P(LANG_KEYBOARD_LOADED));
612 set_file(buf, (char *)global_settings.kbd_file, MAX_FILENAME);
613 break;
614 #endif
616 #if (CONFIG_PLATFORM & PLATFORM_NATIVE)
617 /* firmware file */
618 case FILE_ATTR_MOD:
619 splash(0, ID2P(LANG_WAIT));
620 audio_hard_stop();
621 rolo_load(buf);
622 break;
623 #endif
625 /* plugin file */
626 case FILE_ATTR_ROCK:
627 case FILE_ATTR_LUA:
629 char *plugin = buf, *argument = NULL, lua_path[MAX_PATH];
630 int ret;
632 if ((file_attr & FILE_ATTR_MASK) == FILE_ATTR_LUA) {
633 snprintf(lua_path, sizeof(lua_path)-1, "%s/lua.rock", VIEWERS_DIR); /* Use a #define here ? */
634 plugin = lua_path;
635 argument = buf;
638 if (global_settings.party_mode && audio_status()) {
639 splash(HZ, ID2P(LANG_PARTY_MODE));
640 break;
642 ret = plugin_load(plugin, argument);
643 switch (ret)
645 case PLUGIN_GOTO_WPS:
646 play = true;
647 break;
648 case PLUGIN_USB_CONNECTED:
649 if(*c->dirfilter > NUM_FILTER_MODES)
650 /* leave sub-browsers after usb, doing
651 otherwise might be confusing to the user */
652 rc = GO_TO_ROOT;
653 else
654 rc = GO_TO_FILEBROWSER;
655 break;
657 case PLUGIN_ERROR:
658 case PLUGIN_OK:
660 default:
661 break;
663 break;
665 case FILE_ATTR_CUE:
666 display_cuesheet_content(buf);
667 break;
669 default:
671 const char* plugin;
673 if (global_settings.party_mode && audio_status()) {
674 splash(HZ, ID2P(LANG_PARTY_MODE));
675 break;
678 struct entry* file = tree_get_entry_at(c, c->selected_item);
679 plugin = filetype_get_plugin(file);
680 if (plugin)
682 switch (plugin_load(plugin,buf))
684 case PLUGIN_USB_CONNECTED:
685 rc = GO_TO_FILEBROWSER;
686 break;
687 case PLUGIN_GOTO_WPS:
688 rc = GO_TO_WPS;
689 break;
691 case PLUGIN_OK:
692 case PLUGIN_ERROR:
694 default:
695 break;
698 break;
702 if ( play ) {
703 /* the resume_index must always be the index in the
704 shuffled list in case shuffle is enabled */
705 global_status.resume_index = start_index;
706 global_status.resume_crc32 =
707 playlist_get_filename_crc32(NULL, start_index);
708 global_status.resume_offset = 0;
709 status_save();
710 rc = GO_TO_WPS;
712 else {
713 if (*c->dirfilter > NUM_FILTER_MODES &&
714 *c->dirfilter != SHOW_CFG &&
715 *c->dirfilter != SHOW_FONT &&
716 *c->dirfilter != SHOW_PLUGINS)
718 rc = GO_TO_ROOT;
722 return rc;
725 int ft_exit(struct tree_context* c)
727 extern char lastfile[]; /* from tree.c */
728 char buf[MAX_PATH];
729 int rc = 0;
730 bool exit_func = false;
732 int i = strlen(c->currdir);
733 if (i>1) {
734 while (c->currdir[i-1]!='/')
735 i--;
736 strcpy(buf,&c->currdir[i]);
737 if (i==1)
738 c->currdir[i]=0;
739 else
740 c->currdir[i-1]=0;
742 if (*c->dirfilter > NUM_FILTER_MODES && c->dirlevel < 1)
743 exit_func = true;
745 c->dirlevel--;
746 if ( c->dirlevel < MAX_DIR_LEVELS )
747 c->selected_item=c->selected_item_history[c->dirlevel];
748 else
749 c->selected_item=0;
751 /* if undefined position */
752 if (c->selected_item == -1)
753 strcpy(lastfile, buf);
755 else
757 if (*c->dirfilter > NUM_FILTER_MODES && c->dirlevel < 1)
758 exit_func = true;
761 if (exit_func)
762 rc = 3;
764 return rc;