Code

code style, indent with tabs
[ncmpc.git] / src / screen_file.c
1 /* 
2  * $Id$
3  *
4  * (c) 2004 by Kalle Wallin <kaw@linux.se>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  *
19  */
21 #include <ctype.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <glib.h>
25 #include <ncurses.h>
27 #include "config.h"
28 #include "ncmpc.h"
29 #include "options.h"
30 #include "support.h"
31 #include "mpdclient.h"
32 #include "strfsong.h"
33 #include "command.h"
34 #include "screen.h"
35 #include "screen_utils.h"
36 #include "screen_browse.h"
37 #include "screen_play.h"
39 #define USE_OLD_LAYOUT
40 #undef  USE_OLD_ADD
42 #define BUFSIZE 1024
44 #define HIGHLIGHT  (0x01)
47 static list_window_t *lw = NULL;
48 static list_window_state_t *lw_state = NULL;
49 static mpdclient_filelist_t *filelist = NULL;
52 /* clear the highlight flag for all items in the filelist */
53 void
54 clear_highlights(mpdclient_filelist_t *filelist)
55 {
56         GList *list = g_list_first(filelist->list);
58         while( list ) {
59                 filelist_entry_t *entry = list->data;
61                 entry->flags &= ~HIGHLIGHT;
62                 list = list->next;
63         }
64 }
66 /* change the highlight flag for a song */
67 void
68 set_highlight(mpdclient_filelist_t *filelist, mpd_Song *song, int highlight)
69 {
70         GList *list = g_list_first(filelist->list);
72         if( !song )
73                 return;
75         while( list ) {
76                 filelist_entry_t *entry = list->data;
77                 mpd_InfoEntity *entity  = entry->entity;
79                 if( entity && entity->type==MPD_INFO_ENTITY_TYPE_SONG ) {
80                         mpd_Song *song2 = entity->info.song;
82                         if( strcmp(song->file, song2->file) == 0 ) {
83                                 if(highlight)
84                                         entry->flags |= HIGHLIGHT;
85                                 else
86                                         entry->flags &= ~HIGHLIGHT;
87                         }
88                 }
89                 list = list->next;
90         }
91 }
93 /* sync highlight flags with playlist */
94 void
95 sync_highlights(mpdclient_t *c, mpdclient_filelist_t *filelist)
96 {
97         GList *list = g_list_first(filelist->list);
99         while(list) {
100                 filelist_entry_t *entry = list->data;
101                 mpd_InfoEntity *entity = entry->entity;
103                 if( entity && entity->type==MPD_INFO_ENTITY_TYPE_SONG ) {
104                         mpd_Song *song = entity->info.song;
106                         if( playlist_get_index_from_file(c, song->file) >= 0 )
107                                 entry->flags |= HIGHLIGHT;
108                         else
109                                 entry->flags &= ~HIGHLIGHT;
110                 }
111                 list=list->next;
112         }
115 /* the db have changed -> update the filelist */
116 static void
117 file_changed_callback(mpdclient_t *c, int event, gpointer data)
119         D("screen_file.c> filelist_callback() [%d]\n", event);
120         filelist = mpdclient_filelist_update(c, filelist);
121         sync_highlights(c, filelist);
122         list_window_check_selected(lw, filelist->length);
125 /* the playlist have been updated -> fix highlights */
126 static void
127 playlist_changed_callback(mpdclient_t *c, int event, gpointer data)
129         D("screen_file.c> playlist_callback() [%d]\n", event);
130         switch(event) {
131         case PLAYLIST_EVENT_CLEAR:
132                 clear_highlights(filelist);
133                 break;
134         case PLAYLIST_EVENT_ADD:
135                 set_highlight(filelist, (mpd_Song *) data, 1);
136                 break;
137         case PLAYLIST_EVENT_DELETE:
138                 set_highlight(filelist, (mpd_Song *) data, 0);
139                 break;
140         case PLAYLIST_EVENT_MOVE:
141                 break;
142         default:
143                 sync_highlights(c, filelist);
144                 break;
145         }
148 /* list_window callback */
149 char *
150 browse_lw_callback(int index, int *highlight, void *data)
152         static char buf[BUFSIZE];
153         mpdclient_filelist_t *filelist = (mpdclient_filelist_t *) data;
154         filelist_entry_t *entry;
155         mpd_InfoEntity *entity;
157         *highlight = 0;
158         if( (entry=(filelist_entry_t *)g_list_nth_data(filelist->list,index))==NULL )
159                 return NULL;
161         entity = entry->entity;
162         *highlight = (entry->flags & HIGHLIGHT);
164         if( entity == NULL )
165                 return "[..]";
167         if( entity->type==MPD_INFO_ENTITY_TYPE_DIRECTORY ) {
168                 mpd_Directory *dir = entity->info.directory;
169                 char *dirname = utf8_to_locale(basename(dir->path));
171                 g_snprintf(buf, BUFSIZE, "[%s]", dirname);
172                 g_free(dirname);
173                 return buf;
174         } else if( entity->type==MPD_INFO_ENTITY_TYPE_SONG ) {
175                 mpd_Song *song = entity->info.song;
177                 strfsong(buf, BUFSIZE, LIST_FORMAT, song);
178                 return buf;
179         } else if( entity->type==MPD_INFO_ENTITY_TYPE_PLAYLISTFILE ) {
180                 mpd_PlaylistFile *plf = entity->info.playlistFile;
181                 char *filename = utf8_to_locale(basename(plf->path));
183 #ifdef USE_OLD_LAYOUT
184                 g_snprintf(buf, BUFSIZE, "*%s*", filename);
185 #else
186                 g_snprintf(buf, BUFSIZE, "<Playlist> %s", filename);
187 #endif
188                 g_free(filename);
189                 return buf;
190         }
192         return "Error: Unknown entry!";
195 /* chdir */
196 static int
197 change_directory(screen_t *screen, mpdclient_t *c, filelist_entry_t *entry,
198                  char *new_path)
200         mpd_InfoEntity *entity = NULL;
201         gchar *path = NULL;
203         if( entry!=NULL )
204                 entity = entry->entity;
205         else if( new_path==NULL )
206                 return -1;
208         if( entity==NULL ) {
209                 if( entry || 0==strcmp(new_path, "..") ) {
210                         /* return to parent */
211                         char *parent = g_path_get_dirname(filelist->path);
212                         if( strcmp(parent, ".") == 0 )
213                                 parent[0] = '\0';
214                         path = g_strdup(parent);
215                         list_window_reset(lw);
216                         /* restore previous list window state */
217                         list_window_pop_state(lw_state,lw);
218                 } else {
219                         /* entry==NULL, then new_path ("" is root) */
220                         path = g_strdup(new_path);
221                         list_window_reset(lw);
222                         /* restore first list window state (pop while returning true) */
223                         while(list_window_pop_state(lw_state,lw));
224                 }
225         } else if( entity->type==MPD_INFO_ENTITY_TYPE_DIRECTORY) {
226                 /* enter sub */
227                 mpd_Directory *dir = entity->info.directory;
228                 path = utf8_to_locale(dir->path);
229                 /* save current list window state */
230                 list_window_push_state(lw_state,lw);
231         } else
232                 return -1;
234         filelist = mpdclient_filelist_free(filelist);
235         filelist = mpdclient_filelist_get(c, path);
236         sync_highlights(c, filelist);
237         list_window_check_selected(lw, filelist->length);
238         g_free(path);
239         return 0;
242 static int
243 load_playlist(screen_t *screen, mpdclient_t *c, filelist_entry_t *entry)
245         mpd_InfoEntity *entity = entry->entity;
246         mpd_PlaylistFile *plf = entity->info.playlistFile;
247         char *filename = utf8_to_locale(plf->path);
249         if( mpdclient_cmd_load_playlist_utf8(c, plf->path) == 0 )
250                 screen_status_printf(_("Loading playlist %s..."), basename(filename));
251         g_free(filename);
252         return 0;
255 static int
256 handle_save(screen_t *screen, mpdclient_t *c)
258         filelist_entry_t *entry;
259         char *defaultname = NULL;
262         entry=( filelist_entry_t *) g_list_nth_data(filelist->list,lw->selected);
263         if( entry && entry->entity ) {
264                 mpd_InfoEntity *entity = entry->entity;
265                 if( entity->type==MPD_INFO_ENTITY_TYPE_PLAYLISTFILE ) {
266                         mpd_PlaylistFile *plf = entity->info.playlistFile;
267                         defaultname = plf->path;
268                 }
269         }
271         return playlist_save(screen, c, NULL, defaultname);
274 static int
275 handle_delete(screen_t *screen, mpdclient_t *c)
277         filelist_entry_t *entry;
278         mpd_InfoEntity *entity;
279         mpd_PlaylistFile *plf;
280         char *str, *buf;
281         int key;
283         entry=( filelist_entry_t *) g_list_nth_data(filelist->list,lw->selected);
284         if( entry==NULL || entry->entity==NULL )
285                 return -1;
287         entity = entry->entity;
289         if( entity->type!=MPD_INFO_ENTITY_TYPE_PLAYLISTFILE ) {
290                 screen_status_printf(_("You can only delete playlists!"));
291                 screen_bell();
292                 return -1;
293         }
295         plf = entity->info.playlistFile;
296         str = utf8_to_locale(basename(plf->path));
297         buf = g_strdup_printf(_("Delete playlist %s [%s/%s] ? "), str, YES, NO);
298         g_free(str);
299         key = tolower(screen_getch(screen->status_window.w, buf));
300         g_free(buf);
301         if( key==KEY_RESIZE )
302                 screen_resize();
303         if( key != YES[0] ) {
304                 screen_status_printf(_("Aborted!"));
305                 return 0;
306         }
308         if( mpdclient_cmd_delete_playlist_utf8(c, plf->path) )
309                 return -1;
311         screen_status_printf(_("Playlist deleted!"));
312         return 0;
315 static int
316 enqueue_and_play(screen_t *screen, mpdclient_t *c, filelist_entry_t *entry)
318         int index;
319         mpd_InfoEntity *entity = entry->entity;
320         mpd_Song *song = entity->info.song;
322         if(!( entry->flags & HIGHLIGHT )) {
323                 if( mpdclient_cmd_add(c, song) == 0 ) {
324                         char buf[BUFSIZE];
326                         entry->flags |= HIGHLIGHT;
327                         strfsong(buf, BUFSIZE, LIST_FORMAT, song);
328                         screen_status_printf(_("Adding \'%s\' to playlist\n"), buf);
329                         mpdclient_update(c); /* get song id */
330                 } else
331                         return -1;
332         }
334         index = playlist_get_index_from_file(c, song->file);
335         mpdclient_cmd_play(c, index);
336         return 0;
339 int
340 browse_handle_enter(screen_t *screen,
341                     mpdclient_t *c,
342                     list_window_t *lw,
343                     mpdclient_filelist_t *filelist)
345         filelist_entry_t *entry;
346         mpd_InfoEntity *entity;
348         if ( filelist==NULL )
349                 return -1;
350         entry = ( filelist_entry_t *) g_list_nth_data(filelist->list, lw->selected);
351         if( entry==NULL )
352                 return -1;
354         entity = entry->entity;
355         if( entity==NULL || entity->type==MPD_INFO_ENTITY_TYPE_DIRECTORY )
356                 return change_directory(screen, c, entry, NULL);
357         else if( entity->type==MPD_INFO_ENTITY_TYPE_PLAYLISTFILE )
358                 return load_playlist(screen, c, entry);
359         else if( entity->type==MPD_INFO_ENTITY_TYPE_SONG )
360                 return enqueue_and_play(screen, c, entry);
361         return -1;
365 #ifdef USE_OLD_ADD
366 /* NOTE - The add_directory functions should move to mpdclient.c */
367 extern gint mpdclient_finish_command(mpdclient_t *c);
369 static int
370 add_directory(mpdclient_t *c, char *dir)
372         mpd_InfoEntity *entity;
373         GList *subdir_list = NULL;
374         GList *list = NULL;
375         char *dirname;
377         dirname = utf8_to_locale(dir);
378         screen_status_printf(_("Adding directory %s...\n"), dirname);
379         doupdate();
380         g_free(dirname);
381         dirname = NULL;
383         mpd_sendLsInfoCommand(c->connection, dir);
384         mpd_sendCommandListBegin(c->connection);
385         while( (entity=mpd_getNextInfoEntity(c->connection)) ) {
386                 if( entity->type==MPD_INFO_ENTITY_TYPE_SONG ) {
387                         mpd_Song *song = entity->info.song;
388                         mpd_sendAddCommand(c->connection, song->file);
389                         mpd_freeInfoEntity(entity);
390                 } else if( entity->type==MPD_INFO_ENTITY_TYPE_DIRECTORY ) {
391                         subdir_list = g_list_append(subdir_list, (gpointer) entity);
392                 } else
393                         mpd_freeInfoEntity(entity);
394         }
395         mpd_sendCommandListEnd(c->connection);
396         mpdclient_finish_command(c);
397         c->need_update = TRUE;
399         list = g_list_first(subdir_list);
400         while( list!=NULL ) {
401                 mpd_Directory *dir;
403                 entity = list->data;
404                 dir = entity->info.directory;
405                 add_directory(c, dir->path);
406                 mpd_freeInfoEntity(entity);
407                 list->data=NULL;
408                 list=list->next;
409         }
410         g_list_free(subdir_list);
411         return 0;
413 #endif
415 int
416 browse_handle_select(screen_t *screen,
417                      mpdclient_t *c,
418                      list_window_t *lw,
419                      mpdclient_filelist_t *filelist)
421         filelist_entry_t *entry;
423         if ( filelist==NULL )
424                 return -1;
425         entry=( filelist_entry_t *) g_list_nth_data(filelist->list, lw->selected);
426         if( entry==NULL || entry->entity==NULL)
427                 return -1;
429         if( entry->entity->type==MPD_INFO_ENTITY_TYPE_PLAYLISTFILE )
430                 return load_playlist(screen, c, entry);
432         if( entry->entity->type==MPD_INFO_ENTITY_TYPE_DIRECTORY ) {
433                 mpd_Directory *dir = entry->entity->info.directory;
434 #ifdef USE_OLD_ADD
435                 add_directory(c, tmp);
436 #else
437                 if( mpdclient_cmd_add_path_utf8(c, dir->path) == 0 ) {
438                         char *tmp = utf8_to_locale(dir->path);
440                         screen_status_printf(_("Adding \'%s\' to playlist\n"), tmp);
441                         g_free(tmp);
442                 }
443 #endif
444                 return 0;
445         }
447         if( entry->entity->type!=MPD_INFO_ENTITY_TYPE_SONG )
448                 return -1;
450         if( entry->flags & HIGHLIGHT )
451                 entry->flags &= ~HIGHLIGHT;
452         else
453                 entry->flags |= HIGHLIGHT;
455         if( entry->flags & HIGHLIGHT ) {
456                 if( entry->entity->type==MPD_INFO_ENTITY_TYPE_SONG ) {
457                         mpd_Song *song = entry->entity->info.song;
459                         if( mpdclient_cmd_add(c, song) == 0 ) {
460                                 char buf[BUFSIZE];
462                                 strfsong(buf, BUFSIZE, LIST_FORMAT, song);
463                                 screen_status_printf(_("Adding \'%s\' to playlist\n"), buf);
464                         }
465                 }
466         } else {
467                 /* remove song from playlist */
468                 if( entry->entity->type==MPD_INFO_ENTITY_TYPE_SONG ) {
469                         mpd_Song *song = entry->entity->info.song;
471                         if( song ) {
472                                 int index = playlist_get_index_from_file(c, song->file);
474                                 while( (index=playlist_get_index_from_file(c, song->file))>=0 )
475                                         mpdclient_cmd_delete(c, index);
476                         }
477                 }
478         }
479         return 0;
482 int
483 browse_handle_select_all (screen_t *screen,
484                           mpdclient_t *c,
485                           list_window_t *lw,
486                           mpdclient_filelist_t *filelist)
488         filelist_entry_t *entry;
489         GList *temp = filelist->list;
491         if ( filelist==NULL )
492                 return -1;
493         for (filelist->list = g_list_first(filelist->list);
494              filelist->list;
495              filelist->list = g_list_next(filelist->list)) {
496                 entry=( filelist_entry_t *) filelist->list->data;
497                 if( entry==NULL || entry->entity==NULL)
498                         return -1;
500                 if( entry->entity->type==MPD_INFO_ENTITY_TYPE_PLAYLISTFILE )
501                         load_playlist(screen, c, entry);
503                 if( entry->entity->type==MPD_INFO_ENTITY_TYPE_DIRECTORY ) {
504                         mpd_Directory *dir = entry->entity->info.directory;
505 #ifdef USE_OLD_ADD
506                         add_directory(c, tmp);
507 #else
508                         if (mpdclient_cmd_add_path_utf8(c, dir->path) == 0) {
509                                 char *tmp = utf8_to_locale(dir->path);
511                                 screen_status_printf(_("Adding \'%s\' to playlist\n"), tmp);
512                                 g_free(tmp);
513                         }
514 #endif
515                 }
517                 if( entry->entity->type!=MPD_INFO_ENTITY_TYPE_SONG )
518                         continue;
520                 entry->flags |= HIGHLIGHT;
522                 if( entry->flags & HIGHLIGHT ) {
523                         if( entry->entity->type==MPD_INFO_ENTITY_TYPE_SONG ) {
524                                 mpd_Song *song = entry->entity->info.song;
526                                 if( mpdclient_cmd_add(c, song) == 0 ) {
527                                         char buf[BUFSIZE];
529                                         strfsong(buf, BUFSIZE, LIST_FORMAT, song);
530                                         screen_status_printf(_("Adding \'%s\' to playlist\n"), buf);
531                                 }
532                         }
533                 }
534                 /*
535                 else {
536                         //remove song from playlist
537                         if( entry->entity->type==MPD_INFO_ENTITY_TYPE_SONG ) {
538                                 mpd_Song *song = entry->entity->info.song;
540                                 if( song ) {
541                                         int index = playlist_get_index_from_file(c, song->file);
543                                         while( (index=playlist_get_index_from_file(c, song->file))>=0 )
544                                                 mpdclient_cmd_delete(c, index);
545                                 }
546                         }
547                 }
548                 */
549                 return 0;
550         }
552         filelist->list = temp;
553         return 0;
556 static void
557 browse_init(WINDOW *w, int cols, int rows)
559         lw = list_window_init(w, cols, rows);
560         lw_state = list_window_init_state();
563 static void
564 browse_resize(int cols, int rows)
566         lw->cols = cols;
567         lw->rows = rows;
570 static void
571 browse_exit(void)
573         if( filelist )
574                 filelist = mpdclient_filelist_free(filelist);
575         lw = list_window_free(lw);
576         lw_state = list_window_free_state(lw_state);
579 static void
580 browse_open(screen_t *screen, mpdclient_t *c)
582         if( filelist == NULL ) {
583                 filelist = mpdclient_filelist_get(c, "");
584                 mpdclient_install_playlist_callback(c, playlist_changed_callback);
585                 mpdclient_install_browse_callback(c, file_changed_callback);
586         }
589 static void
590 browse_close(void)
594 static char *
595 browse_title(char *str, size_t size)
597         char *pathcopy;
598         char *parentdir;
600         pathcopy = strdup(filelist->path);
601         parentdir = dirname(pathcopy);
602         parentdir = basename(parentdir);
604         if( parentdir[0] == '.' && strlen(parentdir) == 1 ) {
605                 parentdir = NULL;
606         }
608         g_snprintf(str, size, _("Browse: %s%s%s"),
609                    parentdir ? parentdir : "",
610                    parentdir ? "/" : "",
611                    basename(filelist->path));
612         free(pathcopy);
613         return str;
616 static void
617 browse_paint(screen_t *screen, mpdclient_t *c)
619         lw->clear = 1;
621         list_window_paint(lw, browse_lw_callback, (void *) filelist);
622         wnoutrefresh(lw->w);
625 static void
626 browse_update(screen_t *screen, mpdclient_t *c)
628         if( filelist->updated ) {
629                 browse_paint(screen, c);
630                 filelist->updated = FALSE;
631                 return;
632         }
634         list_window_paint(lw, browse_lw_callback, (void *) filelist);
635         wnoutrefresh(lw->w);
639 #ifdef HAVE_GETMOUSE
640 int
641 browse_handle_mouse_event(screen_t *screen,
642                           mpdclient_t *c,
643                           list_window_t *lw,
644                           mpdclient_filelist_t *filelist)
646         int row;
647         int prev_selected = lw->selected;
648         unsigned long bstate;
649         int length;
651         if ( filelist )
652                 length = filelist->length;
653         else
654                 length = 0;
656         if( screen_get_mouse_event(c, lw, length, &bstate, &row) )
657                 return 1;
659         lw->selected = lw->start+row;
660         list_window_check_selected(lw, length);
662         if( bstate & BUTTON1_CLICKED ) {
663                 if( prev_selected == lw->selected )
664                         browse_handle_enter(screen, c, lw, filelist);
665         } else if( bstate & BUTTON3_CLICKED ) {
666                 if( prev_selected == lw->selected )
667                         browse_handle_select(screen, c, lw, filelist);
668         }
670         return 1;
672 #endif
674 static int
675 browse_cmd(screen_t *screen, mpdclient_t *c, command_t cmd)
677         switch(cmd)
678                 {
679                 case CMD_PLAY:
680                         browse_handle_enter(screen, c, lw, filelist);
681                         return 1;
682                 case CMD_GO_ROOT_DIRECTORY:
683                         return change_directory(screen, c, NULL, "");
684                         break;
685                 case CMD_GO_PARENT_DIRECTORY:
686                         return change_directory(screen, c, NULL, "..");
687                         break;
688                 case CMD_SELECT:
689                         if( browse_handle_select(screen, c, lw, filelist) == 0 )
690                                 {
691                                         /* continue and select next item... */
692                                         cmd = CMD_LIST_NEXT;
693                                 }
694                         break;
695                 case CMD_DELETE:
696                         handle_delete(screen, c);
697                         break;
698                 case CMD_SAVE_PLAYLIST:
699                         handle_save(screen, c);
700                         break;
701                 case CMD_SCREEN_UPDATE:
702                         screen->painted = 0;
703                         lw->clear = 1;
704                         lw->repaint = 1;
705                         filelist = mpdclient_filelist_update(c, filelist);
706                         list_window_check_selected(lw, filelist->length);
707                         screen_status_printf(_("Screen updated!"));
708                         return 1;
709                 case CMD_DB_UPDATE:
710                         if( !c->status->updatingDb )
711                                 {
712                                         if( mpdclient_cmd_db_update_utf8(c,filelist->path)==0 )
713                                                 {
714                                                         if(strcmp(filelist->path,"")) {
715                                                                 screen_status_printf(_("Database update of %s started!"),
716                                                                                      filelist->path);
717                                                         } else {
718                                                                 screen_status_printf(_("Database update started!"));
719                                                         }
720                                                         /* set updatingDb to make shure the browse callback gets called
721                                                          * even if the updated has finished before status is updated */
722                                                         c->status->updatingDb = 1;
723                                                 }
724                                 }
725                         else
726                                 screen_status_printf(_("Database update running..."));
727                         return 1;
728                 case CMD_LIST_FIND:
729                 case CMD_LIST_RFIND:
730                 case CMD_LIST_FIND_NEXT:
731                 case CMD_LIST_RFIND_NEXT:
732                         return screen_find(screen,
733                                            lw, filelist->length,
734                                            cmd, browse_lw_callback, (void *) filelist);
735                 case CMD_MOUSE_EVENT:
736                         return browse_handle_mouse_event(screen,c,lw,filelist);
737                 default:
738                         break;
739                 }
740         return list_window_cmd(lw, filelist->length, cmd);
743 list_window_t *
744 get_filelist_window()
746         return lw;
749 screen_functions_t *
750 get_screen_browse(void)
752         static screen_functions_t functions;
754         memset(&functions, 0, sizeof(screen_functions_t));
755         functions.init = browse_init;
756         functions.exit = browse_exit;
757         functions.open = browse_open;
758         functions.close = browse_close;
759         functions.resize = browse_resize;
760         functions.paint = browse_paint;
761         functions.update = browse_update;
762         functions.cmd = browse_cmd;
763         functions.get_lw = get_filelist_window;
764         functions.get_title = browse_title;
766         return &functions;