aboutsummaryrefslogtreecommitdiff
path: root/playlist
diff options
context:
space:
mode:
Diffstat (limited to 'playlist')
-rw-r--r--playlist/CHANGES.LOG12
-rw-r--r--playlist/INSTALL15
-rw-r--r--playlist/README20
-rw-r--r--playlist/help-playlist.pd37
-rw-r--r--playlist/playlist.c748
-rw-r--r--playlist/playlist.tk89
-rw-r--r--playlist/xmms.pd144
7 files changed, 1065 insertions, 0 deletions
diff --git a/playlist/CHANGES.LOG b/playlist/CHANGES.LOG
new file mode 100644
index 0000000..1b8b586
--- /dev/null
+++ b/playlist/CHANGES.LOG
@@ -0,0 +1,12 @@
+0.6
+ added properties menu
+0.5
+ handle location messages with wrong argument
+0.4
+ added location message + fix memory allocation problems
+0.3
+ fixed crash when list is empty
+0.2
+ fixed extension saving ( all ---> ll )
+0.1
+ first implementation
diff --git a/playlist/INSTALL b/playlist/INSTALL
new file mode 100644
index 0000000..972bba0
--- /dev/null
+++ b/playlist/INSTALL
@@ -0,0 +1,15 @@
+untar in /my/pd/dir/externs
+
+cd /my/pd/dir/externs/playlist
+
+make clean
+
+make
+
+make install
+
+open help-playlist.pd
+
+Thanx for getting here.
+Yves/
+comments and bugs @ ydegoyon@free.fr
diff --git a/playlist/README b/playlist/README
new file mode 100644
index 0000000..dff1892
--- /dev/null
+++ b/playlist/README
@@ -0,0 +1,20 @@
+Version 0.01
+copyleft 2001 by Yves Degoyon
+tarballs and updates available @ http://ydegoyon.free.fr
+
+playlist : choose a file in 1 click with space, numeric, ... characters
+you can also send a seek message to select a file automatically.
+
+To install playlist, follow the steps from INSTALL
+
+This software is published under GPL terms.
+
+This is software with ABSOLUTELY NO WARRANTY.
+Use it at your OWN RISK. It's possible to damage e.g. hardware or your hearing
+due to a bug or for other reasons.
+We do not warrant that the program is free of infringement of any third-party
+patents.
+
+*****************************************************************************
+
+
diff --git a/playlist/help-playlist.pd b/playlist/help-playlist.pd
new file mode 100644
index 0000000..f399ac3
--- /dev/null
+++ b/playlist/help-playlist.pd
@@ -0,0 +1,37 @@
+#N canvas 94 79 817 563 10;
+#X text 28 57 Constructor : playlist <extension> <width> <height>;
+#X text 88 95 playlist all 200 200;
+#X text 88 107 playlist mp3 200 200;
+#X text 30 78 Examples :;
+#X text 80 454 Comments and bugs @ ydegoyon@free.fr;
+#X symbolatom 292 399 50 0 0;
+#X text 543 284 Note : you can scroll the list by dragging;
+#X text 541 295 the mouse in the yellow area;
+#X text 543 306 You can select and scroll in the blue area;
+#X obj 292 181 playlist all 200 200;
+#X msg 298 108 seek \$1;
+#X text 403 90 You can seek a file in the list;
+#X text 402 101 It will skip the first two items ( '.' and '..');
+#X text 402 113 and behave like a round list;
+#X text 28 14 Playlist lets you switch files in one click;
+#X text 27 25 It's handling most non-alpha characters gracefully;
+#X floatatom 352 109 5 0 0;
+#X symbolatom 333 426 50 0 0;
+#X symbolatom 379 453 50 0 0;
+#X msg 151 163 location ..;
+#X msg 150 140 location /tmp;
+#X text 60 211 You can change change location;
+#X text 60 221 with a message.;
+#X text 60 233 Thus \, you can initialize the object;
+#X text 59 242 in a particular folder.;
+#X msg 151 185 location patches;
+#X obj 87 140 loadbang;
+#X connect 9 0 5 0;
+#X connect 9 1 17 0;
+#X connect 9 2 18 0;
+#X connect 10 0 9 0;
+#X connect 16 0 10 0;
+#X connect 19 0 9 0;
+#X connect 20 0 9 0;
+#X connect 25 0 9 0;
+#X connect 26 0 20 0;
diff --git a/playlist/playlist.c b/playlist/playlist.c
new file mode 100644
index 0000000..557646f
--- /dev/null
+++ b/playlist/playlist.c
@@ -0,0 +1,748 @@
+/*------------------------ playlist~ ------------------------------------------ */
+/* */
+/* playlist~ : lets you choose a file with 1 click */
+/* or by sending a 'seek #' message */
+/* constructor : playlist <extension> <width> <height> */
+/* */
+/* Copyleft Yves Degoyon ( ydegoyon@free.fr ) */
+/* */
+/* This program is free software; you can redistribute it and/or */
+/* modify it under the terms of the GNU General Public License */
+/* as published by the Free Software Foundation; either version 2 */
+/* of the License, or (at your option) any later version. */
+/* */
+/* See file LICENSE for further informations on licensing terms. */
+/* */
+/* This program is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program; if not, write to the Free Software */
+/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+/* */
+/* Based on PureData by Miller Puckette and others. */
+/* */
+/* "If a man's made of blood and iron" */
+/* "Doctor, doctor, what's in my chest ????" */
+/* Gang Of Four -- Guns Before Butter */
+/* ---------------------------------------------------------------------------- */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <math.h>
+#include <ctype.h>
+#include <regex.h>
+#include "m_imp.h"
+#include "g_canvas.h"
+#include "t_tk.h"
+
+#ifdef NT
+#include <io.h>
+#else
+#include <unistd.h>
+#include <sys/types.h>
+#include <dirent.h>
+#endif
+
+t_widgetbehavior playlist_widgetbehavior;
+static t_class *playlist_class;
+
+static int guidebug=0;
+
+static char *playlist_version = "playlist: 1 click file chooser : version 0.6, written by Yves Degoyon (ydegoyon@free.fr)";
+
+#define CHAR_WIDTH 6 // average character width with font Helvetica 8
+#define MAX_DIR_LENGTH 2048 // maximum length for a directory name
+
+#define MIN(a,b) (a>b?b:a)
+
+#define SYS_VGUI2(a,b) if (guidebug) \
+ post(a,b);\
+ sys_vgui(a,b)
+
+#define SYS_VGUI3(a,b,c) if (guidebug) \
+ post(a,b,c);\
+ sys_vgui(a,b,c)
+
+#define SYS_VGUI4(a,b,c,d) if (guidebug) \
+ post(a,b,c,d);\
+ sys_vgui(a,b,c,d)
+
+#define SYS_VGUI5(a,b,c,d,e) if (guidebug) \
+ post(a,b,c,d,e);\
+ sys_vgui(a,b,c,d,e)
+
+#define SYS_VGUI6(a,b,c,d,e,f) if (guidebug) \
+ post(a,b,c,d,e,f);\
+ sys_vgui(a,b,c,d,e,f)
+
+#define SYS_VGUI7(a,b,c,d,e,f,g) if (guidebug) \
+ post(a,b,c,d,e,f,g);\
+ sys_vgui(a,b,c,d,e,f,g)
+
+#define SYS_VGUI8(a,b,c,d,e,f,g,h) if (guidebug) \
+ post(a,b,c,d,e,f,g,h);\
+ sys_vgui(a,b,c,d,e,f,g,h)
+
+#define SYS_VGUI9(a,b,c,d,e,f,g,h,i) if (guidebug) \
+ post(a,b,c,d,e,f,g,h,i );\
+ sys_vgui(a,b,c,d,e,f,g,h,i)
+
+
+typedef struct _playlist
+{
+ t_object x_obj;
+ t_glist *x_glist;
+ t_outlet *x_fullpath;
+ t_outlet *x_file;
+ t_outlet *x_dir;
+ char *x_extension; /* extension to selected files */
+ t_int x_height; /* height of the playlist */
+ t_int x_width; /* width of the playlist */
+ t_int x_itemselected; /* index of the selected item */
+ t_int x_selected; /* stores selected state */
+ char **x_dentries; /* directory entries */
+ t_int x_nentries; /* number of entries in the current dir */
+ t_int x_pnentries; /* previous size of entries list */
+ t_int x_firstseen; /* first displayed entry */
+ t_int x_lastseen; /* last displayed entry */
+ t_int x_cdy; /* cumulated y drag */
+ char *x_curdir; /* current directory informations */
+} t_playlist;
+
+
+static void playlist_update_dir(t_playlist *x, t_glist *glist)
+{
+ t_canvas *canvas=glist_getcanvas(glist);
+ t_int i;
+ char wrappedname[ MAX_DIR_LENGTH ];
+
+ // set title
+ SYS_VGUI3(".x%x.c delete %xTITLE\n", glist_getcanvas(glist), x);
+ SYS_VGUI7(".x%x.c create text %d %d -width %d -text \"%s\" \
+ -anchor w -font {Helvetica 8 bold} -tags %xTITLE\n",
+ canvas,
+ x->x_obj.te_xpix+5,
+ x->x_obj.te_ypix-10,
+ x->x_width,
+ x->x_curdir,
+ x );
+
+ // delete previous entries
+ for ( i=x->x_firstseen; i<=x->x_lastseen; i++ )
+ {
+ SYS_VGUI4(".x%x.c delete %xENTRY%d\n", glist_getcanvas(glist), x, i);
+ }
+
+ // display the content of current directory
+ {
+ t_int nentries, i;
+ struct dirent** dentries; /* all directory entries */
+
+ // post( "playlist : scandir : %s", x->x_curdir );
+ if ( ( nentries = scandir(x->x_curdir, &dentries, NULL, alphasort ) ) == -1 )
+ {
+ post( "playlist : could not scan current directory ( where the hell are you ??? )" );
+ perror( "scandir" );
+ return;
+ }
+
+ x->x_firstseen = 0;
+ if ( x->x_dentries )
+ {
+ for ( i=0; i<x->x_nentries; i++ )
+ {
+ // post( "playlist : freeing entry %d size=%d : %s", i, strlen( x->x_dentries[i] ) + 1, x->x_dentries[i] );
+ freebytes( x->x_dentries[i], strlen( x->x_dentries[i] ) + 1 );
+ }
+ }
+ if ( x->x_pnentries != -1 )
+ {
+ freebytes( x->x_dentries, x->x_pnentries*sizeof(char**) );
+ }
+
+ x->x_nentries = 0;
+ // post( "playlist : allocating dentries %d", nentries );
+ x->x_dentries = (char **) getbytes( nentries*sizeof(char**) ) ;
+ x->x_pnentries = nentries;
+ for ( i=0; i<nentries; i++ )
+ {
+ size_t nmatches = 0;
+ regmatch_t matchinfos[1];
+ DIR* tmpdir;
+
+ // ckeck if that entry should be displayed
+ if ( ( ( ( tmpdir = opendir( dentries[i]->d_name ) ) != NULL ) ) ||
+ ( strstr( dentries[i]->d_name, x->x_extension ) ) ||
+ ( !strcmp( x->x_extension, "all" ) )
+ )
+ {
+ // close temporarily opened dir
+ if ( tmpdir )
+ {
+ if ( closedir( tmpdir ) < 0 )
+ {
+ post( "playlist : could not close directory %s", dentries[i]->d_name );
+ }
+ }
+
+ // post( "playlist : allocating entry %d %d : %s", x->x_nentries, strlen( dentries[i]->d_name ) + 1, dentries[i]->d_name );
+ x->x_dentries[x->x_nentries] = ( char * ) getbytes( strlen( dentries[i]->d_name ) + 1 );
+ strcpy( x->x_dentries[x->x_nentries], dentries[i]->d_name );
+
+ // display the entry if displayable
+ if ( x->x_nentries*10+5 < x->x_height )
+ {
+ x->x_lastseen = x->x_nentries;
+ strncpy( wrappedname, x->x_dentries[x->x_nentries], MIN(x->x_width/CHAR_WIDTH, MAX_DIR_LENGTH) );
+ wrappedname[ x->x_width/CHAR_WIDTH ] = '\0';
+ SYS_VGUI8(".x%x.c create text %d %d -fill #000000 -activefill #FF0000 -width %d -text \"%s\" \
+ -anchor w -font {Helvetica 8 bold} -tags %xENTRY%d\n",
+ canvas,
+ x->x_obj.te_xpix+5,
+ x->x_obj.te_ypix+5+(x->x_nentries-x->x_firstseen)*10,
+ x->x_width,
+ wrappedname,
+ x, x->x_nentries );
+ }
+ x->x_nentries++;
+ }
+
+ }
+
+ }
+}
+
+void playlist_output_current(t_playlist* x)
+{
+ // output the selected dir+file
+ if ( x->x_dentries && x->x_itemselected < x->x_nentries && x->x_itemselected >= 0 )
+ {
+ char* tmpstring = (char*) getbytes( strlen( x->x_curdir ) + strlen( x->x_dentries[x->x_itemselected]) + 2 );
+
+ sprintf( tmpstring, "%s/%s", x->x_curdir, x->x_dentries[x->x_itemselected] );
+ outlet_symbol( x->x_dir, gensym( x->x_curdir ) );
+ outlet_symbol( x->x_file, gensym( x->x_dentries[x->x_itemselected] ) );
+ outlet_symbol( x->x_fullpath, gensym( tmpstring ) );
+ }
+}
+
+void playlist_draw_new(t_playlist *x, t_glist *glist)
+{
+ t_canvas *canvas=glist_getcanvas(glist);
+
+ SYS_VGUI7(".x%x.c create rectangle %d %d %d %d -fill #457782 -tags %xPLAYLIST\n",
+ canvas, x->x_obj.te_xpix, x->x_obj.te_ypix,
+ x->x_obj.te_xpix + x->x_width, x->x_obj.te_ypix + x->x_height,
+ x);
+ SYS_VGUI7(".x%x.c create rectangle %d %d %d %d -fill yellow -tags %xSCROLLLIST\n",
+ canvas, x->x_obj.te_xpix+4*x->x_width/5, x->x_obj.te_ypix,
+ x->x_obj.te_xpix + x->x_width, x->x_obj.te_ypix + x->x_height,
+ x);
+
+ playlist_update_dir( x, glist );
+
+}
+
+void playlist_draw_move(t_playlist *x, t_glist *glist)
+{
+ t_canvas *canvas=glist_getcanvas(glist);
+ t_int i;
+
+ SYS_VGUI7(".x%x.c coords %xPLAYLIST %d %d %d %d\n",
+ canvas, x,
+ x->x_obj.te_xpix, x->x_obj.te_ypix,
+ x->x_obj.te_xpix+x->x_width,
+ x->x_obj.te_ypix+x->x_height);
+ SYS_VGUI7(".x%x.c coords %xSCROLLLIST %d %d %d %d\n",
+ canvas, x,
+ x->x_obj.te_xpix+4*x->x_width/5, x->x_obj.te_ypix,
+ x->x_obj.te_xpix+x->x_width,
+ x->x_obj.te_ypix+x->x_height);
+ SYS_VGUI5(".x%x.c coords %xTITLE %d %d\n",
+ canvas, x,
+ x->x_obj.te_xpix+5, x->x_obj.te_ypix-10 );
+ for ( i=x->x_firstseen; i<=x->x_lastseen; i++ )
+ {
+ SYS_VGUI6(".x%x.c coords %xENTRY%d %d %d\n",
+ canvas, x, i,
+ x->x_obj.te_xpix+5,
+ x->x_obj.te_ypix+5+(i-x->x_firstseen)*10);
+ }
+
+ canvas_fixlinesfor( canvas, (t_text*)x );
+}
+
+void playlist_draw_erase(t_playlist* x, t_glist* glist)
+{
+ t_canvas *canvas=glist_getcanvas(glist);
+ int i;
+
+ SYS_VGUI3(".x%x.c delete %xPLAYLIST\n", canvas, x);
+ SYS_VGUI3(".x%x.c delete %xSCROLLLIST\n", canvas, x);
+ SYS_VGUI3(".x%x.c delete %xTITLE\n", canvas, x);
+ for ( i=x->x_firstseen; i<=x->x_lastseen; i++ )
+ {
+ SYS_VGUI4(".x%x.c delete %xENTRY%d\n", glist_getcanvas(glist), x, i);
+ }
+}
+
+void playlist_draw_select(t_playlist* x, t_glist* glist)
+{
+ t_canvas *canvas=glist_getcanvas(glist);
+
+ // post( "playlist : select" );
+ if(x->x_selected)
+ {
+ /* sets the item in blue */
+ SYS_VGUI3(".x%x.c itemconfigure %xPLAYLIST -outline #0000FF\n", canvas, x);
+ }
+ else
+ {
+ SYS_VGUI3(".x%x.c itemconfigure %xPLAYLIST -outline #000000\n", canvas, x);
+ }
+}
+
+/* ------------------------ playlist widgetbehaviour----------------------------- */
+
+
+void playlist_getrect(t_gobj *z, t_glist *owner,
+ int *xp1, int *yp1, int *xp2, int *yp2)
+{
+ t_playlist* x = (t_playlist*)z;
+
+ *xp1 = x->x_obj.te_xpix;
+ *yp1 = x->x_obj.te_ypix;
+ *xp2 = x->x_obj.te_xpix+x->x_width;
+ *yp2 = x->x_obj.te_ypix+x->x_height;
+}
+
+void playlist_save(t_gobj *z, t_binbuf *b)
+{
+ t_playlist *x = (t_playlist *)z;
+
+ // post( "saving playlist : %s", x->x_extension );
+ binbuf_addv(b, "ssiissii", gensym("#X"),gensym("obj"),
+ (t_int)x->x_obj.te_xpix, (t_int)x->x_obj.te_ypix,
+ gensym("playlist"), gensym(x->x_extension), x->x_width, x->x_height );
+ binbuf_addv(b, ";");
+}
+
+void playlist_select(t_gobj *z, t_glist *glist, int selected)
+{
+ t_playlist *x = (t_playlist *)z;
+
+ x->x_selected = selected;
+
+ playlist_draw_select( x, glist );
+}
+
+void playlist_vis(t_gobj *z, t_glist *glist, int vis)
+{
+ t_playlist *x = (t_playlist *)z;
+ t_rtext *y;
+
+ if (vis)
+ {
+ playlist_draw_new( x, glist );
+ }
+ else
+ {
+ playlist_draw_erase( x, glist );
+ }
+}
+
+void playlist_delete(t_gobj *z, t_glist *glist)
+{
+ canvas_deletelinesfor( glist_getcanvas(glist), (t_text *)z);
+}
+
+void playlist_displace(t_gobj *z, t_glist *glist, int dx, int dy)
+{
+ t_playlist *x = (t_playlist *)z;
+ int xold = x->x_obj.te_xpix;
+ int yold = x->x_obj.te_ypix;
+
+ // post( "playlist_displace dx=%d dy=%d", dx, dy );
+
+ x->x_obj.te_xpix += dx;
+ x->x_obj.te_ypix += dy;
+ if(xold != x->x_obj.te_xpix || yold != x->x_obj.te_ypix)
+ {
+ playlist_draw_move(x, glist);
+ }
+}
+
+void playlist_motion(t_playlist *x, t_floatarg dx, t_floatarg dy)
+{
+ int i;
+
+ x->x_cdy+=dy;
+
+ // check if we need to scroll
+ if ( ( x->x_lastseen < x->x_nentries ) )
+ {
+ // eventually, move down
+ if ( ( x->x_cdy >= 3 ) &&
+ ( x->x_firstseen < x->x_nentries - ( x->x_height/10 ) ) )
+ {
+ x->x_cdy = 0;
+ if ( x->x_firstseen + 1 < x->x_nentries )
+ {
+ for ( i=x->x_firstseen; i<=x->x_lastseen; i++ )
+ {
+ SYS_VGUI4(".x%x.c delete %xENTRY%d\n", x->x_glist, x, i);
+ }
+ x->x_firstseen++;
+ for ( i=x->x_firstseen; i< x->x_nentries; i++ )
+ {
+ char *wrappedname = (char *) getbytes( x->x_width );
+
+ if ( (i-x->x_firstseen)*10+5 < x->x_height )
+ {
+ x->x_lastseen = i;
+ strncpy( wrappedname, x->x_dentries[i], x->x_width/CHAR_WIDTH );
+ wrappedname[ x->x_width/CHAR_WIDTH ] = '\0';
+ SYS_VGUI8(".x%x.c create text %d %d -fill #000000 -activefill #FF0000 -width %d -text \"%s\" \
+ -anchor w -font {Helvetica 8 bold} -tags %xENTRY%d\n",
+ glist_getcanvas(x->x_glist),
+ x->x_obj.te_xpix+5,
+ x->x_obj.te_ypix+5+(i-x->x_firstseen)*10,
+ x->x_width,
+ wrappedname,
+ x, i );
+ }
+ else break;
+ }
+ SYS_VGUI4(".x%x.c itemconfigure %xENTRY%d -fill #FF0000\n", x->x_glist, x, x->x_itemselected);
+ // post( "playlist : moved down first=%d last=%d", x->x_firstseen, x->x_lastseen );
+ }
+ }
+ // eventually, move up
+ if ( ( x->x_cdy <= -3 ) &&
+ ( x->x_lastseen >= ( x->x_height/10 ) ) )
+ {
+ x->x_cdy = 0;
+ if ( x->x_firstseen - 1 >= 0 )
+ {
+ for ( i=x->x_firstseen; i<=x->x_lastseen; i++ )
+ {
+ SYS_VGUI4(".x%x.c delete %xENTRY%d\n", x->x_glist, x, i);
+ }
+ x->x_firstseen--;
+ for ( i=x->x_firstseen; i< x->x_nentries; i++ )
+ {
+ char *wrappedname = (char *) getbytes( x->x_width );
+
+ if ( (i-x->x_firstseen)*10+5 < x->x_height )
+ {
+ x->x_lastseen = i;
+ strncpy( wrappedname, x->x_dentries[i], x->x_width/CHAR_WIDTH );
+ wrappedname[ x->x_width/CHAR_WIDTH ] = '\0';
+ SYS_VGUI8(".x%x.c create text %d %d -fill #000000 -activefill #FF0000 -width %d -text \"%s\" \
+ -anchor w -font {Helvetica 8 bold} -tags %xENTRY%d\n",
+ glist_getcanvas(x->x_glist),
+ x->x_obj.te_xpix+5,
+ x->x_obj.te_ypix+5+(i-x->x_firstseen)*10,
+ x->x_width,
+ wrappedname,
+ x, i );
+ }
+ else break;
+ }
+ SYS_VGUI4(".x%x.c itemconfigure %xENTRY%d -fill #FF0000\n", x->x_glist, x, x->x_itemselected);
+ // post( "playlist : moved up first=%d last=%d", x->x_firstseen, x->x_lastseen );
+ }
+ }
+ } // scroll test
+}
+
+
+int playlist_click(t_gobj *z, struct _glist *glist,
+ int xpix, int ypix, int shift, int alt, int dbl, int doit)
+{
+ t_playlist* x = (t_playlist *)z;
+
+ if (doit)
+ {
+ // leave a margin for scrolling without selection
+ if ( (xpix-x->x_obj.te_xpix) < 4*x->x_width/5 )
+ {
+ // deselect previously selected item
+ SYS_VGUI4(".x%x.c itemconfigure %xENTRY%d -fill #000000\n", x->x_glist, x, x->x_itemselected);
+ x->x_itemselected = x->x_firstseen + (ypix-x->x_obj.te_ypix)/10;
+ SYS_VGUI4(".x%x.c itemconfigure %xENTRY%d -fill #FF0000\n", x->x_glist, x, x->x_itemselected);
+ // post( "playlist : selected item : %d", x->x_itemselected );
+ if ( x->x_dentries && ( x->x_itemselected < x->x_nentries ) )
+ {
+ char *tmpstring = (char *) getbytes( strlen( x->x_curdir ) + strlen( x->x_dentries[x->x_itemselected] ) + 2 );
+ sprintf( tmpstring, "%s/%s", x->x_curdir, x->x_dentries[x->x_itemselected] );
+ // post( "playlist : chdir : %s", tmpstring );
+ if ( chdir( tmpstring ) < 0 )
+ {
+ playlist_output_current(x);
+ }
+ else
+ {
+ if ( !strcmp( x->x_dentries[ x->x_itemselected ], ".." ) )
+ {
+ char *iamthelastslash;
+
+ iamthelastslash = strrchr( x->x_curdir, '/' );
+ *iamthelastslash = '\0';
+
+ if ( !strcmp( x->x_curdir, "" ) )
+ {
+ strcpy( x->x_curdir, "/" );
+ }
+ }
+ else
+ if ( !strcmp( x->x_dentries[ x->x_itemselected ], "." ) )
+ {
+ // nothing
+ }
+ else
+ {
+ if ( strlen( x->x_curdir ) + strlen( x->x_dentries[x->x_itemselected] ) + 2 > MAX_DIR_LENGTH )
+ {
+ post( "playlist : maximum dir length reached : cannot change directory" );
+ return -1;
+ }
+ if ( strcmp( x->x_curdir, "/" ) )
+ {
+ sprintf( x->x_curdir, "%s/%s", x->x_curdir, x->x_dentries[x->x_itemselected] );
+ }
+ else
+ {
+ sprintf( x->x_curdir, "/%s", x->x_dentries[x->x_itemselected] );
+ }
+ }
+
+ playlist_update_dir( x, glist );
+ }
+ }
+ }
+ glist_grab(glist, &x->x_obj.te_g, (t_glistmotionfn)playlist_motion,
+ 0, xpix, ypix);
+ }
+ return (1);
+}
+
+static void playlist_properties(t_gobj *z, t_glist *owner)
+{
+ char buf[800];
+ t_playlist *x=(t_playlist *)z;
+
+ sprintf(buf, "pdtk_playlist_dialog %%s %s %d %d\n",
+ x->x_extension, x->x_width, x->x_height);
+ // post("playlist_properties : %s", buf );
+ gfxstub_new(&x->x_obj.ob_pd, x, buf);
+}
+
+static void playlist_dialog(t_playlist *x, t_symbol *s, int argc, t_atom *argv)
+{
+ if ( !x ) {
+ post( "playlist : error :tried to set properties on an unexisting object" );
+ }
+ if ( argc != 3 )
+ {
+ post( "playlist : error in the number of arguments ( %d instead of 3 )", argc );
+ return;
+ }
+ if ( argv[0].a_type != A_SYMBOL || argv[1].a_type != A_FLOAT ||
+ argv[2].a_type != A_FLOAT ) {
+ post( "playlist : wrong arguments" );
+ return;
+ }
+ x->x_extension = argv[0].a_w.w_symbol->s_name;
+ x->x_width = (int)argv[1].a_w.w_float;
+ x->x_height = (int)argv[2].a_w.w_float;
+
+ playlist_draw_erase(x, x->x_glist);
+ playlist_draw_new(x, x->x_glist);
+}
+
+
+t_playlist *playlist_new(t_symbol *extension, t_floatarg fwidth, t_floatarg fheight )
+{
+ int i;
+ t_playlist *x;
+ char *tmpcurdir;
+
+ if ( !strcmp( extension->s_name, "" ) )
+ {
+ error( "playlist : no extension specified" );
+ error( "playlist : usage : playlist <extension> <width> <height>" );
+ return NULL;
+ }
+
+ if ( fwidth <= 0 )
+ {
+ error( "playlist : wrong width (%d)", fwidth );
+ error( "playlist : usage : playlist <extension> <width> <height>" );
+ return NULL;
+ }
+
+ if ( fheight <= 0 )
+ {
+ error( "playlist : wrong height (%d)", fheight );
+ error( "playlist : usage : playlist <extension> <width> <height>" );
+ return NULL;
+ }
+
+ x = (t_playlist *)pd_new(playlist_class);
+
+ x->x_width = fwidth;
+ x->x_height = fheight;
+ x->x_extension = ( char * ) getbytes( strlen( extension->s_name ) + 2 );
+ sprintf( x->x_extension, "%s", extension->s_name );
+
+ x->x_fullpath = outlet_new(&x->x_obj, &s_symbol );
+ x->x_file = outlet_new(&x->x_obj, &s_symbol );
+ x->x_dir = outlet_new(&x->x_obj, &s_symbol );
+
+ x->x_glist = (t_glist *) canvas_getcurrent();
+ x->x_nentries = 0;
+ x->x_pnentries = 0;
+ x->x_dentries = NULL;
+
+ // get current directory full path
+ x->x_curdir = ( char * ) getbytes( MAX_DIR_LENGTH );
+ if ( ( tmpcurdir = getenv( "PWD" ) ) == NULL )
+ {
+ post( "playlist : could not get current directory ( where the hell are you ??? )" );
+ return NULL;
+ }
+ strncpy( x->x_curdir, tmpcurdir, strlen( tmpcurdir ) );
+ x->x_curdir[ strlen( tmpcurdir ) ] = '\0';
+
+ x->x_selected = 0;
+ x->x_itemselected = -1;
+
+ // post( "playlist : built extension=%s width=%d height=%d", x->x_extension, x->x_width, x->x_height );
+
+ return (x);
+}
+
+void playlist_free(t_playlist *x)
+{
+ // post( "playlist : playlist_free" );
+ if ( x->x_extension )
+ {
+ freebytes( x->x_extension, strlen( x->x_extension ) );
+ }
+ if ( x->x_curdir )
+ {
+ freebytes( x->x_curdir, MAX_DIR_LENGTH );
+ }
+}
+
+void playlist_seek(t_playlist *x, t_floatarg fseeked)
+{
+ int iout=0;
+
+ if ( fseeked < 0 )
+ {
+ post( "playlist : wrong searched file : %f", fseeked );
+ return;
+ }
+
+ if ( x->x_nentries > 2 )
+ {
+ // do not select . or ..
+ iout = (int)fseeked % (x->x_nentries-2) + 2;
+ }
+ else
+ {
+ return;
+ }
+ SYS_VGUI4(".x%x.c itemconfigure %xENTRY%d -fill #000000\n", x->x_glist, x, x->x_itemselected);
+ x->x_itemselected = iout;
+ SYS_VGUI4(".x%x.c itemconfigure %xENTRY%d -fill #FF0000\n", x->x_glist, x, x->x_itemselected);
+ playlist_output_current(x);
+}
+
+void playlist_location(t_playlist *x, t_symbol *flocation)
+{
+ int iout=0;
+ char olddir[ MAX_DIR_LENGTH ]; /* remember old location */
+
+ strcpy( olddir, x->x_curdir );
+
+ if ( !strcmp( flocation->s_name, ".." ) )
+ {
+ char *iamthelastslash;
+
+ iamthelastslash = strrchr( x->x_curdir, '/' );
+ *iamthelastslash = '\0';
+
+ if ( !strcmp( x->x_curdir, "" ) )
+ {
+ strcpy( x->x_curdir, "/" );
+ }
+ }
+ else
+ if ( !strncmp( flocation->s_name, "/", 1 ) )
+ {
+ // absolute path required
+ if ( strlen( flocation->s_name ) >= MAX_DIR_LENGTH )
+ {
+ error( "playlist : maximum dir length reached : cannot change directory" );
+ return;
+ }
+ strncpy( x->x_curdir, flocation->s_name, MAX_DIR_LENGTH );
+ }
+ else
+ {
+ // relative path
+ if ( strlen( x->x_curdir ) + strlen( flocation->s_name ) + 2 > MAX_DIR_LENGTH )
+ {
+ post( "playlist : maximum dir length reached : cannot change directory" );
+ return;
+ }
+ if ( strcmp( x->x_curdir, "/" ) )
+ {
+ sprintf( x->x_curdir, "%s/%s", x->x_curdir, flocation->s_name );
+ }
+ else
+ {
+ sprintf( x->x_curdir, "/%s", flocation->s_name );
+ }
+ }
+
+ if ( chdir( x->x_curdir ) < 0 )
+ {
+ error( "playlist : requested location >%s< is not a directory", x->x_curdir );
+ strcpy( x->x_curdir, olddir );
+ return;
+ }
+
+ playlist_update_dir( x, x->x_glist );
+}
+
+void playlist_setup(void)
+{
+ post( playlist_version );
+#include "playlist.tk2c"
+ playlist_class = class_new(gensym("playlist"), (t_newmethod)playlist_new,
+ (t_method)playlist_free, sizeof(t_playlist),
+ CLASS_DEFAULT, A_SYMBOL, A_FLOAT, A_FLOAT, 0);
+ class_addmethod(playlist_class, (t_method)playlist_seek, gensym("seek"), A_FLOAT, 0 );
+ class_addmethod(playlist_class, (t_method)playlist_location, gensym("location"), A_SYMBOL, 0 );
+ class_addmethod(playlist_class, (t_method)playlist_dialog, gensym("dialog"), A_GIMME, 0 );
+
+ playlist_widgetbehavior.w_getrectfn = playlist_getrect;
+ playlist_widgetbehavior.w_displacefn = playlist_displace;
+ playlist_widgetbehavior.w_selectfn = playlist_select;
+ playlist_widgetbehavior.w_activatefn = NULL;
+ playlist_widgetbehavior.w_deletefn = playlist_delete;
+ playlist_widgetbehavior.w_visfn = playlist_vis;
+ playlist_widgetbehavior.w_clickfn = playlist_click;
+ playlist_widgetbehavior.w_propertiesfn = playlist_properties;
+ playlist_widgetbehavior.w_savefn = playlist_save;
+ class_setwidget(playlist_class, &playlist_widgetbehavior);
+ class_sethelpsymbol(playlist_class, gensym("help-playlist.pd"));
+}
diff --git a/playlist/playlist.tk b/playlist/playlist.tk
new file mode 100644
index 0000000..025c312
--- /dev/null
+++ b/playlist/playlist.tk
@@ -0,0 +1,89 @@
+############ playlist procedures -- ydegoyon@free.fr #########
+
+proc playlist_apply {id} {
+# strip "." from the TK id to make a variable name suffix
+ set vid [string trimleft $id .]
+# for each variable, make a local variable to hold its name...
+ set var_graph_extension [concat graph_extension_$vid]
+ global $var_graph_extension
+ set var_graph_width [concat graph_width_$vid]
+ global $var_graph_width
+ set var_graph_height [concat graph_height$vid]
+ global $var_graph_height
+
+ set cmd [concat $id dialog \
+ [eval concat $$var_graph_extension] \
+ [eval concat $$var_graph_width] \
+ [eval concat $$var_graph_height] \
+ \;]
+ #puts stderr $cmd
+ pd $cmd
+}
+
+proc playlist_cancel {id} {
+ set cmd [concat $id cancel \;]
+ #puts stderr $cmd
+ pd $cmd
+}
+
+proc playlist_ok {id} {
+ playlist_apply $id
+ playlist_cancel $id
+}
+
+proc pdtk_playlist_dialog {id extension width height} {
+ set vid [string trimleft $id .]
+ set var_graph_extension [concat graph_extension_$vid]
+ global $var_graph_extension
+ set var_graph_width [concat graph_width_$vid]
+ global $var_graph_width
+ set var_graph_height [concat graph_height$vid]
+ global $var_graph_height
+
+ set $var_graph_extension $extension
+ set $var_graph_width $width
+ set $var_graph_height $height
+
+ toplevel $id
+ wm title $id {playlist}
+ wm protocol $id WM_DELETE_WINDOW [concat playlist_cancel $id]
+
+ label $id.label -text {PLAYLIST PROPERTIES}
+ pack $id.label -side top
+
+ frame $id.buttonframe
+ pack $id.buttonframe -side bottom -fill x -pady 2m
+ button $id.buttonframe.cancel -text {Cancel}\
+ -command "playlist_cancel $id"
+ button $id.buttonframe.apply -text {Apply}\
+ -command "playlist_apply $id"
+ button $id.buttonframe.ok -text {OK}\
+ -command "playlist_ok $id"
+ pack $id.buttonframe.cancel -side left -expand 1
+ pack $id.buttonframe.apply -side left -expand 1
+ pack $id.buttonframe.ok -side left -expand 1
+
+ frame $id.1rangef
+ pack $id.1rangef -side top
+ label $id.1rangef.lextension -text "Files Extension :"
+ entry $id.1rangef.extension -textvariable $var_graph_extension -width 7
+ pack $id.1rangef.lextension $id.1rangef.extension -side left
+
+ frame $id.2rangef
+ pack $id.2rangef -side top
+ label $id.2rangef.lwidth -text "Width :"
+ entry $id.2rangef.width -textvariable $var_graph_width -width 7
+ pack $id.2rangef.lwidth $id.2rangef.width -side left
+
+ frame $id.3rangef
+ pack $id.3rangef -side top
+ label $id.3rangef.lheight -text "Height :"
+ entry $id.3rangef.height -textvariable $var_graph_height -width 7
+ pack $id.3rangef.lheight $id.3rangef.height -side left
+
+ bind $id.1rangef.extension <KeyPress-Return> [concat playlist_ok $id]
+ bind $id.2rangef.width <KeyPress-Return> [concat playlist_ok $id]
+ bind $id.3rangef.height <KeyPress-Return> [concat playlist_ok $id]
+}
+
+############ playlist procedures END -- ydegoyon@free.fr #########
diff --git a/playlist/xmms.pd b/playlist/xmms.pd
new file mode 100644
index 0000000..b9282cd
--- /dev/null
+++ b/playlist/xmms.pd
@@ -0,0 +1,144 @@
+#N canvas 146 29 755 574 10;
+#X obj 411 144 mp3fileout~;
+#X floatatom 411 165 5 0 0;
+#X floatatom 456 165 5 0 0;
+#X msg 409 113 open \$1;
+#X msg 466 113 start;
+#X msg 511 114 stop;
+#X text 534 154 Number of frames emitted;
+#X text 396 184 Connection state;
+#X symbolatom 127 286 80 0 0;
+#X floatatom 520 63 5 0 0;
+#X obj 20 57 playlist mp3 300 200;
+#X msg 651 37 bang;
+#X text 23 17 Step 1 : point to your MP3s directory;
+#X obj 357 513 dac~;
+#X symbolatom 131 438 10 0 0;
+#X text 127 452 Incomer's address;
+#X obj 349 470 *~ 1;
+#X floatatom 461 513 5 0 0;
+#X obj 427 513 / 100;
+#X obj 390 470 *~ 1;
+#X text 501 515 Volume;
+#X msg 336 38 connect localhost 5672;
+#X obj 95 341 mp3streamin~ 5672 1;
+#X text 295 15 Step 2 : connect to the streamer;
+#X text 65 285 Playing :;
+#X msg 463 64 seek \$1;
+#X text 566 11 Step 3 :start random play;
+#X obj 657 410 loadbang;
+#X msg 656 434 \; pd dsp 1;
+#X msg 379 220 seek \$1;
+#X obj 437 219 hsl 128 15 0 1e+07 0 0 empty empty empty 20 8 0 8 -262144
+-1 -1 0 0;
+#X msg 547 114 disconnect;
+#X obj 573 37 random 1000;
+#X msg 620 73 seed \$1;
+#X floatatom 673 73 5 0 0;
+#X msg 634 220 bang;
+#X text 575 264 Step 3 bis :start seq play;
+#X obj 592 220 countn;
+#X obj 578 242 - 1;
+#X floatatom 531 243 5 0 0;
+#X obj 551 187 spigot;
+#X obj 599 186 tgl 15 0 empty empty empty 20 8 0 8 -262144 -1 -1 0
+1;
+#X msg 624 186 1;
+#X obj 618 95 spigot;
+#X obj 666 94 tgl 15 0 empty empty empty 20 8 0 8 -262144 -1 -1 1 1
+;
+#X msg 691 94 1;
+#X obj 362 441 ekwalizer --------------------------;
+#X msg 544 315 100;
+#X obj 385 343 vsl 15 80 0 100 0 0 empty empty empty 20 8 0 8 -262144
+-1 -1 7900 1;
+#X obj 408 343 vsl 15 80 0 100 0 0 empty empty empty 20 8 0 8 -262144
+-1 -1 7900 1;
+#X obj 432 343 vsl 15 80 0 100 0 0 empty empty empty 20 8 0 8 -262144
+-1 -1 7900 1;
+#X obj 454 343 vsl 15 80 0 100 0 0 empty empty empty 20 8 0 8 -262144
+-1 -1 7900 1;
+#X obj 477 343 vsl 15 80 0 100 0 0 empty empty empty 20 8 0 8 -262144
+-1 -1 0 1;
+#X obj 498 343 vsl 15 80 0 100 0 0 empty empty empty 20 8 0 8 -262144
+-1 -1 0 1;
+#X obj 516 343 vsl 15 80 0 100 0 0 empty empty empty 20 8 0 8 -262144
+-1 -1 0 1;
+#X obj 534 343 vsl 15 80 0 100 0 0 empty empty empty 20 8 0 8 -262144
+-1 -1 0 1;
+#X obj 552 343 vsl 15 80 0 100 0 0 empty empty empty 20 8 0 8 -262144
+-1 -1 0 1;
+#X obj 573 343 vsl 15 80 0 100 0 0 empty empty empty 20 8 0 8 -262144
+-1 -1 0 1;
+#X obj 579 315 loadbang;
+#X obj 494 539 loadbang;
+#X msg 463 539 50;
+#X obj 259 486 compressor~;
+#X floatatom 279 434 5 0 0;
+#X obj 241 509 env~;
+#X obj 266 463 / 100;
+#X connect 0 0 1 0;
+#X connect 0 1 2 0;
+#X connect 0 2 40 0;
+#X connect 0 2 43 0;
+#X connect 3 0 0 0;
+#X connect 4 0 0 0;
+#X connect 5 0 0 0;
+#X connect 10 0 8 0;
+#X connect 10 0 3 0;
+#X connect 11 0 32 0;
+#X connect 11 0 45 0;
+#X connect 16 0 61 0;
+#X connect 17 0 18 0;
+#X connect 18 0 16 1;
+#X connect 18 0 19 1;
+#X connect 19 0 61 0;
+#X connect 21 0 0 0;
+#X connect 21 0 4 0;
+#X connect 22 0 46 0;
+#X connect 22 1 46 0;
+#X connect 22 2 14 0;
+#X connect 25 0 10 0;
+#X connect 27 0 28 0;
+#X connect 29 0 0 0;
+#X connect 30 0 29 0;
+#X connect 31 0 0 0;
+#X connect 32 0 9 0;
+#X connect 32 0 25 0;
+#X connect 33 0 32 0;
+#X connect 34 0 33 0;
+#X connect 35 0 37 0;
+#X connect 35 0 42 0;
+#X connect 37 0 38 0;
+#X connect 38 0 25 0;
+#X connect 38 0 39 0;
+#X connect 40 0 37 0;
+#X connect 41 0 40 1;
+#X connect 42 0 41 0;
+#X connect 43 0 32 0;
+#X connect 44 0 43 1;
+#X connect 45 0 44 0;
+#X connect 46 0 19 0;
+#X connect 46 0 16 0;
+#X connect 47 0 48 0;
+#X connect 47 0 49 0;
+#X connect 47 0 50 0;
+#X connect 47 0 51 0;
+#X connect 48 0 46 1;
+#X connect 49 0 46 2;
+#X connect 50 0 46 3;
+#X connect 51 0 46 4;
+#X connect 52 0 46 5;
+#X connect 53 0 46 6;
+#X connect 54 0 46 7;
+#X connect 55 0 46 8;
+#X connect 56 0 46 9;
+#X connect 57 0 46 10;
+#X connect 58 0 47 0;
+#X connect 59 0 60 0;
+#X connect 60 0 17 0;
+#X connect 61 0 13 0;
+#X connect 61 0 13 1;
+#X connect 61 0 63 0;
+#X connect 62 0 64 0;
+#X connect 64 0 61 1;