gpixpod-0.6.2/0000755000175000017500000000000010513202110012424 5ustar flagarflagargpixpod-0.6.2/imgconvert/0000755000175000017500000000000010513202110014601 5ustar flagarflagargpixpod-0.6.2/imgconvert/imgconvert.h0000644000175000017500000000057610507301070017147 0ustar flagarflagar#include #include #include #include static const int IMGCONVERT_BEHAVIOUR_FIT= 0; static const int IMGCONVERT_BEHAVIOUR_ZOOM= 1; static const int IMGCONVERT_BEHAVIOUR_STRETCH= 2; static const int IMGCONVERT_AUTOROTATE_NONE= 0; static const int IMGCONVERT_AUTOROTATE_CW= 1; static const int IMGCONVERT_AUTOROTATE_CCW= 2; gpixpod-0.6.2/imgconvert/imgconvert.c0000644000175000017500000003136510507301070017142 0ustar flagarflagar#include "imgconvert.h" GdkPixbuf* getPixbufC(gchar* filename, gint width, gint height, gint behaviour, gint autorotate){ GdkPixbuf* pixbuf; GdkPixbuf* bgpixbuf; GdkPixbuf* oldpixbuf; pixbuf = gdk_pixbuf_new_from_file(filename, NULL); gint pwidth, pheight; pwidth = gdk_pixbuf_get_width(pixbuf); pheight = gdk_pixbuf_get_height(pixbuf); gfloat proportion = pwidth/pheight; if(proportion < 1 && autorotate == IMGCONVERT_AUTOROTATE_CW){ // CW oldpixbuf = pixbuf; pixbuf = gdk_pixbuf_rotate_simple(pixbuf, GDK_PIXBUF_ROTATE_CLOCKWISE); g_object_unref(oldpixbuf); pwidth = gdk_pixbuf_get_width(pixbuf); pheight = gdk_pixbuf_get_height(pixbuf); }else if(proportion < 1 && autorotate == IMGCONVERT_AUTOROTATE_CCW){ // CCW oldpixbuf = pixbuf; pixbuf = gdk_pixbuf_rotate_simple(pixbuf, GDK_PIXBUF_ROTATE_COUNTERCLOCKWISE); g_object_unref(oldpixbuf); pwidth = gdk_pixbuf_get_width(pixbuf); pheight = gdk_pixbuf_get_height(pixbuf); } gint height_with_ratio = pheight*width/pwidth; oldpixbuf = pixbuf; if(behaviour == IMGCONVERT_BEHAVIOUR_STRETCH){ // Stretch pixbuf = gdk_pixbuf_scale_simple(pixbuf, width, height, GDK_INTERP_BILINEAR); }else if(behaviour == IMGCONVERT_BEHAVIOUR_ZOOM){ // Zoom if(height_with_ratio > height){ //bgpixbuf = gdk_pixbuf_composite_color_simple(bgpixbuf, width, height, GDK_INTERP_BILINEAR, 0, 2, 0, 0); pixbuf = gdk_pixbuf_scale_simple(pixbuf, width, height_with_ratio, GDK_INTERP_BILINEAR); bgpixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, gdk_pixbuf_get_has_alpha(pixbuf), 8, width, height); //gdk_pixbuf_fill(bgpixbuf, 0); gdk_pixbuf_copy_area(pixbuf, 0, (height_with_ratio-height)/2, width, height, bgpixbuf, 0, 0); g_object_unref(pixbuf); pixbuf = bgpixbuf; }else if(height_with_ratio < height){ gint width_with_ratio = pwidth*height/pheight; //bgpixbuf = gdk_pixbuf_composite_color_simple(bgpixbuf, width, height, GDK_INTERP_BILINEAR, 0, 2, 0, 0); bgpixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, gdk_pixbuf_get_has_alpha(pixbuf), 8, width, height); //gdk_pixbuf_fill(bgpixbuf, 0); pixbuf = gdk_pixbuf_scale_simple(pixbuf, width_with_ratio, height, GDK_INTERP_BILINEAR); gdk_pixbuf_copy_area(pixbuf, (width_with_ratio-width)/2, 0, width, height, bgpixbuf, 0, 0); g_object_unref(pixbuf); pixbuf = bgpixbuf; }else{ pixbuf = gdk_pixbuf_scale_simple(pixbuf, width, height, GDK_INTERP_BILINEAR); } }else{ //Fit if(height > height_with_ratio){ //bgpixbuf = gdk_pixbuf_composite_color_simple(bgpixbuf, width, height, GDK_INTERP_BILINEAR, 0, 2, 0, 0); bgpixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, gdk_pixbuf_get_has_alpha(pixbuf), 8, width, height); gdk_pixbuf_fill(bgpixbuf, 0); pixbuf = gdk_pixbuf_scale_simple(pixbuf, width, height_with_ratio, GDK_INTERP_BILINEAR); gdk_pixbuf_copy_area(pixbuf, 0, 0, width, height_with_ratio, bgpixbuf, 0, (height-height_with_ratio)/2); g_object_unref(pixbuf); pixbuf = bgpixbuf; }else if(height < height_with_ratio){ gint width_with_ratio = pwidth*height/pheight; //bgpixbuf = gdk_pixbuf_composite_color_simple(bgpixbuf, width, height, GDK_INTERP_BILINEAR, 0, 2, 0, 0); bgpixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, gdk_pixbuf_get_has_alpha(pixbuf), 8, width, height); gdk_pixbuf_fill(bgpixbuf, 0); pixbuf = gdk_pixbuf_scale_simple(pixbuf, width_with_ratio, height, GDK_INTERP_BILINEAR); gdk_pixbuf_copy_area(pixbuf, 0, 0, width_with_ratio, height, bgpixbuf, (width-width_with_ratio)/2, 0); g_object_unref(pixbuf); pixbuf = bgpixbuf; }else{ pixbuf = gdk_pixbuf_scale_simple(pixbuf, width, height, GDK_INTERP_BILINEAR); } } g_object_unref(oldpixbuf); return pixbuf; } void toRGB565C(gchar* filename, gint width, gint height, gboolean swap_bytes, gboolean rotate, gint behaviour, gint autorotate, gchar* savefilename){ GdkPixbuf* pixbuf; //pixbuf = gdk_pixbuf_new_from_file_at_scale(filename, width, height, FALSE, NULL); pixbuf = getPixbufC(filename, width, height, behaviour, autorotate); if(!pixbuf){ printf("Argh! Failed to get pixbuf!\n"); exit(1); } if(rotate){ GdkPixbuf* oldpixbuf; oldpixbuf = pixbuf; pixbuf = gdk_pixbuf_rotate_simple(pixbuf, GDK_PIXBUF_ROTATE_COUNTERCLOCKWISE); g_object_unref(oldpixbuf); gint width2 = height; height = width; width = width2; } guchar *pixels = gdk_pixbuf_get_pixels(pixbuf); gint x = 0; gint y = 0; gint rgbpx, red, green, green2, blue, byte1, byte2; //gint imgsize = gdk_pixbuf_get_rowstride(pixbuf)*(gdk_pixbuf_get_height(pixbuf)-1)+ // (gdk_pixbuf_get_width(pixbuf)*(31/8)); if(gdk_pixbuf_get_has_alpha(pixbuf)){ rgbpx = 4; }else{ rgbpx = 3; } //gint imgsize = width*height*rgbpx; guchar *rgb565data = (guchar *) g_malloc(width*height*2); if(!rgb565data){ printf("Argh... Could not allocate memory when converting to RGB565!\n"); exit(1); } // Sometimes happen that rowstride > width*rgpx. So we cut exceeding padding bytes. gint rowstride = gdk_pixbuf_get_rowstride(pixbuf); gint imgwidth = width*rgbpx; gint exc = rowstride - imgwidth; gint h = 0; gint w; while(h < height){ w = 0; while(w < imgwidth){ red = pixels[x]; red = red >> 3; green = pixels[x+1] >> 2; blue = pixels[x+2] >> 3; green2 = green & 7; byte1 = (red << 3) + (green >> 3); byte2 = (green2 << 5) + blue; if(swap_bytes){ rgb565data[y] = byte2; rgb565data[y+1] = byte1; }else{ rgb565data[y] = byte1; rgb565data[y+1] = byte2; } w += rgbpx; x += rgbpx; y += 2; } x += exc; h++; } FILE *fsp = fopen(savefilename, "w+b"); if(!fsp){ printf("Argh! Could not open file for writing RGB565 data!\n"); exit(1); } fwrite(rgb565data, width*height*2, 1, fsp); fclose(fsp); while(G_IS_OBJECT(pixbuf)){ g_object_unref(pixbuf); } g_free(rgb565data); } void fromRGB565C(guchar* rgb565data, gint width, gint height, gboolean swap_bytes, gboolean rotate, gchar* savefilename){ gint x = 0; gint y = 0; gint red, green, green1, green2, blue, byte1, byte2; gint imgsize = width*3*height; guchar *pixels = (guchar *) g_malloc(imgsize); if(!pixels){ printf("Argh... Could not allocate memory when converting from RGB565!\n"); exit(1); } while(x < imgsize/3*2){ if(swap_bytes){ byte2 = rgb565data[x]; byte1 = rgb565data[x+1]; }else{ byte1 = rgb565data[x]; byte2 = rgb565data[x+1]; } red = byte1 >> 3; blue = byte2 & 31; green1 = byte1 & 7; green2 = byte2 >> 5; green1 = green1 << 3; green = green1 + green2; red = red << 3; green = green << 2; blue = blue << 3; pixels[y] = red; pixels[y+1] = green; pixels[y+2] = blue; x += 2; y += 3; } GdkPixbuf* pixbuf; if(rotate){ pixbuf = gdk_pixbuf_new_from_data(pixels, GDK_COLORSPACE_RGB, FALSE, 8, height, width, (height * 3), NULL, NULL); GdkPixbuf* oldpixbuf = pixbuf; pixbuf = gdk_pixbuf_rotate_simple(pixbuf, GDK_PIXBUF_ROTATE_CLOCKWISE); g_object_unref(oldpixbuf); }else{ pixbuf = gdk_pixbuf_new_from_data(pixels, GDK_COLORSPACE_RGB, FALSE, 8, width, height, (width * 3), NULL, NULL); } gdk_pixbuf_save(pixbuf, savefilename, "png", NULL, NULL); //g_free(pixels); g_object_unref(pixbuf); } gint limit8bit(gint x){ if(x > 255){ return 255; }else if(x < 0){ return 0; }else{ return x; } } void toInterlacedUYVYC(gchar* filename, gint behaviour, gint autorotate, gchar *savefilename){ GdkPixbuf* pixbuf; gint width = 720; gint height = 480; //pixbuf = gdk_pixbuf_new_from_file_at_scale(filename, width, height, FALSE, NULL); pixbuf = getPixbufC(filename, width, height, behaviour, autorotate); if(!pixbuf){ printf("Argh! Failed to get pixbuf!\n"); exit(1); } guchar* pixels = gdk_pixbuf_get_pixels(pixbuf); gint x = 0; gint z = 0; gint z2 = 0; gint r0, g0, b0, r1, g1, b1, r2, g2, b2, r3, g3, b3; //gint imgsize = gdk_pixbuf_get_rowstride(pixbuf)*(gdk_pixbuf_get_height(pixbuf)-1)+(gdk_pixbuf_get_width(pixbuf)*(31/8)); gint yuvsize = width*2*height; guchar* yuvdata = (guchar*) g_malloc(yuvsize); if(!yuvdata){ printf("Argh... Could not allocate memory when converting to interlaced YUV!\n"); exit(1); } gint halfyuv = yuvsize/2; gint alphabit, rgbpx; if(gdk_pixbuf_get_has_alpha(pixbuf)){ alphabit = 1; rgbpx = 4; }else{ alphabit = 0; rgbpx = 3; } gint rowstride = gdk_pixbuf_get_rowstride(pixbuf); gint exc = rowstride - width*rgbpx; gint h = 0; while(h < height){ gint w = 0; if((h % 2) == 0){ while(w < width){ r0 = pixels[x]; g0 = pixels[x+1]; b0 = pixels[x+2]; r1 = pixels[x+3+alphabit]; g1 = pixels[x+4+alphabit]; b1 = pixels[x+5+alphabit]; yuvdata[z] = ((r0*-38 + g0*-74 + b0*112 + 128) >> 8) + 128; //U0 yuvdata[z+1] = ((r0*66 + g0*129 + b0*25 + 128) >> 8) + 16; //Y0 yuvdata[z+2] = ((r0*112 + g0*-94 + b0*-18 + 128) >> 8) + 128; //V0 yuvdata[z+3] = ((r1*66 + g1*129 + b1*25 + 128) >> 8) + 16; //Y1 w += 2; z += 4; x += rgbpx*2; } }else{ while(w < width){ r2 = pixels[x]; g2 = pixels[x+1]; b2 = pixels[x+2]; r3 = pixels[x+3+alphabit]; g3 = pixels[x+4+alphabit]; b3 = pixels[x+5+alphabit]; yuvdata[halfyuv+z2] = ((r2*-38 + g2*-74 + b2*112 + 128) >> 8) + 128; //U1 yuvdata[halfyuv+z2+1] = ((r2*66 + g2*129 + b2*25 + 128) >> 8) + 16; //Y2 yuvdata[halfyuv+z2+2] = ((r2*112 + g2*-94 + b2*-18 + 128) >> 8) + 128; //V1 yuvdata[halfyuv+z2+3] = ((r3*66 + g3*129 + b3*25 + 128) >> 8) + 16; //Y3 w += 2; z2 += 4; x += rgbpx*2; } } x += exc; h++; } FILE *fsp = fopen(savefilename, "w+b"); if(!fsp){ printf("Argh! Could not open file for writing interlaced YUV data!\n"); exit(1); } fwrite(yuvdata, yuvsize, 1, fsp); fclose(fsp); while(G_IS_OBJECT(pixbuf)){ g_object_unref(pixbuf); } g_free(yuvdata); } void fromInterlacedUYVYC(guchar* yuvdata, gchar* savefilename){ gint width = 720; gint height = 480; gint imgsize = width*3*height; guchar* rgbdata = (guchar*) g_malloc(imgsize); if(!rgbdata){ printf("Argh... Could not allocate memory when converting from interlaced YUV!\n"); exit(1); } gint halfimgsize = imgsize/2; gint halfyuv = halfimgsize/3*2; gint x = 0; gint z = 0; gint z2 = 0; gint u0, y0, v0, y1, u1, y2, v1, y3; gint h = 0; while(h < height){ gint w = 0; if((h % 2) == 0){ while(w < width){ u0 = yuvdata[z]; y0 = yuvdata[z+1]; v0 = yuvdata[z+2]; y1 = yuvdata[z+3]; rgbdata[x] = limit8bit((y0-16)*1.164 + (v0-128)*1.596); //R0 rgbdata[x+1] = limit8bit((y0-16)*1.164 - (v0-128)*0.813 - (u0-128)*0.391); //G0 rgbdata[x+2] = limit8bit((y0-16)*1.164 + (u0-128)*2.018); //B0 rgbdata[x+3] = limit8bit((y0-16)*1.164 + (v0-128)*1.596); //R1 rgbdata[x+4] = limit8bit((y1-16)*1.164 - (v0-128)*0.813 - (u0-128)*0.391); //G1 rgbdata[x+5] = limit8bit((y1-16)*1.164 + (u0-128)*2.018); //B1 w += 2; z += 4; x += 6; } }else{ while(w < width){ u1 = yuvdata[halfyuv+z2]; y2 = yuvdata[halfyuv+z2+1]; v1 = yuvdata[halfyuv+z2+2]; y3 = yuvdata[halfyuv+z2+3]; rgbdata[x] = limit8bit((y2-16)*1.164 + (v1-128)*1.596); rgbdata[x+1] = limit8bit((y2-16)*1.164 - (v1-128)*0.813 - (u1-128)*0.391); rgbdata[x+2] = limit8bit((y2-16)*1.164 + (u1-128)*2.018); rgbdata[x+3] = limit8bit((y2-16)*1.164 + (v1-128)*1.596); rgbdata[x+4] = limit8bit((y3-16)*1.164 - (v1-128)*0.813 - (u1-128)*0.391); rgbdata[x+5] = limit8bit((y3-16)*1.164 + (u1-128)*2.018); w += 2; z2 += 4; x += 6; } } h++; } GdkPixbuf *pixbuf = gdk_pixbuf_new_from_data(rgbdata, GDK_COLORSPACE_RGB, FALSE, 8, width, height, (width * 3), NULL, NULL); //g_free(rgbdata); gdk_pixbuf_save(pixbuf, savefilename, "png", NULL, NULL); } void testAll(){ //GdkPixbuf* pixbuf; gint width, height; width = 640; height = 480; //guchar* pixbufchars; toRGB565C("tramontofortini.jpg", width, height, FALSE, TRUE, IMGCONVERT_BEHAVIOUR_FIT, IMGCONVERT_AUTOROTATE_NONE, "test.rgb"); //pixbuf = fromRGB565C(pixbufchars, height, width, FALSE, TRUE); //gdk_pixbuf_save(pixbuf, "test.png", "png", NULL, NULL); } //int main(int argc, char *argv[]){ // gtk_init(&argc, &argv); // GdkPixbuf* pixbuf; //pixbuf = gdk_pixbuf_new_from_file("GPixPod.png", NULL); // gint width, height; //width = gdk_pixbuf_get_width(pixbuf); //height = gdk_pixbuf_get_height(pixbuf); //if(gdk_pixbuf_get_has_alpha(pixbuf)){ // printf("Pixbuf ha alpha"); //}else{ // printf("Pixbuf non ha alpha"); //} // width = 640; // height = 480; //pixbuf = getPixbuf(argv[1], width, height, FALSE, 1, 1); // guchar* pixbufchars; // pixbufchars = toRGB565C(argv[1], width, height, TRUE, FALSE, IMGCONVERT_BEHAVIOUR_ZOOM, IMGCONVERT_AUTOROTATE_NONE); // pixbuf = fromRGB565C(pixbufchars, width, height, TRUE, FALSE); // gdk_pixbuf_save(pixbuf, "test.png", "png", NULL, NULL); // pixbufchars = toInterlacedUYVYC(argv[1], 2, 0); // pixbuf = fromInterlacedUYVYC(pixbufchars); // gdk_pixbuf_save(pixbuf, "test.png", "png", NULL, NULL); // return 0; //} gpixpod-0.6.2/imgconvert/imgconvertmodule.h0000644000175000017500000000122510507301070020345 0ustar flagarflagar#include #include "imgconvert.h" extern GdkPixbuf* getPixbufC(gchar* filename, gint width, gint height, gint behaviour, gint autorotate); extern void toRGB565C(gchar* filename, gint width, gint height, gboolean swap_bytes, gboolean rotate, gint behaviour, gint autorotate, gchar *savefilename); extern void fromRGB565C(guchar* rgb565data, gint width, gint height, gboolean swap_bytes, gboolean rotate, gchar* savefilename); extern void toInterlacedUYVYC(gchar* filename, gint behaviour, gint autorotate, gchar *savefilename); extern void fromInterlacedUYVYC(guchar* yuvdata, gchar* savefilename); extern int limit8bit(int x); extern void testAll(); gpixpod-0.6.2/imgconvert/imgconvertmodule.c0000644000175000017500000000532510507301070020345 0ustar flagarflagar#include "imgconvertmodule.h" static PyObject *toRGB565(PyObject *self, PyObject *args){ gchar* filename; gchar* savefilename; gint width, height; gint behaviour; // = 0; //default is FIT gint autorotate; // = 0; //default is None gint swap_bytes; // = TRUE; gint rotate; // = FALSE; if(!PyArg_ParseTuple(args, "siiiiiis", &filename, &width, &height, &swap_bytes, &rotate, &behaviour, &autorotate, &savefilename)){ printf("Argh! Failed to parse tuple!\n"); exit(1); } toRGB565C(filename, width, height, swap_bytes, rotate, behaviour, autorotate, savefilename); Py_INCREF(Py_None); return Py_None; } static PyObject *fromRGB565(PyObject *self, PyObject *args){ guchar* rgbdata; gint width, height; gint swap_bytes; // = TRUE; gint rotate; // = FALSE; gchar* savefilename; // = "gpixpod_imgconvert_test.png"; gint rgbdatalen; if(!PyArg_ParseTuple(args, "s#iiiis", &rgbdata, &rgbdatalen, &width, &height, &swap_bytes, &rotate, &savefilename)){ printf("Argh! Failed to parse tuple!\n"); exit(1); } fromRGB565C(rgbdata, width, height, swap_bytes, rotate, savefilename); Py_INCREF(Py_None); return Py_None; } static PyObject *toInterlacedUYVY(PyObject *self, PyObject *args){ gchar *filename, *savefilename; gint behaviour, autorotate; PyArg_ParseTuple(args, "siis", &filename, &behaviour, &autorotate, &savefilename); toInterlacedUYVYC(filename, behaviour, autorotate, savefilename); Py_INCREF(Py_None); return Py_None; } static PyObject *fromInterlacedUYVY(PyObject *self, PyObject *args){ guchar* yuvdata; gchar* savefilename; PyArg_ParseTuple(args, "ss", &yuvdata, &savefilename); fromInterlacedUYVYC(yuvdata, savefilename); Py_INCREF(Py_None); return Py_None; } static PyObject *limit(PyObject *self, PyObject *args){ int x; PyArg_ParseTuple(args, "i", &x); int y = limit8bit(x); return Py_BuildValue("i", y); } static PyObject *testall(PyObject *self, PyObject *args){ testAll(); Py_INCREF(Py_None); return Py_None; } static PyMethodDef IMGConvertMethods[] = { {"toRGB565", toRGB565, METH_VARARGS, "Convert an image to RGB565"}, {"fromRGB565", fromRGB565, METH_VARARGS, "Convert a RGB565 stream to pixbuf"}, {"toInterlacedUYVY", toInterlacedUYVY, METH_VARARGS, "Convert an image to interlaced UYVY"}, {"fromInterlacedUYVY", fromInterlacedUYVY, METH_VARARGS, "Convert an interlaced UYVY stream to pixbuf"}, {"limit", limit, METH_VARARGS, "Limit a number in 0-255 range"}, {"testall", testall, METH_VARARGS, "Test all"}, {NULL, NULL, 0, NULL} }; PyMODINIT_FUNC initimgconvert(void){ (void) Py_InitModule("imgconvert", IMGConvertMethods); } int main(int argc, char *argv[]){ gtk_init(&argc, &argv); Py_SetProgramName(argv[0]); Py_Initialize(); initimgconvert(); return 0; } gpixpod-0.6.2/imgconvert/Makefile0000644000175000017500000000122110507301070016245 0ustar flagarflagarCC=gcc LD=gcc CFLAGS=-Wall -fPIC `pkg-config --cflags gtk+-2.0` -I/usr/include/python2.4 -g LLDFLAGS=-Wall -shared `pkg-config --libs gtk+-2.0` -lpython2.4 -g SLDFLAGS=-Wall `pkg-config --libs gtk+-2.0` -g all: imgconvert.o imgconvertmodule.o $(LD) $(LLDFLAGS) imgconvertmodule.o imgconvert.o -o imgconvertmodule.so && cp imgconvertmodule.so ../ clean: rm -f *.o *.so imgconvert imgconvertmodule: $(CC) $(CFLAGS) imgconvertmodule.c -o imgconvertmodule.o imgconvert: $(CC) $(CFLAGS) imgconvert.c -o imgconvert.o main: $(CC) $(CFLAGS) main.c -o main.o test: imgconvert.o main.o $(LD) $(SLDFLAGS) imgconvert.o main.o -o imgconvert gpixpod-0.6.2/po/0000755000175000017500000000000010513202110013042 5ustar flagarflagargpixpod-0.6.2/po/es/0000755000175000017500000000000010513202110013451 5ustar flagarflagargpixpod-0.6.2/po/es/LC_MESSAGES/0000755000175000017500000000000010513202110015236 5ustar flagarflagargpixpod-0.6.2/po/es/LC_MESSAGES/gpixpod.mo0000644000175000017500000002715410507301070017266 0ustar flagarflagarsL   0 / U7 J N ' D a #r   b 51 9g 0  ! , 6 EU ! H  0O)e*%'))Q({E&. 7X.k]V r   %1;3oTO(?/Roy <J MO[& Td/!71D Wbh!# h4`Nkm5 (:Mbkz2;.gjMP q )#f3634.:i%{ $H$, HQ  3 ' !5! L!m!9!!7!,"64",k"="""/"-#5#Q#9b###[#R$q$ $ $"$+$$?%8N%%n%c &o&/x&U&t&s'''T'''(((C(S\(((4( )%*)2P))))))1)1*$J*o** Y+td++_+V,qt,i,P- `- k-y- ---- --&--'.<.T.& 6\ZM+iIK=La?!O`j.4JV*#YR7-( g$d8)]Bn"Wo[@fQ:P92cXDTAh'p,kbN>  es< C3F^%r _HmE0l/GqS5;1U %s of %s - %s%%(chmod +s `which eject` as root)Are you sure to remove thumbnails cache?Create backups before. You have been warned! And please, report your feedback!Full resolution copies of your photos will be uploaded on your iPodFull resolution copies of your photos will not be uploaded on your iPodFullscreen iPod imagePhoto Database found.Quit now?Slideshow current iPod imageSmall iPod thumbnailTV output iPod imageiPod ejected, but the Do not disconnect message seems to be still shown on the iPod.iPod and existing Photo Database found.iPod disconnected. Do you want to exit now?iPod found, attached to %s.No full resolution imageAbout GPixPodAbout GPixPod by Flavio GargiuloAdd Al_bumAdd AlbumAdd Photo with full resolutionAdd Photo with full resolution image regardless of the global settingAdd Photo without full resolutionAdd Photo without full resolution image regardless of the global settingAdd Photo(s)Add _PhotoAdd a full resolution image to the current photoAdd a new photo albumAdd a photo to the current selected albumAdd info - GPixPodAdd new photo albumAdding albums...Adding full resolution images to photos...Adding photos...All the unsaved changes will be lost.Are you sure to delete the whole album?Ask before opening default Photo DatabaseAutodetect iPod connection/disconnectionBut Photo Database not found. Do you want to create a new one?CancelChoose File - GPixPodCopy full resolution photo on the iPodDefaultsDelete the current selected itemDeleting albums...Deleting full resolution images from photos...Deleting photos...Disconnect iPodDo you want to load the Photo Database found in the iPod attached to %s?Do you want to load the Photo Database found in the iPod attached to %s?Do you want to save before quit?E_ject iPodEject iPodEnter the name of the new album:Enter the new name for the album "%s"Full ResolutionFull resolution image will be deleted after savedFull resolution photo not found. Maybe have you deleted it?GPixPod PreferencesHelp GPixPod development with a PayPal donation! You will choose the amount, freely.If you really want to clean cache, do it only after saved and quit right after!ImagesIt does not have a color screen and it does not support photos!It is recommended to cancel right away, unless you know what are you doing.Not reconnecting means that you could not save your changes, resulting in a crash. And re-opening would abort all changesNot saved yetOKOpening %s...Opening Photo Database... It could take several minutes, depending on its size!Organize photos on your iPod, freely! Photo AlbumsPhoto Database %s opened.Photo Database saved.Please reconnect the iPod and without loading again the Photo DatabasePlease connect the iPod...Please specify info:Please wait... (it could take several minutes!)Progress status...Remove Full Resolution from photoRemove the full resolution image from the current photoRename photo albumRenaming albums...Save imageSavedSaving Photo Database...Select iPod folder mountpointSelect the iPod folder mountpointThere are changes not saved.There are not unsaved changes.This is now the default action of drag & drop and of the Add Photo button. You can always use the Add Photo with/without full resolution buttons to override this settingThumbnailsThumbnails cache is automatically removed when saving, only in rare cases you would remove it manually. Thumbnails cache removed!To let disappear the message from the iPod, make your eject executable setuid rootUsing default preferencesYou are using an iPod %s. Please quit now: your model is not supported, and it never will be.You are using an iPod %s. Your model is considered as an iPod Photo and nothing is guaranteed to work.Zoom (%):_About_Clean cache_Delete_Donate!_Edit_File_Help_Toolsavailable after savediPod HAL autodetection disablediPod mountpoint: translator-creditswith full resolutionwithout full resolutionProject-Id-Version: gpixpod 0.3.4 Report-Msgid-Bugs-To: POT-Creation-Date: 2006-03-22 12:23+0100 PO-Revision-Date: 2006-03-22 20:31-0300 Last-Translator: Diego Belotti Language-Team: Spanish MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=2; plural=(n != 1); %s de %s - %s%%(chmod +s `which eject` como root)Esta seguro que desea borrar el cache de thumbnails?Debe crear respaldos antes. Usted ha sido advertido! Y por favor, realimente con su experiencia!Copia de sus fotos con máxima resolución serán guardadas en su iPodCopia de sus fotos con máxima resolución no serán guardadas en su iPodImagen fullscren de iPodPhoto Database encontradaSalir ahora?Presentación de imagenes del iPodImagen pequeña de iPodSalida TV de imagen del iPodiPod expulsado, pero el mensaje Do not disconnect parece seguir mostrándose en el iPod.iPod con una Photo Database encontrados.iPod desconectado. Desea salir ahora?iPod encontrado, conectado a %sNo existe imagen de máxima resoluciónAcerca de GPixPodAcerca de GPixPod por Flavio GargiuloAgregar Al_bumAgregar AlbumAgregar foto con máxima resoluciónAgregar foto con máxima resolución a pesar de la configuración globalAgregar foto sin máxima resoluciónAgregar foto sin máxima resolución a pesar de la configuración globalAgregar Foto(s)Agregar _FotoAgregar una imagen de resolución máxima a la fotoAgregar un nuevo álbum de fotosAgregar una foto al álbum seleccionadoAgregar Info - GPixPodAgregar un nuevo álbum de fotosAgregando albumes...Agregando a las fotos imagenes con máxima resolución...Agregando fotos...Todos los cambios que no fueron guardados se perderán.Esta seguro que desea borrar todo el álbum?Preguntar antes de abrir la Photo Database por defectoAutodetectar conexión/desconexión del iPodPhoto Database no encontrada. Quieres crear una nueva?CancelarSeleccionar archivo - GPixPodCopiar la foto al iPod en su resolución actualDefectoBorrar el item seleccionadoBorrando albumesBorrando de las fotos imagenes con máxima resolución...Borrando fotos...Desconectar el iPodQuieres cargar la Photo Database encontrada en el iPod conectado a %s?Desea cargar la Photo Database encontrada en el iPod conectado a %s?Quiere guardar antes de salir?E_yectar iPodEyectar iPodIngrese el nombre del nuevo álbumIngrese el nuevo nombre para el álbum "%s"Resolución máximaLa imagen de máxima resolución será borrada luego de guardarFoto con máxima resolución no encotrada. Ud la borró?GPixPod PreferenciasAyuda el desarrollo de GPixPod con una donación via PayPal! Sentite libre de elegir el monto de la donación.Si realmente quieres borrar el cache, hazlo luego de guardar e inmediatamente después debes salir!ImagenesNo tiene una pantalla color y no soporta fotos!Se recomiendo cancelar ahora, a menos que tenga idea de lo que está haciendo.No reconectar significa que no se guardarán los cambios, resultando en un crash. Re-abriendo abortará los cambios.Aún no fue guardadoOKAbriendo %s...Abriendo Photo Database... Puede demorar algunos minutos, dependiendo de su tamaño!Organice sus fotos en el iPod, gratis! Albumes de FotosPhoto Database %s abierta.Photo Database guardada.Por favor reconecte el iPod y sin cargar nuevamente la Photo DatabasePor favor conecte el iPod...Por favor especifique info:Por favor espere... (puede demorar algunos minutos!)Progreso...Borrar máxima resolución de la fotoBorrar la imagen de máxima resolución de la fotoRenombrar álbum de fotosRenombrando albumesGuardar imágenGuardadoGuardando la Photo DatabaseSeleccione carpeta para punto de montaje del iPodSeleccione carpeta para punto de montaje del iPodExisten cambios no guardados.No hay cambios no guardados.Esta es ahora la acción de drag & drop y del botón Agregar Foto.Ud. siempre podrá utilizar los botones Agregar foto con/sin máxima resolución para sobreescribir esta configuración.ThumbnailsEl cache de thumbnails es borrado automaticamente al guardar, solo en casos extraños debe ser borrado manualmente. Cache de thumbnails borrado!Para hacer desaparecer el mensaje en el iPod, haga a eject ejecutable setuid rootUsar preferencias por defectoUd. está usando un iPod %s. Por favor : su modelo no está soportado, y nunca lo será.Estás usando un iPod. Tu modelo es considerado como un iPod Photo y no se garantiza que funcione.Zoom (%)_Acerca de_Borrar cache_Borrar_Donación!_Editar_Archivo_Ayuda_Herramientasdisponible luego de guardarseDeshablitar autodetección HAL de iPodPunto de montaje del iPod: Diego Belotti con máxima resoluciónsin máxima resolucióngpixpod-0.6.2/po/es/es.po0000644000175000017500000006434210513121312014435 0ustar flagarflagar# GPIXPOD - organize photos on your iPod, freely! # Copyright (C) 2006 FLAVIO GARGIULO # This file is distributed under the same license as the GPIXPOD package. # FLAVIO GARGIULO , 2006. # msgid "" msgstr "" "Project-Id-Version: gpixpod 0.3.4\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2006-10-11 09:52+0200\n" "PO-Revision-Date: 2006-03-22 20:31-0300\n" "Last-Translator: Diego Belotti \n" "Language-Team: Spanish \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: gpixpod.py:56 msgid "Using default preferences" msgstr "Usar preferencias por defecto" #: gpixpod.py:60 gpixpod.py:62 msgid "Add new photo album" msgstr "Agregar un nuevo álbum de fotos" #: gpixpod.py:60 gpixpod.py:62 msgid "Enter the name of the new album:" msgstr "Ingrese el nombre del nuevo álbum" #: gpixpod.py:61 gpixpod.py:63 #, fuzzy msgid "New Photo Album" msgstr "Nuevo álbum de Fotos" #: gpixpod.py:64 gpixpod.py:65 gpixpod.py:91 gpixpod.py:97 gpixpod.py:1046 msgid "Images" msgstr "Imagenes" #: gpixpod.py:67 #, fuzzy msgid "Image" msgstr "Imagen" #: gpixpod.py:69 msgid "Choose photo" msgstr "" #: gpixpod.py:69 msgid "Enter a Photo ID:" msgstr "" #: gpixpod.py:82 msgid "Select the iPod folder mountpoint" msgstr "Seleccione carpeta para punto de montaje del iPod" #: gpixpod.py:92 gpixpod.py:98 gpixpod.py:1047 msgid "Save image" msgstr "Guardar imágen" #: gpixpod.py:158 msgid "Please connect the iPod..." msgstr "Por favor conecte el iPod..." #: gpixpod.py:219 msgid "iPod HAL autodetection disabled" msgstr "Deshablitar autodetección HAL de iPod" #: gpixpod.py:232 msgid "Photo Database found." msgstr "Photo Database encontrada" #: gpixpod.py:232 #, python-format msgid "" "Do you want to load the Photo Database found in the iPod attached to " "%s?" msgstr "" "Quieres cargar la Photo Database encontrada en el iPod conectado a " "%s?" #: gpixpod.py:237 gpixpod.py:1730 #, python-format msgid "iPod found, attached to %s." msgstr "iPod encontrado, conectado a %s" #: gpixpod.py:237 gpixpod.py:1727 msgid "But Photo Database not found. Do you want to create a new one?" msgstr "Photo Database no encontrada. Quieres crear una nueva?" #: gpixpod.py:270 #, fuzzy msgid "A Photo Database already exists" msgstr "Photo Database guardada." #: gpixpod.py:270 msgid "Do you want to overwrite it completely?" msgstr "" #: gpixpod.py:283 gpixpod.glade.h:11 msgid "Fullscreen iPod image" msgstr "Imagen fullscren de iPod" #: gpixpod.py:284 gpixpod.glade.h:16 msgid "Small iPod thumbnail" msgstr "Imagen pequeña de iPod" #: gpixpod.py:290 #, python-format msgid "" "You are using an iPod %s. Please quit now: your model is not " "supported, and it never will be." msgstr "" "Ud. está usando un iPod %s. Por favor : su modelo " "no está soportado, y nunca lo será." #: gpixpod.py:291 msgid "It does not have a color screen and it does not support photos!" msgstr "No tiene una pantalla color y no soporta fotos!" #: gpixpod.py:292 msgid "Quit now?" msgstr "Salir ahora?" #: gpixpod.py:333 gpixpod_cli.py:176 msgid "" "Interruption requested. Please wait while saving at the current status..." msgstr "" #: gpixpod.py:356 #, python-format msgid "Opening %s..." msgstr "Abriendo %s..." #: gpixpod.py:358 msgid "" "Opening Photo Database... It could take several minutes, depending on its " "size!" msgstr "" "Abriendo Photo Database... Puede demorar algunos minutos, dependiendo de su " "tamaño!" #: gpixpod.py:400 #, python-format msgid "Photo Database %s opened." msgstr "Photo Database %s abierta." #: gpixpod.py:424 msgid "Adding full resolution images to photos..." msgstr "Agregando a las fotos imagenes con máxima resolución..." #: gpixpod.py:441 msgid "Deleting full resolution images from photos..." msgstr "Borrando de las fotos imagenes con máxima resolución..." #: gpixpod.py:459 msgid "Adding albums..." msgstr "Agregando albumes..." #: gpixpod.py:474 #, fuzzy msgid "Adding photo references..." msgstr "Agregando fotos..." #: gpixpod.py:488 msgid "Renaming albums..." msgstr "Renombrando albumes" #: gpixpod.py:502 msgid "Adding photos..." msgstr "Agregando fotos..." #: gpixpod.py:513 gpixpod.py:560 gpixpod.py:576 #, python-format msgid "%s of %s - %s%%" msgstr "%s de %s - %s%%" #: gpixpod.py:520 #, fuzzy msgid "Deleting photo references..." msgstr "Borrando fotos..." #: gpixpod.py:534 #, fuzzy msgid "Deleting whole photo album references..." msgstr "Borrando fotos..." #: gpixpod.py:548 msgid "Deleting photos..." msgstr "Borrando fotos..." #: gpixpod.py:567 msgid "Deleting albums..." msgstr "Borrando albumes" #: gpixpod.py:592 msgid "Saving Photo Database..." msgstr "Guardando la Photo Database" #: gpixpod.py:593 msgid "Please wait... (it could take several minutes!)" msgstr "Por favor espere... (puede demorar algunos minutos!)" #: gpixpod.py:604 msgid "Photo Database saved." msgstr "Photo Database guardada." #: gpixpod.py:632 gpixpod.py:775 gpixpod.py:1321 msgid "No full resolution image" msgstr "No existe imagen de máxima resolución" #: gpixpod.py:640 msgid "Photo Albums" msgstr "Albumes de Fotos" #: gpixpod.py:643 msgid "Previews" msgstr "" #: gpixpod.py:813 msgid "To be created" msgstr "" #: gpixpod.py:816 msgid "Available" msgstr "" #: gpixpod.py:817 #, fuzzy, python-format msgid "" "Photo Album:\t\t%s\n" "Photo Album ID:\t%s\n" "Number of photos:\t%s\n" "Status:\t\t%s" msgstr "" "Álbum de Fotos: %s \n" "Foto ID: %s \n" "Camino a la foto path: %s \n" "Tamaño original: %sx%s \n" "Status: %s" #: gpixpod.py:832 msgid "Saved" msgstr "Guardado" #: gpixpod.py:835 msgid "Not saved yet" msgstr "Aún no fue guardado" #: gpixpod.py:836 #, fuzzy, python-format msgid "" "Photo Album: %s \n" "Photo ID: %s \n" "Photo path: %s \n" "Original size: %sx%s \n" "Status: %s" msgstr "" "Álbum de Fotos: %s \n" "Foto ID: %s \n" "Camino a la foto path: %s \n" "Tamaño original: %sx%s \n" "Status: %s" #: gpixpod.py:847 msgid "Full resolution photo not found. Maybe have you deleted it?" msgstr "Foto con máxima resolución no encotrada. Ud la borró?" #: gpixpod.py:949 msgid "Rename photo album" msgstr "Renombrar álbum de fotos" #: gpixpod.py:949 #, python-format msgid "Enter the new name for the album \"%s\"" msgstr "Ingrese el nuevo nombre para el álbum \"%s\"" #: gpixpod.py:1062 msgid "" "iPod ejected, but the Do not disconnect message seems to be " "still shown on the iPod." msgstr "" "iPod expulsado, pero el mensaje Do not disconnect parece " "seguir mostrándose en el iPod." #: gpixpod.py:1063 msgid "" "To let disappear the message from the iPod, make your eject " "executable setuid root" msgstr "" "Para hacer desaparecer el mensaje en el iPod, haga a eject ejecutable " "setuid root" #: gpixpod.py:1064 msgid "(chmod +s `which eject` as root)" msgstr "(chmod +s `which eject` como root)" #: gpixpod.py:1070 #, fuzzy msgid "There are unsaved changes" msgstr "No hay cambios no guardados." #: gpixpod.py:1070 #, fuzzy msgid "Do you want to save your changes before closing this program?" msgstr "Quiere guardar antes de salir?" #: gpixpod.py:1237 #, fuzzy msgid " (with full resolution)" msgstr "con máxima resolución" #: gpixpod.py:1239 #, fuzzy msgid " (without full resolution)" msgstr "sin máxima resolución" #: gpixpod.py:1327 msgid "Not existing photo!" msgstr "" #: gpixpod.py:1327 msgid "The number is not a valid Photo ID." msgstr "" #: gpixpod.py:1334 msgid "Delete all photo album references?" msgstr "" #: gpixpod.py:1334 msgid "No photos will be deleted, but all the photo album references." msgstr "" #: gpixpod.py:1376 msgid "available after saved" msgstr "disponible luego de guardarse" #: gpixpod.py:1385 msgid "Full resolution image will be deleted after saved" msgstr "La imagen de máxima resolución será borrada luego de guardar" #: gpixpod.py:1394 msgid "Are you sure to delete the whole album?" msgstr "Esta seguro que desea borrar todo el álbum?" #: gpixpod.py:1471 msgid "Previously unreferenced photos" msgstr "" #: gpixpod.py:1485 msgid "No unreferenced photos" msgstr "" #: gpixpod.py:1485 msgid "" "There are not unreferenced photos. Each existing photo is linked to at least " "one photo album." msgstr "" #: gpixpod.py:1489 msgid "Are you sure to remove thumbnails cache?" msgstr "Esta seguro que desea borrar el cache de thumbnails?" #: gpixpod.py:1490 msgid "" "Thumbnails cache is automatically removed when saving, only in rare cases " "you would remove it manually.\n" msgstr "" "El cache de thumbnails es borrado automaticamente al guardar, solo en casos " "extraños debe ser borrado manualmente.\n" #: gpixpod.py:1491 msgid "" "It is recommended to cancel right away, unless you know what are you " "doing." msgstr "" "Se recomiendo cancelar ahora, a menos que tenga idea de lo que está " "haciendo." #: gpixpod.py:1495 msgid "Thumbnails cache removed!" msgstr "Cache de thumbnails borrado!" #: gpixpod.py:1502 msgid "Nothing has been modified. Photo Database seems in a proper status." msgstr "" #: gpixpod.py:1504 msgid "" "Photo Database has been modified. Please save now to complete the rescue." msgstr "" #: gpixpod.py:1527 #, python-format msgid "%s photos in %s albums" msgstr "" #: gpixpod.py:1660 msgid "" "Full resolution copies of your photos will be uploaded on your iPod" msgstr "" "Copia de sus fotos con máxima resolución serán guardadas en su iPod" #: gpixpod.py:1662 msgid "" "Full resolution copies of your photos will not be uploaded on your iPod" msgstr "" "Copia de sus fotos con máxima resolución no serán guardadas en su iPod" #: gpixpod.py:1663 msgid "" "This is now the default action of drag & drop and of the Add Photo button. You can always use the Add Photo with/without full resolution buttons to override this setting" msgstr "" "Esta es ahora la acción de drag & drop y del botón Agregar Foto." "Ud. siempre podrá utilizar los botones Agregar foto con/sin máxima " "resolución para sobreescribir esta configuración." #: gpixpod.py:1697 msgid "GPixPod development needs your help." msgstr "" #: gpixpod.py:1698 msgid "" "If you like GPixPod, please consider buying it from http://www.flagar.com/" "en/software/gpixpod or donating by PayPal (then you could ask the " "author to remove this message). Proceed now?" msgstr "" #: gpixpod.py:1724 #, fuzzy, python-format msgid "iPod %s and existing Photo Database found." msgstr "iPod con una Photo Database encontrados." #: gpixpod.py:1724 #, python-format msgid "" "Do you want to load the Photo Database found in the iPod attached to %" "s?" msgstr "" "Desea cargar la Photo Database encontrada en el iPod conectado a %s?" #: gpixpod.py:1727 #, fuzzy, python-format msgid "iPod %s found, attached to %s." msgstr "iPod encontrado, conectado a %s" #: gpixpod.py:1730 msgid "" "But model not autodetected. You should really specify your iPod model " "in Preferences now." msgstr "" #: gpixpod.py:1740 msgid "All the unsaved changes will be lost." msgstr "Todos los cambios que no fueron guardados se perderán." #: gpixpod.py:1742 msgid "There are not unsaved changes." msgstr "No hay cambios no guardados." #: gpixpod.py:1743 msgid "iPod disconnected. Do you want to exit now?" msgstr "iPod desconectado. Desea salir ahora?" #: gpixpod.py:1746 msgid "" "Please reconnect the iPod and without loading again the Photo " "Database" msgstr "" "Por favor reconecte el iPod y sin cargar nuevamente la Photo " "Database" #: gpixpod.py:1747 msgid "" "Not reconnecting means that you could not save your changes, resulting in a " "crash. And re-opening would abort all changes" msgstr "" "No reconectar significa que no se guardarán los cambios, resultando en un " "crash. Re-abriendo abortará los cambios." #: gpixpod_cli.py:76 #, fuzzy, python-format msgid "Loading Photo Database '%s'... " msgstr "Guardando la Photo Database" #: gpixpod_cli.py:82 #, fuzzy, python-format msgid "Selected iPod model is: %s" msgstr "Seleccione carpeta para punto de montaje del iPod" #: gpixpod_cli.py:84 #, python-format msgid "Autodetected iPod model is: %s" msgstr "" #: gpixpod_cli.py:87 #, fuzzy, python-format msgid "Photo Database contains %s albums" msgstr "Photo Database guardada." #: gpixpod_cli.py:89 #, python-format msgid "- '%s', containing %s pictures" msgstr "" #: gpixpod_cli.py:95 #, python-format msgid "Photo album '%s' does not exist, creating it... " msgstr "" #: gpixpod_cli.py:101 #, python-format msgid "created (n. %s)." msgstr "" #: gpixpod_cli.py:104 #, fuzzy msgid "Full resolution image will be regularly copied" msgstr "La imagen de máxima resolución será borrada luego de guardar" #: gpixpod_cli.py:106 #, fuzzy msgid "Full resolution image will NOT be copied" msgstr "La imagen de máxima resolución será borrada luego de guardar" #: gpixpod_cli.py:108 msgid "clockwise" msgstr "" #: gpixpod_cli.py:108 msgid "counter-clockwise" msgstr "" #: gpixpod_cli.py:109 #, python-format msgid "Detected portrait images will be automatically rotated %s" msgstr "" #: gpixpod_cli.py:110 #, python-format msgid "Image uploading behaviour is: %s" msgstr "" #: gpixpod_cli.py:111 #, fuzzy, python-format msgid "Adding pictures to the photo album '%s'" msgstr "Agregar un nuevo álbum de fotos" #: gpixpod_cli.py:117 msgid "Press Ctrl-c to stop adding images" msgstr "" #: gpixpod_cli.py:128 #, python-format msgid "" "\n" "Photo '%s' is already stored in Photo Database." msgstr "" #: gpixpod_cli.py:131 #, fuzzy, python-format msgid "" "\n" "Adding photo '%s'... " msgstr "Agregando fotos..." #: gpixpod_cli.py:141 #, fuzzy, python-format msgid "added (%s of %s - %s%%)." msgstr "%s de %s - %s%%" #: gpixpod_cli.py:149 #, fuzzy msgid "Saving Photo Database... " msgstr "Guardando la Photo Database" #: gpixpod_cli.py:159 #, fuzzy msgid "saved" msgstr "Guardado" #: gpixpod_cli.py:162 msgid "" "\n" "No changes done. Nothing to save!" msgstr "" #: gpixpod_cli.py:165 #, fuzzy msgid "Saving synchronization status... " msgstr "Guardando la Photo Database" #: gpixpod_cli.py:170 #, fuzzy msgid "saved." msgstr "Guardado" #: gpixpod_cli.py:172 msgid "All done." msgstr "" #: gpixpod_cli.py:187 msgid "The mountpoint of your connected iPod" msgstr "" #: gpixpod_cli.py:188 msgid "" "Manually provide iPod model (Nano|Photo|Color|5G) avoiding autodetection" msgstr "" #: gpixpod_cli.py:189 msgid "Skip synchronization and duplicate checking" msgstr "" #: gpixpod_cli.py:190 msgid "Do not display detailed output" msgstr "" #: gpixpod_cli.py:191 msgid "Name of the photo album to use. It will be created if absent." msgstr "" #: gpixpod_cli.py:192 msgid "Add images from directories recursively" msgstr "" #: gpixpod_cli.py:193 #, fuzzy msgid "Do not copy full resolution image" msgstr "No existe imagen de máxima resolución" #: gpixpod_cli.py:194 msgid "Zoom images instead of fit" msgstr "" #: gpixpod_cli.py:195 msgid "Stretch images instead of fit" msgstr "" #: gpixpod_cli.py:196 msgid "Rotate automatically portrait images clockwise" msgstr "" #: gpixpod_cli.py:197 msgid "Rotate automatically portrait images counter-clockwise" msgstr "" #: gpixpod_cli.py:219 #, fuzzy msgid "Invalid iPod mountpoint" msgstr "Punto de montaje del iPod: " #: gpixpod.glade.h:1 msgid " " msgstr " " #: gpixpod.glade.h:2 msgid "10 seconds" msgstr "" #: gpixpod.glade.h:3 msgid "15 seconds" msgstr "" #: gpixpod.glade.h:4 msgid "20 seconds" msgstr "" #: gpixpod.glade.h:5 msgid "3 seconds" msgstr "" #: gpixpod.glade.h:6 msgid "30 seconds" msgstr "" #: gpixpod.glade.h:7 msgid "5 seconds" msgstr "" #: gpixpod.glade.h:8 msgid "90° Clockwise" msgstr "" #: gpixpod.glade.h:9 msgid "90° Counter-clockwise" msgstr "" #: gpixpod.glade.h:10 #, fuzzy msgid "Default behaviour for images" msgstr "Presentación de imagenes del iPod" #: gpixpod.glade.h:12 msgid "Image behaviour" msgstr "" #: gpixpod.glade.h:13 #, fuzzy msgid "Photos" msgstr "Photo Database encontrada" #: gpixpod.glade.h:14 msgid "Rotate portrait images automatically" msgstr "" #: gpixpod.glade.h:15 msgid "Slideshow current iPod image" msgstr "Presentación de imagenes del iPod" #: gpixpod.glade.h:17 msgid "TV output iPod image" msgstr "Salida TV de imagen del iPod" #: gpixpod.glade.h:18 #, fuzzy msgid "Thumbnails cache" msgstr "Cache de thumbnails borrado!" #: gpixpod.glade.h:19 #, fuzzy msgid "iPod options" msgstr "Imagen pequeña de iPod" #: gpixpod.glade.h:20 msgid "iPod info" msgstr "" #: gpixpod.glade.h:21 msgid "About GPixPod" msgstr "Acerca de GPixPod" #: gpixpod.glade.h:22 msgid "About GPixPod by Flavio Gargiulo" msgstr "Acerca de GPixPod por Flavio Gargiulo" #: gpixpod.glade.h:23 msgid "Add Al_bum" msgstr "Agregar Al_bum" #: gpixpod.glade.h:24 msgid "Add Album" msgstr "Agregar Album" #: gpixpod.glade.h:25 #, fuzzy msgid "Add Full Resolution to photo" msgstr "Copiar la foto al iPod en su resolución máxima" #: gpixpod.glade.h:26 #, fuzzy msgid "Add Photo reference" msgstr "GPixPod Preferencias" #: gpixpod.glade.h:27 msgid "Add Photo(s)" msgstr "Agregar Foto(s)" #: gpixpod.glade.h:28 msgid "Add _Photo" msgstr "Agregar _Foto" #: gpixpod.glade.h:29 msgid "Add a full resolution image to the current photo" msgstr "Agregar una imagen de resolución máxima a la foto" #: gpixpod.glade.h:30 msgid "Add a new photo album" msgstr "Agregar un nuevo álbum de fotos" #: gpixpod.glade.h:31 msgid "Add a photo to the current selected album" msgstr "Agregar una foto al álbum seleccionado" #: gpixpod.glade.h:32 #, fuzzy msgid "" "Add a reference to an existing photo in the currently selected photo album" msgstr "Agregar una foto al álbum seleccionado" #: gpixpod.glade.h:33 msgid "Add info - GPixPod" msgstr "Agregar Info - GPixPod" #: gpixpod.glade.h:34 msgid "Advanced options" msgstr "" #: gpixpod.glade.h:35 msgid "Ask before opening default Photo Database" msgstr "Preguntar antes de abrir la Photo Database por defecto" #: gpixpod.glade.h:36 msgid "" "Autodetect every time\n" "iPod Nano\n" "iPod Photo\n" "iPod Color\n" "iPod Video/5G" msgstr "" #: gpixpod.glade.h:41 msgid "Autodetect iPod connection/disconnection" msgstr "Autodetectar conexión/desconexión del iPod" #: gpixpod.glade.h:42 #, fuzzy msgid "Available storage:" msgstr "disponible luego de guardarse" #: gpixpod.glade.h:43 msgid "Cancel" msgstr "Cancelar" #: gpixpod.glade.h:44 msgid "Check the Photo Database for incongruences and try to fix it" msgstr "" #: gpixpod.glade.h:45 msgid "Choose File - GPixPod" msgstr "Seleccionar archivo - GPixPod" #: gpixpod.glade.h:46 #, fuzzy msgid "Copy full resolution image" msgstr "No existe imagen de máxima resolución" #: gpixpod.glade.h:47 #, fuzzy msgid "Copy full resolution image on the iPod" msgstr "Copiar la foto al iPod en su resolución actual" #: gpixpod.glade.h:48 msgid "Defaults" msgstr "Defecto" #: gpixpod.glade.h:49 #, fuzzy msgid "Delete reference" msgstr "Usar preferencias por defecto" #: gpixpod.glade.h:50 msgid "Delete the current selected item" msgstr "Borrar el item seleccionado" #: gpixpod.glade.h:51 #, fuzzy msgid "Delete the references related to the current selected item" msgstr "Borrar el item seleccionado" #: gpixpod.glade.h:52 msgid "Device:" msgstr "" #: gpixpod.glade.h:53 msgid "Disabled" msgstr "" #: gpixpod.glade.h:54 msgid "Disconnect iPod" msgstr "Desconectar el iPod" #: gpixpod.glade.h:55 msgid "E_ject iPod" msgstr "E_yectar iPod" #: gpixpod.glade.h:56 msgid "Eject iPod" msgstr "Eyectar iPod" #: gpixpod.glade.h:57 msgid "Fit" msgstr "" #: gpixpod.glade.h:58 msgid "" "Flavio Gargiulo (Italian)\n" "Diego Belotti (Spanish)" msgstr "" #: gpixpod.glade.h:60 msgid "Full Resolution" msgstr "Resolución máxima" #: gpixpod.glade.h:61 #, fuzzy msgid "Full resolution image" msgstr "Resolución máxima" #: gpixpod.glade.h:62 msgid "GPixPod Preferences" msgstr "GPixPod Preferencias" #: gpixpod.glade.h:63 msgid "Generate a new photo album containing all unreferenced orphaned photos" msgstr "" #: gpixpod.glade.h:64 msgid "" "Help GPixPod development with a PayPal donation! You will choose the amount, " "freely." msgstr "" "Ayuda el desarrollo de GPixPod con una donación via PayPal! Sentite libre de " "elegir el monto de la donación." #: gpixpod.glade.h:65 msgid "" "If you really want to clean cache, do it only after saved and quit right " "after!" msgstr "" "Si realmente quieres borrar el cache, hazlo luego de guardar e " "inmediatamente después debes salir!" #: gpixpod.glade.h:66 msgid "Informations about the connected iPod" msgstr "" #: gpixpod.glade.h:67 msgid "Keep stored on the computer" msgstr "" #: gpixpod.glade.h:68 #, fuzzy msgid "Keep stored on the iPod" msgstr "Por favor conecte el iPod..." #: gpixpod.glade.h:69 msgid "List _previews" msgstr "" #: gpixpod.glade.h:70 #, fuzzy msgid "Mountpoint:" msgstr "Punto de montaje del iPod: " #: gpixpod.glade.h:71 msgid "New Album from unreferenced photos" msgstr "" #: gpixpod.glade.h:72 msgid "OK" msgstr "OK" #: gpixpod.glade.h:73 msgid "Organize photos on your iPod, freely!\n" msgstr "Organice sus fotos en el iPod, gratis!\n" #: gpixpod.glade.h:75 #, fuzzy msgid "Photo Album" msgstr "Albumes de Fotos" #: gpixpod.glade.h:76 #, fuzzy msgid "Photo Database:" msgstr "Photo Database guardada." #: gpixpod.glade.h:77 msgid "Please specify info:" msgstr "Por favor especifique info:" #: gpixpod.glade.h:78 msgid "Progress status..." msgstr "Progreso..." #: gpixpod.glade.h:79 msgid "Remove Full Resolution from photo" msgstr "Borrar máxima resolución de la foto" #: gpixpod.glade.h:80 msgid "Remove the full resolution image from the current photo" msgstr "Borrar la imagen de máxima resolución de la foto" #: gpixpod.glade.h:81 #, fuzzy msgid "Rename the currently selected photo album" msgstr "Borrar el item seleccionado" #: gpixpod.glade.h:82 msgid "Scan directories recursively" msgstr "" #: gpixpod.glade.h:83 msgid "Select iPod folder mountpoint" msgstr "Seleccione carpeta para punto de montaje del iPod" #: gpixpod.glade.h:84 msgid "Show fullscreen" msgstr "" #: gpixpod.glade.h:85 msgid "Skip duplicates, images already in Photo Database" msgstr "" #: gpixpod.glade.h:86 msgid "Slideshow" msgstr "" #: gpixpod.glade.h:87 msgid "Start Slideshow" msgstr "" #: gpixpod.glade.h:88 msgid "Storage:" msgstr "" #: gpixpod.glade.h:89 msgid "Stretch" msgstr "" #: gpixpod.glade.h:90 msgid "Thumbnails" msgstr "Thumbnails" #: gpixpod.glade.h:91 msgid "Tool_bar" msgstr "" #: gpixpod.glade.h:92 msgid "Total storage:" msgstr "" #: gpixpod.glade.h:93 msgid "Used for photos:" msgstr "" #: gpixpod.glade.h:94 msgid "Used storage:" msgstr "" #: gpixpod.glade.h:95 msgid "Zoom" msgstr "" #: gpixpod.glade.h:97 #, no-c-format msgid "Zoom (%):" msgstr "Zoom (%)" #: gpixpod.glade.h:98 msgid "_About" msgstr "_Acerca de" #: gpixpod.glade.h:99 msgid "_Clean cache" msgstr "_Borrar cache" #: gpixpod.glade.h:100 msgid "_Delete" msgstr "_Borrar" #: gpixpod.glade.h:101 msgid "_Details pane" msgstr "" #: gpixpod.glade.h:102 msgid "_Donate!" msgstr "_Donación!" #: gpixpod.glade.h:103 msgid "_Edit" msgstr "_Editar" #: gpixpod.glade.h:104 msgid "_Export" msgstr "" #: gpixpod.glade.h:105 msgid "_File" msgstr "_Archivo" #: gpixpod.glade.h:106 msgid "_Help" msgstr "_Ayuda" #: gpixpod.glade.h:107 #, fuzzy msgid "_Rename album" msgstr "Renombrar álbum de fotos" #: gpixpod.glade.h:108 msgid "_Rescue database" msgstr "" #: gpixpod.glade.h:109 msgid "_Tools" msgstr "_Herramientas" #: gpixpod.glade.h:110 #, fuzzy msgid "_View" msgstr "_Archivo" #: gpixpod.glade.h:111 #, fuzzy msgid "iPod TV output image" msgstr "Salida TV de imagen del iPod" #: gpixpod.glade.h:112 #, fuzzy msgid "iPod fullscreen image" msgstr "Imagen fullscren de iPod" #: gpixpod.glade.h:113 msgid "iPod info" msgstr "" #: gpixpod.glade.h:114 #, fuzzy msgid "iPod list image" msgstr "Guardar imágen" #: gpixpod.glade.h:115 msgid "iPod model:" msgstr "" #: gpixpod.glade.h:116 #, fuzzy msgid "iPod model: " msgstr "Punto de montaje del iPod: " #: gpixpod.glade.h:117 msgid "iPod mountpoint: " msgstr "Punto de montaje del iPod: " #: gpixpod.glade.h:118 #, fuzzy msgid "iPod slideshow image" msgstr "Guardar imágen" #: gpixpod.glade.h:119 msgid "i_Pod info" msgstr "" #~ msgid "" #~ "You are using an iPod %s. Your model is considered as an iPod " #~ "Photo and nothing is guaranteed to work." #~ msgstr "" #~ "Estás usando un iPod. Tu modelo es considerado como un iPod Photo " #~ "y no se garantiza que funcione." #~ msgid "" #~ "Create backups before. You have been warned! And please, report " #~ "your feedback!" #~ msgstr "" #~ "Debe crear respaldos antes. Usted ha sido advertido! Y por favor, " #~ "realimente con su experiencia!" #~ msgid "There are changes not saved." #~ msgstr "Existen cambios no guardados." #~ msgid "Add Photo with full resolution" #~ msgstr "Agregar foto con máxima resolución" #~ msgid "" #~ "Add Photo with full resolution image regardless of the global setting" #~ msgstr "" #~ "Agregar foto con máxima resolución a pesar de la configuración global" #~ msgid "Add Photo without full resolution" #~ msgstr "Agregar foto sin máxima resolución" #~ msgid "" #~ "Add Photo without full resolution image regardless of the global setting" #~ msgstr "" #~ "Agregar foto sin máxima resolución a pesar de la configuración global" #~ msgid "translator-credits" #~ msgstr "Diego Belotti " #~ msgid "" #~ "Image has been just added. Its details will be available after saved." #~ msgstr "" #~ "La imagen a sido agregada. Sus detalles estarán disponibles luego de " #~ "guardar." gpixpod-0.6.2/po/it/0000755000175000017500000000000010513202110013456 5ustar flagarflagargpixpod-0.6.2/po/it/LC_MESSAGES/0000755000175000017500000000000010513202110015243 5ustar flagarflagargpixpod-0.6.2/po/it/LC_MESSAGES/gpixpod.mo0000644000175000017500000005471110513121466017300 0ustar flagarflagar="0+\a!0    # - 8BQh/#JN%t+ ++#W{83b890-:h      0F)\J'  *1\w' %')CC( Eg2< &C"Lo ;.<(O9x!] Vr ' = /! ;!F! X!%y!!^!""(("1Q".";""F#TI##Oe$$ $$%$I %S%?k%R%%&2&A&Ha& &=&"&'>''f'y}' 'C(I( L(OZ(&( (}([) ))!*I4*~**0*T**+E+/Z+"++++!+7 ,B,)U,,.,6, ,- -"-!@-b--!---1-+. H.R.b.k.s.%.#.].9/X/r/ .0h900 0`0+141C1 T1b1k|11112 2-2 52C2L2R2Z2`2 f2t22222 22223#3 93C3 S3 _3l3~3 333m3 5/:55j555 5525&6 A6 L6 W6 b6 l6 w66667637Q67U7(7@8H8f88 88/858"*9)M9w9?949k:t:3:1:):2;K;i;*;;;#;; <<=(<f<'<X</=7=P=p=1===/=!>2>.D>'s>4>C>2?"G? j?v?E?y?J@<R@@ @"@)@@1ADAXAJwAA2AB,B:GBBB B BB$B#CZ9CNC"CADHD WDeD"{D(DDfD5EFE0WE;E9E?E>FSSFTFFTG*H+3H_H hHNHH;H^/I'III'IR J]JAtJ%JJSJDKcKKRKQLTL_iL)L LMM N-N#IN\mNNN/NU"OxOO9O2O !P+PHP#`P8PP"P"P8Q<SQQQ!QQ2Q"R*=R*hR%RR1R6S :SHS \SgS'oS!S#S`S">TaTT GUQUU UbU\VsVVVVxV 8WDW(UW ~WWWWW WWWWWX X 'X3XOXhXxXX1XX!XY&YBYSYeY YYYY2{)^K[- S;4\my5eG s#c`EXhuv(JiV W*jDN]l,P@tB}L+U%pTz&=H'.I!b<6_Ra|Mw gCrF/$nOx9q>YkA7?0Z~od3"18f :Q Adding photo '%s'... No changes done. Nothing to save! Photo '%s' is already stored in Photo Database. (with full resolution) (without full resolution)%s of %s - %s%%(chmod +s `which eject` as root)- '%s', containing %s pictures10 seconds15 seconds20 seconds3 seconds30 seconds5 seconds90° Clockwise90° Counter-clockwise%s photos in %s albumsAre you sure to remove thumbnails cache?Default behaviour for imagesFull resolution copies of your photos will be uploaded on your iPodFull resolution copies of your photos will not be uploaded on your iPodFullscreen iPod imageGPixPod development needs your help.Image behaviourNot existing photo!Photo Database found.PhotosQuit now?Rotate portrait images automaticallySlideshow current iPod imageSmall iPod thumbnailTV output iPod imageThumbnails cacheiPod %s and existing Photo Database found.iPod %s found, attached to %s.iPod ejected, but the Do not disconnect message seems to be still shown on the iPod.iPod optionsiPod disconnected. Do you want to exit now?iPod found, attached to %s.No full resolution imageiPod infoA Photo Database already existsAbout GPixPodAbout GPixPod by Flavio GargiuloAdd Al_bumAdd AlbumAdd Full Resolution to photoAdd Photo referenceAdd Photo(s)Add _PhotoAdd a full resolution image to the current photoAdd a new photo albumAdd a photo to the current selected albumAdd a reference to an existing photo in the currently selected photo albumAdd images from directories recursivelyAdd info - GPixPodAdd new photo albumAdding albums...Adding full resolution images to photos...Adding photo references...Adding photos...Adding pictures to the photo album '%s'Advanced optionsAll done.All the unsaved changes will be lost.Are you sure to delete the whole album?Ask before opening default Photo DatabaseAutodetect every time iPod Nano iPod Photo iPod Color iPod Video/5GAutodetect iPod connection/disconnectionAutodetected iPod model is: %sAvailableAvailable storage:But Photo Database not found. Do you want to create a new one?But model not autodetected. You should really specify your iPod model in Preferences now.CancelCheck the Photo Database for incongruences and try to fix itChoose File - GPixPodChoose photoCopy full resolution imageCopy full resolution image on the iPodDefaultsDelete all photo album references?Delete referenceDelete the current selected itemDelete the references related to the current selected itemDeleting albums...Deleting full resolution images from photos...Deleting photo references...Deleting photos...Deleting whole photo album references...Detected portrait images will be automatically rotated %sDevice:DisabledDisconnect iPodDo not copy full resolution imageDo not display detailed outputDo you want to load the Photo Database found in the iPod attached to %s?Do you want to load the Photo Database found in the iPod attached to %s?Do you want to overwrite it completely?Do you want to save your changes before closing this program?E_ject iPodEject iPodEnter a Photo ID:Enter the name of the new album:Enter the new name for the album "%s"FitFlavio Gargiulo (Italian) Diego Belotti (Spanish)Full ResolutionFull resolution imageFull resolution image will NOT be copiedFull resolution image will be deleted after savedFull resolution image will be regularly copiedFull resolution photo not found. Maybe have you deleted it?GPixPod PreferencesGenerate a new photo album containing all unreferenced orphaned photosHelp GPixPod development with a PayPal donation! You will choose the amount, freely.If you like GPixPod, please consider buying it from http://www.flagar.com/en/software/gpixpod or donating by PayPal (then you could ask the author to remove this message). Proceed now?If you really want to clean cache, do it only after saved and quit right after!ImageImage uploading behaviour is: %sImagesInformations about the connected iPodInterruption requested. Please wait while saving at the current status...Invalid iPod mountpointIt does not have a color screen and it does not support photos!It is recommended to cancel right away, unless you know what are you doing.Keep stored on the computerKeep stored on the iPodList _previewsLoading Photo Database '%s'... Manually provide iPod model (Nano|Photo|Color|5G) avoiding autodetectionMountpoint:Name of the photo album to use. It will be created if absent.New Album from unreferenced photosNew Photo AlbumNo photos will be deleted, but all the photo album references.No unreferenced photosNot reconnecting means that you could not save your changes, resulting in a crash. And re-opening would abort all changesNot saved yetNothing has been modified. Photo Database seems in a proper status.OKOpening %s...Opening Photo Database... It could take several minutes, depending on its size!Organize photos on your iPod, freely! Photo AlbumPhoto Album: %s Photo Album ID: %s Number of photos: %s Status: %sPhoto Album: %s Photo ID: %s Photo path: %s Original size: %sx%s Status: %sPhoto AlbumsPhoto Database %s opened.Photo Database contains %s albumsPhoto Database has been modified. Please save now to complete the rescue.Photo Database saved.Photo Database:Photo album '%s' does not exist, creating it... Please reconnect the iPod and without loading again the Photo DatabasePlease connect the iPod...Please specify info:Please wait... (it could take several minutes!)Press Ctrl-c to stop adding imagesPreviewsPreviously unreferenced photosProgress status...Remove Full Resolution from photoRemove the full resolution image from the current photoRename photo albumRename the currently selected photo albumRenaming albums...Rotate automatically portrait images clockwiseRotate automatically portrait images counter-clockwiseSave imageSavedSaving Photo Database...Saving Photo Database... Saving synchronization status... Scan directories recursivelySelect iPod folder mountpointSelect the iPod folder mountpointSelected iPod model is: %sShow fullscreenSkip duplicates, images already in Photo DatabaseSkip synchronization and duplicate checkingSlideshowStart SlideshowStorage:StretchStretch images instead of fitThe mountpoint of your connected iPodThe number is not a valid Photo ID.There are not unreferenced photos. Each existing photo is linked to at least one photo album.There are not unsaved changes.There are unsaved changesThis is now the default action of drag & drop and of the Add Photo button. You can always use the Add Photo with/without full resolution buttons to override this settingThumbnailsThumbnails cache is automatically removed when saving, only in rare cases you would remove it manually. Thumbnails cache removed!To be createdTo let disappear the message from the iPod, make your eject executable setuid rootTool_barTotal storage:Used for photos:Used storage:Using default preferencesYou are using an iPod %s. Please quit now: your model is not supported, and it never will be.ZoomZoom (%):Zoom images instead of fit_About_Clean cache_Delete_Details pane_Donate!_Edit_Export_File_Help_Rename album_Rescue database_Tools_Viewadded (%s of %s - %s%%).available after savedclockwisecounter-clockwisecreated (n. %s).iPod HAL autodetection disablediPod TV output imageiPod fullscreen imageiPod infoiPod list imageiPod model:iPod model: iPod mountpoint: iPod slideshow imagei_Pod infosavedsaved.Project-Id-Version: gpixpod 0.3.4 Report-Msgid-Bugs-To: POT-Creation-Date: 2006-10-11 09:52+0200 PO-Revision-Date: 2006-10-11 09:54+0200 Last-Translator: Flavio Gargiulo Language-Team: Italian MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=2; plural=(n != 1); Inserimento della foto '%s'... Nessuna modifica apportata. Niente da salvare! La foto '%s' è già memorizzata nel Photo Database. con alta risoluzione (senza alta risoluzione)%s di %s - %s%%(chmod +s `which eject` come root)- '%s', contenente %s foto10 secondi15 secondi20 secondi3 secondi30 secondi5 secondi90° in senso orario90° in senso antiorario%s foto in %s albumSei sicuro di rimuovere le miniature temporanee?Immagine di iPod per la diapositiva correnteLe copie ad alta risoluzione delle tue foto verranno caricate sul tuo iPodLe copie ad alta risoluzione delle tue foto non verranno caricate sul tuo iPodImmagine di iPod a schermo interoIl tuo supporto è importante per lo sviluppo di GPixPod.Tipo di memorizzazioneTale foto non esiste!Photo Database trovato.FotoUscire adesso?Ruota immagini verticali automaticamenteImmagine dell'iPod per la diapositiva correnteMiniatura dell'iPod piccolaImmagine dell'iPod per l'uscita TVMiniature temporaneeRilevati iPod %s e Photo Database già esistente.iPod %s trovato, connesso a %s.iPod disconnesso, ma il messaggio Non Scollegare sembra essere ancora visualizzato sull'iPod.Opzioni iPodiPod disconnesso. Vuoi uscire adesso?iPod trovato, connesso a %s.Senza immagine ad alta risoluzioneInfo sull'iPodEsiste già un Photo DatabaseInformazioni su GPixPodInformazioni su GPixPod di Flavio GargiuloAggiungi Al_bumAggiungi AlbumAggiungi alta risoluzione alla fotoAggiungi riferimento a fotoAggiungi FotoAggiungi _FotoAggiungi un'immagine ad alta risoluzione per la foto correnteAggiungi un nuovo album di fotoAggiungi una foto all'album selezionatoAggiungi un riferimento ad una foto esistente nell'album di foto attualmente selezionatoAggiungi immagini dalle cartelle ricorsivamenteInserisci info - GPixPodAggiungi un nuovo album di fotoInserimento degli album...Inserimento delle immagini ad alta risoluzione...Inserimento delle foto...Inserimento delle foto...Le foto saranno aggiunte all'album di foto '%s'Opzioni avanzateTutto completato.Tutte le modifiche non salvate andranno perse.Sei sicuro di eliminare l'intero album?Chiedi prima di aprire il Photo Database predefinitoAutorileva ogni volta iPod Nano iPod Photo iPod Color iPod Video/5GAutorileva la connessione/disconnessione dell'iPodIl modello di iPod rilevato è: %sDisponibileSpazio disponibile:Ma nessun file Photo Database trovato. Vuoi crearne uno nuovo?Ma modello non rilevato. Dovresti specificare assolutamente il tuo modello di iPod nelle Preferenze adesso.AnnullaVerifica la validità del Photo Database e prova a ripararloScegli File - GPixPodScegli fotoCopia immagine ad alta risoluzioneCopia la foto a piena risoluzione su iPodOpzioni predefiniteEliminare tutti i riferimenti dell'album di foto?Elimina riferimentoElimina l'elemento selezionatoElimina tutti i riferimenti correlati all'elemento attualmente selezionatoEliminazione degli album...Eliminazione delle immagini ad alta risoluzione...Eliminazione delle foto...Eliminazione delle foto...Eliminazione di tutti i riferimenti degli album di foto...Le immagini rilevate verticali verranno automaticamente ruotate %sDispositivo:DisabilitatoDisconnetti iPodNon copiare foto ad alta risoluzioneNon visualizzare output dettagliatoVuoi caricare il file Photo Database trovato nell'iPod connesso a %s?Vuoi caricare il Photo Database trovato nell'iPod connesso a %s?Vuoi sovrascriverlo completamente?Vuoi salvare le tue modifiche prima di chiudere questo programma?S_collega iPodScollega iPodInserisci un ID Foto:Inserisci il nome del nuovo album:Inserisci il nuovo nome per l'album "%s"AdattaFlavio·Gargiulo··(Italiano) Diego·Belotti··(Spagnolo)Alta RisoluzioneAlta RisoluzioneL'immagine ad alta risoluzione NON sarà copiataL'immagine ad alta risoluzione sarà eliminata dopo salvatoL'immagine ad alta risoluzione sarà regolarmente copiataFoto a risoluzione massima non trovata. Forse l'hai cancellata?Preferenze - GPixPodGenera un nuovo album di foto contenente tutte le foto al momento senza riferimentiContribuisci con PayPal allo sviluppo di GPixPod! Sceglierai l'importo, liberamente.Se ti piace GPixPod, considera di acquistarlo da http://www.flagar.com/en/software/gpixpod oppure fai una donazione con PayPal (dopo magari chiedi all'autore di rimuovere questo messaggio). Procedi ora?Se vuoi davvero rimuovere la cache, fallo soltanto dopo salvato ed esci subito dopo!ImmagineIl tipo di memorizzazione delle foto è: %sImmaginiInformazioni sull'iPod collegatoRichiesta interruzione. Attendere prego mentre si salva fino a questo punto...Mountpoint iPod non validoNon ha uno schermo a colori e non supporta immagini e foto!Si consiglia di annullare subito, a meno che non sei convinto di ciò che stai facendo.Conservale sul disco fisso del computerConservale sull'iPod_Anteprime nell'elencoCaricamento del Photo Database '%s'... Specifica un modello di iPod manualmente (Nano|Photo|Color|5G) senza autorilevarloCartella base di iPod:Nome dell'album di foto da utilizzare. Verrà creato se mancante.Nuovo Album da foto senza riferimentiNuovo Album di FotoNon sarà eliminata nessuna foto, piuttosto tutti i riferimenti dell'album di foto.Nessuna foto senza riferimentiSe non lo riconnetti, non potrai salvare le tue modifiche, e otterrai un crash. E un nuovo caricamento annullerebbe tutte le modifiche.Non ancora salvataNon è stato modificato nulla. Il Photo Database sembra strutturato correttamente.OKCaricamento di %s...Caricamento del Photo Database... Può richiedere parecchi minuti, in base alle sue dimensioni!Archivia foto sul tuo iPod, liberamente! Album di FotoAlbum di Foto: %s ID album di foto: %s Numero di foto: %s Stato: %sAlbum di Foto: %s ID Foto: %s Percorso immagine: %s Dimensioni originali: %sx%s Stato: %sAlbum di FotoPhoto Database %s caricato.Il Photo Database contiene %s albumIl Photo Database è stato modificato. Per favore salva adesso per completare il ripristino.Photo Database salvato.Photo Database:Non esiste un album di foto '%s', da creare... Per favore ricollega l'iPod e senza caricare di nuovo il Photo DatabasePer favore collega l'iPod...Per favore specifica dettagli:Attendere prego... (potrebbe richiedere parecchi minuti!)Premere Ctrl-c per smettere di aggiungere immaginiAnteprimeFoto prima senza riferimentiStato di avanzamento...Elimina alta risoluzione dalla fotoElimina immagine ad alta risoluzione dalla foto correnteRinomina album di fotoRinomina album di foto selezionatoModifica dei titoli degli album...Ruota automaticamente immagini verticali in senso orarioRuota automaticamente immagini verticali in senso antiorarioSalva immagineSalvataSalvataggio del Photo Database...Salvataggio Photo Database...Memorizzazione dello stato di sincronizzazione... Esamina le cartelle ricorsivamenteSeleziona la cartella principale dell'iPodSeleziona la cartella principale dell'iPodIl modello di iPod selezionato è: %sMostra a schermo interoSalta duplicati, immagini già nel Photo DatabaseSalta la sincronizzazione e il controllo dei duplicatiPresentazioneAvvia presentazioneCapacità:EspandiEspandi le immagini invece di adattarleIl mountpoint dell'iPod collegatoIl numero non è un ID Foto valido.Non ci sono foto senza riferimenti. Ogni foto esistente è collegata in almeno un album di foto.Non ci sono modifiche non salvate.Ci sono modifiche non salvateQuesta adesso è l'azione predefinita del drag & drop e del pulsante Aggiungi Foto. Puoi sempre usare i pulsanti Aggiungi Foto con/senza alta risoluzione per ignorare questa opzioneMiniatureLe miniature temporanee sono rimosse automaticamente durante il salvataggio, solo in rari casi si cancellerebbero manualmente. Miniature temporanee rimosse!Da crearePer far scomparire il messaggio dall'iPod, rendi setuid root il tuo eseguibile eject_Barra degli strumentiCapacità totale:Usata per foto:Spazio utilizzato:Preferenze predefiniteStai utilizzando un iPod %s. Per favore esci subito: il tuo modello non è supportato, e non lo sarà mai.IngrandisciZoom (%):Ingrandisci immagini invece di adattarle_Informazioni_Cancella la cache_EliminaRiquadro _dettagliFai una _donazione!_Modifica_Esporta_FileA_iuto_Rinomina album di foto_Ripristina databaseS_trumenti_Visualizzainserita (%s di %s - %s%%).disponibile dopo salvatoin senso orarioin senso antiorariocreato (n. %s).Autorilevamento dell'iPod tramite HAL disattivatoImmagine di iPod per uscita TVImmagine di iPod a schermo interoInformazioni sull'iPodImmagine di iPod per elencoModello di iPod:Modello di iPod: Cartella base dell'iPod: Immagine di iPod per diapositiveInformazioni sull'_iPodsalvatosalvato.gpixpod-0.6.2/po/it/it.po0000644000175000017500000007235410513121442014455 0ustar flagarflagar# GPIXPOD - organize photos on your iPod, freely! # Copyright (C) 2006 FLAVIO GARGIULO # This file is distributed under the same license as the GPIXPOD package. # FLAVIO GARGIULO , 2006. # msgid "" msgstr "" "Project-Id-Version: gpixpod 0.3.4\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2006-10-11 09:52+0200\n" "PO-Revision-Date: 2006-10-11 09:54+0200\n" "Last-Translator: Flavio Gargiulo \n" "Language-Team: Italian \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);" #: gpixpod.py:56 msgid "Using default preferences" msgstr "Preferenze predefinite" #: gpixpod.py:60 gpixpod.py:62 msgid "Add new photo album" msgstr "Aggiungi un nuovo album di foto" #: gpixpod.py:60 gpixpod.py:62 msgid "Enter the name of the new album:" msgstr "Inserisci il nome del nuovo album:" #: gpixpod.py:61 gpixpod.py:63 msgid "New Photo Album" msgstr "Nuovo Album di Foto" #: gpixpod.py:64 gpixpod.py:65 gpixpod.py:91 gpixpod.py:97 gpixpod.py:1046 msgid "Images" msgstr "Immagini" #: gpixpod.py:67 msgid "Image" msgstr "Immagine" #: gpixpod.py:69 msgid "Choose photo" msgstr "Scegli foto" #: gpixpod.py:69 msgid "Enter a Photo ID:" msgstr "Inserisci un ID Foto:" #: gpixpod.py:82 msgid "Select the iPod folder mountpoint" msgstr "Seleziona la cartella principale dell'iPod" #: gpixpod.py:92 gpixpod.py:98 gpixpod.py:1047 msgid "Save image" msgstr "Salva immagine" #: gpixpod.py:158 msgid "Please connect the iPod..." msgstr "Per favore collega l'iPod..." #: gpixpod.py:219 msgid "iPod HAL autodetection disabled" msgstr "Autorilevamento dell'iPod tramite HAL disattivato" #: gpixpod.py:232 msgid "Photo Database found." msgstr "Photo Database trovato." #: gpixpod.py:232 #, python-format msgid "" "Do you want to load the Photo Database found in the iPod attached to " "%s?" msgstr "" "Vuoi caricare il file Photo Database trovato nell'iPod connesso a " "%s?" #: gpixpod.py:237 gpixpod.py:1730 #, python-format msgid "iPod found, attached to %s." msgstr "iPod trovato, connesso a %s." #: gpixpod.py:237 gpixpod.py:1727 msgid "But Photo Database not found. Do you want to create a new one?" msgstr "Ma nessun file Photo Database trovato. Vuoi crearne uno nuovo?" #: gpixpod.py:270 msgid "A Photo Database already exists" msgstr "Esiste già un Photo Database" #: gpixpod.py:270 msgid "Do you want to overwrite it completely?" msgstr "Vuoi sovrascriverlo completamente?" #: gpixpod.py:283 gpixpod.glade.h:11 msgid "Fullscreen iPod image" msgstr "Immagine di iPod a schermo intero" #: gpixpod.py:284 gpixpod.glade.h:16 msgid "Small iPod thumbnail" msgstr "Miniatura dell'iPod piccola" #: gpixpod.py:290 #, python-format msgid "" "You are using an iPod %s. Please quit now: your model is not " "supported, and it never will be." msgstr "" "Stai utilizzando un iPod %s. Per favore esci subito: il tuo " "modello non è supportato, e non lo sarà mai." #: gpixpod.py:291 msgid "It does not have a color screen and it does not support photos!" msgstr "Non ha uno schermo a colori e non supporta immagini e foto!" #: gpixpod.py:292 msgid "Quit now?" msgstr "Uscire adesso?" #: gpixpod.py:333 gpixpod_cli.py:176 msgid "" "Interruption requested. Please wait while saving at the current status..." msgstr "" "Richiesta interruzione. Attendere prego mentre si salva fino a questo " "punto..." #: gpixpod.py:356 #, python-format msgid "Opening %s..." msgstr "Caricamento di %s..." #: gpixpod.py:358 msgid "" "Opening Photo Database... It could take several minutes, depending on its " "size!" msgstr "" "Caricamento del Photo Database... Può richiedere parecchi minuti, in base " "alle sue dimensioni!" #: gpixpod.py:400 #, python-format msgid "Photo Database %s opened." msgstr "Photo Database %s caricato." #: gpixpod.py:424 msgid "Adding full resolution images to photos..." msgstr "Inserimento delle immagini ad alta risoluzione..." #: gpixpod.py:441 msgid "Deleting full resolution images from photos..." msgstr "Eliminazione delle immagini ad alta risoluzione..." #: gpixpod.py:459 msgid "Adding albums..." msgstr "Inserimento degli album..." #: gpixpod.py:474 msgid "Adding photo references..." msgstr "Inserimento delle foto..." #: gpixpod.py:488 msgid "Renaming albums..." msgstr "Modifica dei titoli degli album..." #: gpixpod.py:502 msgid "Adding photos..." msgstr "Inserimento delle foto..." #: gpixpod.py:513 gpixpod.py:560 gpixpod.py:576 #, python-format msgid "%s of %s - %s%%" msgstr "%s di %s - %s%%" #: gpixpod.py:520 msgid "Deleting photo references..." msgstr "Eliminazione delle foto..." #: gpixpod.py:534 msgid "Deleting whole photo album references..." msgstr "Eliminazione di tutti i riferimenti degli album di foto..." #: gpixpod.py:548 msgid "Deleting photos..." msgstr "Eliminazione delle foto..." #: gpixpod.py:567 msgid "Deleting albums..." msgstr "Eliminazione degli album..." #: gpixpod.py:592 msgid "Saving Photo Database..." msgstr "Salvataggio del Photo Database..." #: gpixpod.py:593 msgid "Please wait... (it could take several minutes!)" msgstr "Attendere prego... (potrebbe richiedere parecchi minuti!)" #: gpixpod.py:604 msgid "Photo Database saved." msgstr "Photo Database salvato." #: gpixpod.py:632 gpixpod.py:775 gpixpod.py:1321 msgid "No full resolution image" msgstr "Senza immagine ad alta risoluzione" #: gpixpod.py:640 msgid "Photo Albums" msgstr "Album di Foto" #: gpixpod.py:643 msgid "Previews" msgstr "Anteprime" #: gpixpod.py:813 msgid "To be created" msgstr "Da creare" #: gpixpod.py:816 msgid "Available" msgstr "Disponibile" #: gpixpod.py:817 #, python-format msgid "" "Photo Album:\t\t%s\n" "Photo Album ID:\t%s\n" "Number of photos:\t%s\n" "Status:\t\t%s" msgstr "" "Album di Foto: \t\t%s\n" "ID album di foto:\t%s\n" "Numero di foto:\t%s\n" "Stato:\t\t%s" #: gpixpod.py:832 msgid "Saved" msgstr "Salvata" #: gpixpod.py:835 msgid "Not saved yet" msgstr "Non ancora salvata" #: gpixpod.py:836 #, python-format msgid "" "Photo Album: %s \n" "Photo ID: %s \n" "Photo path: %s \n" "Original size: %sx%s \n" "Status: %s" msgstr "" "Album di Foto: %s \n" "ID Foto: %s \n" "Percorso immagine: %s \n" "Dimensioni originali: %sx%s \n" "Stato: %s" #: gpixpod.py:847 msgid "Full resolution photo not found. Maybe have you deleted it?" msgstr "Foto a risoluzione massima non trovata. Forse l'hai cancellata?" #: gpixpod.py:949 msgid "Rename photo album" msgstr "Rinomina album di foto" #: gpixpod.py:949 #, python-format msgid "Enter the new name for the album \"%s\"" msgstr "Inserisci il nuovo nome per l'album \"%s\"" #: gpixpod.py:1062 msgid "" "iPod ejected, but the Do not disconnect message seems to be " "still shown on the iPod." msgstr "" "iPod disconnesso, ma il messaggio Non Scollegare sembra essere " "ancora visualizzato sull'iPod." #: gpixpod.py:1063 msgid "" "To let disappear the message from the iPod, make your eject " "executable setuid root" msgstr "" "Per far scomparire il messaggio dall'iPod, rendi setuid root il tuo " "eseguibile eject" #: gpixpod.py:1064 msgid "(chmod +s `which eject` as root)" msgstr "(chmod +s `which eject` come root)" #: gpixpod.py:1070 msgid "There are unsaved changes" msgstr "Ci sono modifiche non salvate" #: gpixpod.py:1070 msgid "Do you want to save your changes before closing this program?" msgstr "Vuoi salvare le tue modifiche prima di chiudere questo programma?" #: gpixpod.py:1237 msgid " (with full resolution)" msgstr " con alta risoluzione" #: gpixpod.py:1239 msgid " (without full resolution)" msgstr " (senza alta risoluzione)" #: gpixpod.py:1327 msgid "Not existing photo!" msgstr "Tale foto non esiste!" #: gpixpod.py:1327 msgid "The number is not a valid Photo ID." msgstr "Il numero non è un ID Foto valido." #: gpixpod.py:1334 msgid "Delete all photo album references?" msgstr "Eliminare tutti i riferimenti dell'album di foto?" #: gpixpod.py:1334 msgid "No photos will be deleted, but all the photo album references." msgstr "" "Non sarà eliminata nessuna foto, piuttosto tutti i riferimenti dell'album di " "foto." #: gpixpod.py:1376 msgid "available after saved" msgstr "disponibile dopo salvato" #: gpixpod.py:1385 msgid "Full resolution image will be deleted after saved" msgstr "L'immagine ad alta risoluzione sarà eliminata dopo salvato" #: gpixpod.py:1394 msgid "Are you sure to delete the whole album?" msgstr "Sei sicuro di eliminare l'intero album?" #: gpixpod.py:1471 msgid "Previously unreferenced photos" msgstr "Foto prima senza riferimenti" #: gpixpod.py:1485 msgid "No unreferenced photos" msgstr "Nessuna foto senza riferimenti" #: gpixpod.py:1485 msgid "" "There are not unreferenced photos. Each existing photo is linked to at least " "one photo album." msgstr "" "Non ci sono foto senza riferimenti. Ogni foto esistente è collegata in " "almeno un album di foto." #: gpixpod.py:1489 msgid "Are you sure to remove thumbnails cache?" msgstr "Sei sicuro di rimuovere le miniature temporanee?" #: gpixpod.py:1490 msgid "" "Thumbnails cache is automatically removed when saving, only in rare cases " "you would remove it manually.\n" msgstr "" "Le miniature temporanee sono rimosse automaticamente durante il salvataggio, " "solo in rari casi si cancellerebbero manualmente.\n" #: gpixpod.py:1491 msgid "" "It is recommended to cancel right away, unless you know what are you " "doing." msgstr "" "Si consiglia di annullare subito, a meno che non sei convinto di ciò " "che stai facendo." #: gpixpod.py:1495 msgid "Thumbnails cache removed!" msgstr "Miniature temporanee rimosse!" #: gpixpod.py:1502 msgid "Nothing has been modified. Photo Database seems in a proper status." msgstr "" "Non è stato modificato nulla. Il Photo Database sembra strutturato " "correttamente." #: gpixpod.py:1504 msgid "" "Photo Database has been modified. Please save now to complete the rescue." msgstr "" "Il Photo Database è stato modificato. Per favore salva adesso per completare " "il ripristino." #: gpixpod.py:1527 #, python-format msgid "%s photos in %s albums" msgstr "%s foto in %s album" #: gpixpod.py:1660 msgid "" "Full resolution copies of your photos will be uploaded on your iPod" msgstr "" "Le copie ad alta risoluzione delle tue foto verranno caricate sul tuo " "iPod" #: gpixpod.py:1662 msgid "" "Full resolution copies of your photos will not be uploaded on your iPod" msgstr "" "Le copie ad alta risoluzione delle tue foto non verranno caricate sul tuo " "iPod" #: gpixpod.py:1663 msgid "" "This is now the default action of drag & drop and of the Add Photo button. You can always use the Add Photo with/without full resolution buttons to override this setting" msgstr "" "Questa adesso è l'azione predefinita del drag & drop e del pulsante " "Aggiungi Foto. Puoi sempre usare i pulsanti Aggiungi Foto con/" "senza alta risoluzione per ignorare questa opzione" #: gpixpod.py:1697 msgid "GPixPod development needs your help." msgstr "Il tuo supporto è importante per lo sviluppo di GPixPod." #: gpixpod.py:1698 msgid "" "If you like GPixPod, please consider buying it from http://www.flagar.com/" "en/software/gpixpod or donating by PayPal (then you could ask the " "author to remove this message). Proceed now?" msgstr "" "Se ti piace GPixPod, considera di acquistarlo da http://www.flagar.com/en/" "software/gpixpod oppure fai una donazione con PayPal (dopo magari " "chiedi all'autore di rimuovere questo messaggio). Procedi ora?" #: gpixpod.py:1724 #, python-format msgid "iPod %s and existing Photo Database found." msgstr "Rilevati iPod %s e Photo Database già esistente." #: gpixpod.py:1724 #, python-format msgid "" "Do you want to load the Photo Database found in the iPod attached to %" "s?" msgstr "" "Vuoi caricare il Photo Database trovato nell'iPod connesso a %s?" #: gpixpod.py:1727 #, python-format msgid "iPod %s found, attached to %s." msgstr "iPod %s trovato, connesso a %s." #: gpixpod.py:1730 msgid "" "But model not autodetected. You should really specify your iPod model " "in Preferences now." msgstr "" "Ma modello non rilevato. Dovresti specificare assolutamente il tuo " "modello di iPod nelle Preferenze adesso." #: gpixpod.py:1740 msgid "All the unsaved changes will be lost." msgstr "Tutte le modifiche non salvate andranno perse." #: gpixpod.py:1742 msgid "There are not unsaved changes." msgstr "Non ci sono modifiche non salvate." #: gpixpod.py:1743 msgid "iPod disconnected. Do you want to exit now?" msgstr "iPod disconnesso. Vuoi uscire adesso?" #: gpixpod.py:1746 msgid "" "Please reconnect the iPod and without loading again the Photo " "Database" msgstr "" "Per favore ricollega l'iPod e senza caricare di nuovo il Photo " "Database" #: gpixpod.py:1747 msgid "" "Not reconnecting means that you could not save your changes, resulting in a " "crash. And re-opening would abort all changes" msgstr "" "Se non lo riconnetti, non potrai salvare le tue modifiche, e otterrai un " "crash. E un nuovo caricamento annullerebbe tutte le modifiche." #: gpixpod_cli.py:76 #, python-format msgid "Loading Photo Database '%s'... " msgstr "Caricamento del Photo Database '%s'... " #: gpixpod_cli.py:82 #, python-format msgid "Selected iPod model is: %s" msgstr "Il modello di iPod selezionato è: %s" #: gpixpod_cli.py:84 #, python-format msgid "Autodetected iPod model is: %s" msgstr "Il modello di iPod rilevato è: %s" #: gpixpod_cli.py:87 #, python-format msgid "Photo Database contains %s albums" msgstr "Il Photo Database contiene %s album" #: gpixpod_cli.py:89 #, python-format msgid "- '%s', containing %s pictures" msgstr "- '%s', contenente %s foto" #: gpixpod_cli.py:95 #, python-format msgid "Photo album '%s' does not exist, creating it... " msgstr "Non esiste un album di foto '%s', da creare... " #: gpixpod_cli.py:101 #, python-format msgid "created (n. %s)." msgstr "creato (n. %s)." #: gpixpod_cli.py:104 msgid "Full resolution image will be regularly copied" msgstr "L'immagine ad alta risoluzione sarà regolarmente copiata" #: gpixpod_cli.py:106 msgid "Full resolution image will NOT be copied" msgstr "L'immagine ad alta risoluzione NON sarà copiata" #: gpixpod_cli.py:108 msgid "clockwise" msgstr "in senso orario" #: gpixpod_cli.py:108 msgid "counter-clockwise" msgstr "in senso antiorario" #: gpixpod_cli.py:109 #, python-format msgid "Detected portrait images will be automatically rotated %s" msgstr "Le immagini rilevate verticali verranno automaticamente ruotate %s" #: gpixpod_cli.py:110 #, python-format msgid "Image uploading behaviour is: %s" msgstr "Il tipo di memorizzazione delle foto è: %s" #: gpixpod_cli.py:111 #, python-format msgid "Adding pictures to the photo album '%s'" msgstr "Le foto saranno aggiunte all'album di foto '%s'" #: gpixpod_cli.py:117 msgid "Press Ctrl-c to stop adding images" msgstr "Premere Ctrl-c per smettere di aggiungere immagini" #: gpixpod_cli.py:128 #, python-format msgid "" "\n" "Photo '%s' is already stored in Photo Database." msgstr "" "\n" "La foto '%s' è già memorizzata nel Photo Database." #: gpixpod_cli.py:131 #, python-format msgid "" "\n" "Adding photo '%s'... " msgstr "" "\n" "Inserimento della foto '%s'... " #: gpixpod_cli.py:141 #, python-format msgid "added (%s of %s - %s%%)." msgstr "inserita (%s di %s - %s%%)." #: gpixpod_cli.py:149 msgid "Saving Photo Database... " msgstr "Salvataggio Photo Database..." #: gpixpod_cli.py:159 msgid "saved" msgstr "salvato" #: gpixpod_cli.py:162 msgid "" "\n" "No changes done. Nothing to save!" msgstr "" "\n" "Nessuna modifica apportata. Niente da salvare!" #: gpixpod_cli.py:165 msgid "Saving synchronization status... " msgstr "Memorizzazione dello stato di sincronizzazione... " #: gpixpod_cli.py:170 msgid "saved." msgstr "salvato." #: gpixpod_cli.py:172 msgid "All done." msgstr "Tutto completato." #: gpixpod_cli.py:187 msgid "The mountpoint of your connected iPod" msgstr "Il mountpoint dell'iPod collegato" #: gpixpod_cli.py:188 msgid "" "Manually provide iPod model (Nano|Photo|Color|5G) avoiding autodetection" msgstr "" "Specifica un modello di iPod manualmente (Nano|Photo|Color|5G) senza " "autorilevarlo" #: gpixpod_cli.py:189 msgid "Skip synchronization and duplicate checking" msgstr "Salta la sincronizzazione e il controllo dei duplicati" #: gpixpod_cli.py:190 msgid "Do not display detailed output" msgstr "Non visualizzare output dettagliato" #: gpixpod_cli.py:191 msgid "Name of the photo album to use. It will be created if absent." msgstr "Nome dell'album di foto da utilizzare. Verrà creato se mancante." #: gpixpod_cli.py:192 msgid "Add images from directories recursively" msgstr "Aggiungi immagini dalle cartelle ricorsivamente" #: gpixpod_cli.py:193 msgid "Do not copy full resolution image" msgstr "Non copiare foto ad alta risoluzione" #: gpixpod_cli.py:194 msgid "Zoom images instead of fit" msgstr "Ingrandisci immagini invece di adattarle" #: gpixpod_cli.py:195 msgid "Stretch images instead of fit" msgstr "Espandi le immagini invece di adattarle" #: gpixpod_cli.py:196 msgid "Rotate automatically portrait images clockwise" msgstr "Ruota automaticamente immagini verticali in senso orario" #: gpixpod_cli.py:197 msgid "Rotate automatically portrait images counter-clockwise" msgstr "Ruota automaticamente immagini verticali in senso antiorario" #: gpixpod_cli.py:219 msgid "Invalid iPod mountpoint" msgstr "Mountpoint iPod non valido" #: gpixpod.glade.h:1 msgid " " msgstr " " #: gpixpod.glade.h:2 msgid "10 seconds" msgstr "10 secondi" #: gpixpod.glade.h:3 msgid "15 seconds" msgstr "15 secondi" #: gpixpod.glade.h:4 msgid "20 seconds" msgstr "20 secondi" #: gpixpod.glade.h:5 msgid "3 seconds" msgstr "3 secondi" #: gpixpod.glade.h:6 msgid "30 seconds" msgstr "30 secondi" #: gpixpod.glade.h:7 msgid "5 seconds" msgstr "5 secondi" #: gpixpod.glade.h:8 msgid "90° Clockwise" msgstr "90° in senso orario" #: gpixpod.glade.h:9 msgid "90° Counter-clockwise" msgstr "90° in senso antiorario" #: gpixpod.glade.h:10 msgid "Default behaviour for images" msgstr "Immagine di iPod per la diapositiva corrente" #: gpixpod.glade.h:12 msgid "Image behaviour" msgstr "Tipo di memorizzazione" #: gpixpod.glade.h:13 msgid "Photos" msgstr "Foto" #: gpixpod.glade.h:14 msgid "Rotate portrait images automatically" msgstr "Ruota immagini verticali automaticamente" #: gpixpod.glade.h:15 msgid "Slideshow current iPod image" msgstr "Immagine dell'iPod per la diapositiva corrente" #: gpixpod.glade.h:17 msgid "TV output iPod image" msgstr "Immagine dell'iPod per l'uscita TV" #: gpixpod.glade.h:18 msgid "Thumbnails cache" msgstr "Miniature temporanee" #: gpixpod.glade.h:19 msgid "iPod options" msgstr "Opzioni iPod" #: gpixpod.glade.h:20 msgid "iPod info" msgstr "Info sull'iPod" #: gpixpod.glade.h:21 msgid "About GPixPod" msgstr "Informazioni su GPixPod" #: gpixpod.glade.h:22 msgid "About GPixPod by Flavio Gargiulo" msgstr "Informazioni su GPixPod di Flavio Gargiulo" #: gpixpod.glade.h:23 msgid "Add Al_bum" msgstr "Aggiungi Al_bum" #: gpixpod.glade.h:24 msgid "Add Album" msgstr "Aggiungi Album" #: gpixpod.glade.h:25 msgid "Add Full Resolution to photo" msgstr "Aggiungi alta risoluzione alla foto" #: gpixpod.glade.h:26 msgid "Add Photo reference" msgstr "Aggiungi riferimento a foto" #: gpixpod.glade.h:27 msgid "Add Photo(s)" msgstr "Aggiungi Foto" #: gpixpod.glade.h:28 msgid "Add _Photo" msgstr "Aggiungi _Foto" #: gpixpod.glade.h:29 msgid "Add a full resolution image to the current photo" msgstr "Aggiungi un'immagine ad alta risoluzione per la foto corrente" #: gpixpod.glade.h:30 msgid "Add a new photo album" msgstr "Aggiungi un nuovo album di foto" #: gpixpod.glade.h:31 msgid "Add a photo to the current selected album" msgstr "Aggiungi una foto all'album selezionato" #: gpixpod.glade.h:32 msgid "" "Add a reference to an existing photo in the currently selected photo album" msgstr "" "Aggiungi un riferimento ad una foto esistente nell'album di foto attualmente " "selezionato" #: gpixpod.glade.h:33 msgid "Add info - GPixPod" msgstr "Inserisci info - GPixPod" #: gpixpod.glade.h:34 msgid "Advanced options" msgstr "Opzioni avanzate" #: gpixpod.glade.h:35 msgid "Ask before opening default Photo Database" msgstr "Chiedi prima di aprire il Photo Database predefinito" #: gpixpod.glade.h:36 msgid "" "Autodetect every time\n" "iPod Nano\n" "iPod Photo\n" "iPod Color\n" "iPod Video/5G" msgstr "" "Autorileva ogni volta\n" "iPod Nano\n" "iPod Photo\n" "iPod Color\n" "iPod Video/5G" #: gpixpod.glade.h:41 msgid "Autodetect iPod connection/disconnection" msgstr "Autorileva la connessione/disconnessione dell'iPod" #: gpixpod.glade.h:42 msgid "Available storage:" msgstr "Spazio disponibile:" #: gpixpod.glade.h:43 msgid "Cancel" msgstr "Annulla" #: gpixpod.glade.h:44 msgid "Check the Photo Database for incongruences and try to fix it" msgstr "Verifica la validità del Photo Database e prova a ripararlo" #: gpixpod.glade.h:45 msgid "Choose File - GPixPod" msgstr "Scegli File - GPixPod" #: gpixpod.glade.h:46 msgid "Copy full resolution image" msgstr "Copia immagine ad alta risoluzione" #: gpixpod.glade.h:47 msgid "Copy full resolution image on the iPod" msgstr "Copia la foto a piena risoluzione su iPod" #: gpixpod.glade.h:48 msgid "Defaults" msgstr "Opzioni predefinite" #: gpixpod.glade.h:49 msgid "Delete reference" msgstr "Elimina riferimento" #: gpixpod.glade.h:50 msgid "Delete the current selected item" msgstr "Elimina l'elemento selezionato" #: gpixpod.glade.h:51 msgid "Delete the references related to the current selected item" msgstr "" "Elimina tutti i riferimenti correlati all'elemento attualmente selezionato" #: gpixpod.glade.h:52 msgid "Device:" msgstr "Dispositivo:" #: gpixpod.glade.h:53 msgid "Disabled" msgstr "Disabilitato" #: gpixpod.glade.h:54 msgid "Disconnect iPod" msgstr "Disconnetti iPod" #: gpixpod.glade.h:55 msgid "E_ject iPod" msgstr "S_collega iPod" #: gpixpod.glade.h:56 msgid "Eject iPod" msgstr "Scollega iPod" #: gpixpod.glade.h:57 msgid "Fit" msgstr "Adatta" #: gpixpod.glade.h:58 msgid "" "Flavio Gargiulo (Italian)\n" "Diego Belotti (Spanish)" msgstr "" "Flavio·Gargiulo··(Italiano)\n" "Diego·Belotti··(Spagnolo)" #: gpixpod.glade.h:60 msgid "Full Resolution" msgstr "Alta Risoluzione" #: gpixpod.glade.h:61 msgid "Full resolution image" msgstr "Alta Risoluzione" #: gpixpod.glade.h:62 msgid "GPixPod Preferences" msgstr "Preferenze - GPixPod" #: gpixpod.glade.h:63 msgid "Generate a new photo album containing all unreferenced orphaned photos" msgstr "" "Genera un nuovo album di foto contenente tutte le foto al momento senza " "riferimenti" #: gpixpod.glade.h:64 msgid "" "Help GPixPod development with a PayPal donation! You will choose the amount, " "freely." msgstr "" "Contribuisci con PayPal allo sviluppo di GPixPod! Sceglierai l'importo, " "liberamente." #: gpixpod.glade.h:65 msgid "" "If you really want to clean cache, do it only after saved and quit right " "after!" msgstr "" "Se vuoi davvero rimuovere la cache, fallo soltanto dopo salvato ed esci " "subito dopo!" #: gpixpod.glade.h:66 msgid "Informations about the connected iPod" msgstr "Informazioni sull'iPod collegato" #: gpixpod.glade.h:67 msgid "Keep stored on the computer" msgstr "Conservale sul disco fisso del computer" #: gpixpod.glade.h:68 msgid "Keep stored on the iPod" msgstr "Conservale sull'iPod" #: gpixpod.glade.h:69 msgid "List _previews" msgstr "_Anteprime nell'elenco" #: gpixpod.glade.h:70 msgid "Mountpoint:" msgstr "Cartella base di iPod:" #: gpixpod.glade.h:71 msgid "New Album from unreferenced photos" msgstr "Nuovo Album da foto senza riferimenti" #: gpixpod.glade.h:72 msgid "OK" msgstr "OK" #: gpixpod.glade.h:73 msgid "Organize photos on your iPod, freely!\n" msgstr "Archivia foto sul tuo iPod, liberamente!\n" #: gpixpod.glade.h:75 msgid "Photo Album" msgstr "Album di Foto" #: gpixpod.glade.h:76 msgid "Photo Database:" msgstr "Photo Database:" #: gpixpod.glade.h:77 msgid "Please specify info:" msgstr "Per favore specifica dettagli:" #: gpixpod.glade.h:78 msgid "Progress status..." msgstr "Stato di avanzamento..." #: gpixpod.glade.h:79 msgid "Remove Full Resolution from photo" msgstr "Elimina alta risoluzione dalla foto" #: gpixpod.glade.h:80 msgid "Remove the full resolution image from the current photo" msgstr "Elimina immagine ad alta risoluzione dalla foto corrente" #: gpixpod.glade.h:81 msgid "Rename the currently selected photo album" msgstr "Rinomina album di foto selezionato" #: gpixpod.glade.h:82 msgid "Scan directories recursively" msgstr "Esamina le cartelle ricorsivamente" #: gpixpod.glade.h:83 msgid "Select iPod folder mountpoint" msgstr "Seleziona la cartella principale dell'iPod" #: gpixpod.glade.h:84 msgid "Show fullscreen" msgstr "Mostra a schermo intero" #: gpixpod.glade.h:85 msgid "Skip duplicates, images already in Photo Database" msgstr "Salta duplicati, immagini già nel Photo Database" #: gpixpod.glade.h:86 msgid "Slideshow" msgstr "Presentazione" #: gpixpod.glade.h:87 msgid "Start Slideshow" msgstr "Avvia presentazione" #: gpixpod.glade.h:88 msgid "Storage:" msgstr "Capacità:" #: gpixpod.glade.h:89 msgid "Stretch" msgstr "Espandi" #: gpixpod.glade.h:90 msgid "Thumbnails" msgstr "Miniature" #: gpixpod.glade.h:91 msgid "Tool_bar" msgstr "_Barra degli strumenti" #: gpixpod.glade.h:92 msgid "Total storage:" msgstr "Capacità totale:" #: gpixpod.glade.h:93 msgid "Used for photos:" msgstr "Usata per foto:" #: gpixpod.glade.h:94 msgid "Used storage:" msgstr "Spazio utilizzato:" #: gpixpod.glade.h:95 msgid "Zoom" msgstr "Ingrandisci" #: gpixpod.glade.h:97 #, no-c-format msgid "Zoom (%):" msgstr "Zoom (%):" #: gpixpod.glade.h:98 msgid "_About" msgstr "_Informazioni" #: gpixpod.glade.h:99 msgid "_Clean cache" msgstr "_Cancella la cache" #: gpixpod.glade.h:100 msgid "_Delete" msgstr "_Elimina" #: gpixpod.glade.h:101 msgid "_Details pane" msgstr "Riquadro _dettagli" #: gpixpod.glade.h:102 msgid "_Donate!" msgstr "Fai una _donazione!" #: gpixpod.glade.h:103 msgid "_Edit" msgstr "_Modifica" #: gpixpod.glade.h:104 msgid "_Export" msgstr "_Esporta" #: gpixpod.glade.h:105 msgid "_File" msgstr "_File" #: gpixpod.glade.h:106 msgid "_Help" msgstr "A_iuto" #: gpixpod.glade.h:107 msgid "_Rename album" msgstr "_Rinomina album di foto" #: gpixpod.glade.h:108 msgid "_Rescue database" msgstr "_Ripristina database" #: gpixpod.glade.h:109 msgid "_Tools" msgstr "S_trumenti" #: gpixpod.glade.h:110 msgid "_View" msgstr "_Visualizza" #: gpixpod.glade.h:111 msgid "iPod TV output image" msgstr "Immagine di iPod per uscita TV" #: gpixpod.glade.h:112 msgid "iPod fullscreen image" msgstr "Immagine di iPod a schermo intero" #: gpixpod.glade.h:113 msgid "iPod info" msgstr "Informazioni sull'iPod" #: gpixpod.glade.h:114 msgid "iPod list image" msgstr "Immagine di iPod per elenco" #: gpixpod.glade.h:115 msgid "iPod model:" msgstr "Modello di iPod:" #: gpixpod.glade.h:116 msgid "iPod model: " msgstr "Modello di iPod: " #: gpixpod.glade.h:117 msgid "iPod mountpoint: " msgstr "Cartella base dell'iPod: " #: gpixpod.glade.h:118 msgid "iPod slideshow image" msgstr "Immagine di iPod per diapositive" #: gpixpod.glade.h:119 msgid "i_Pod info" msgstr "Informazioni sull'_iPod" #~ msgid "" #~ "You are using an iPod %s. Your model is considered as an iPod " #~ "Photo and nothing is guaranteed to work." #~ msgstr "" #~ "Stai utilizzando un iPod %s. Il tuo modello viene considerato come " #~ "un iPod Photo e niente è garantito che funzioni." #~ msgid "" #~ "Create backups before. You have been warned! And please, report " #~ "your feedback!" #~ msgstr "" #~ "Crea copie di backup prima. Sei stato avvisato! E per favore, " #~ "inoltra i tuoi commenti!" #~ msgid "There are changes not saved." #~ msgstr "Ci sono modifiche non salvate." #~ msgid "Add Photo with full resolution" #~ msgstr "Aggiungi Foto con alta risoluzione" #~ msgid "" #~ "Add Photo with full resolution image regardless of the global setting" #~ msgstr "" #~ "Aggiungi Foto con alta risoluzione ignorando l'impostazione generale" #~ msgid "Add Photo without full resolution" #~ msgstr "Aggiungi Foto senza alta risoluzione" #~ msgid "" #~ "Add Photo without full resolution image regardless of the global setting" #~ msgstr "" #~ "Aggiungi Foto senza alta risoluzione ignorando l'impostazione generale" #~ msgid "translator-credits" #~ msgstr "Flavio Gargiulo " #~ msgid "" #~ "Image has been just added. Its details will be available after saved." #~ msgstr "" #~ "L'immagine è stata appena inserita. I dettagli saranno disponibili dopo " #~ "salvata" gpixpod-0.6.2/po/msg.pot0000644000175000017500000004512610513121272014375 0ustar flagarflagar# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2006-10-11 09:52+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: gpixpod.py:56 msgid "Using default preferences" msgstr "" #: gpixpod.py:60 gpixpod.py:62 msgid "Add new photo album" msgstr "" #: gpixpod.py:60 gpixpod.py:62 msgid "Enter the name of the new album:" msgstr "" #: gpixpod.py:61 gpixpod.py:63 msgid "New Photo Album" msgstr "" #: gpixpod.py:64 gpixpod.py:65 gpixpod.py:91 gpixpod.py:97 gpixpod.py:1046 msgid "Images" msgstr "" #: gpixpod.py:67 msgid "Image" msgstr "" #: gpixpod.py:69 msgid "Choose photo" msgstr "" #: gpixpod.py:69 msgid "Enter a Photo ID:" msgstr "" #: gpixpod.py:82 msgid "Select the iPod folder mountpoint" msgstr "" #: gpixpod.py:92 gpixpod.py:98 gpixpod.py:1047 msgid "Save image" msgstr "" #: gpixpod.py:158 msgid "Please connect the iPod..." msgstr "" #: gpixpod.py:219 msgid "iPod HAL autodetection disabled" msgstr "" #: gpixpod.py:232 msgid "Photo Database found." msgstr "" #: gpixpod.py:232 #, python-format msgid "" "Do you want to load the Photo Database found in the iPod attached to " "%s?" msgstr "" #: gpixpod.py:237 gpixpod.py:1730 #, python-format msgid "iPod found, attached to %s." msgstr "" #: gpixpod.py:237 gpixpod.py:1727 msgid "But Photo Database not found. Do you want to create a new one?" msgstr "" #: gpixpod.py:270 msgid "A Photo Database already exists" msgstr "" #: gpixpod.py:270 msgid "Do you want to overwrite it completely?" msgstr "" #: gpixpod.py:283 gpixpod.glade.h:11 msgid "Fullscreen iPod image" msgstr "" #: gpixpod.py:284 gpixpod.glade.h:16 msgid "Small iPod thumbnail" msgstr "" #: gpixpod.py:290 #, python-format msgid "" "You are using an iPod %s. Please quit now: your model is not " "supported, and it never will be." msgstr "" #: gpixpod.py:291 msgid "It does not have a color screen and it does not support photos!" msgstr "" #: gpixpod.py:292 msgid "Quit now?" msgstr "" #: gpixpod.py:333 gpixpod_cli.py:176 msgid "" "Interruption requested. Please wait while saving at the current status..." msgstr "" #: gpixpod.py:356 #, python-format msgid "Opening %s..." msgstr "" #: gpixpod.py:358 msgid "" "Opening Photo Database... It could take several minutes, depending on its " "size!" msgstr "" #: gpixpod.py:400 #, python-format msgid "Photo Database %s opened." msgstr "" #: gpixpod.py:424 msgid "Adding full resolution images to photos..." msgstr "" #: gpixpod.py:441 msgid "Deleting full resolution images from photos..." msgstr "" #: gpixpod.py:459 msgid "Adding albums..." msgstr "" #: gpixpod.py:474 msgid "Adding photo references..." msgstr "" #: gpixpod.py:488 msgid "Renaming albums..." msgstr "" #: gpixpod.py:502 msgid "Adding photos..." msgstr "" #: gpixpod.py:513 gpixpod.py:560 gpixpod.py:576 #, python-format msgid "%s of %s - %s%%" msgstr "" #: gpixpod.py:520 msgid "Deleting photo references..." msgstr "" #: gpixpod.py:534 msgid "Deleting whole photo album references..." msgstr "" #: gpixpod.py:548 msgid "Deleting photos..." msgstr "" #: gpixpod.py:567 msgid "Deleting albums..." msgstr "" #: gpixpod.py:592 msgid "Saving Photo Database..." msgstr "" #: gpixpod.py:593 msgid "Please wait... (it could take several minutes!)" msgstr "" #: gpixpod.py:604 msgid "Photo Database saved." msgstr "" #: gpixpod.py:632 gpixpod.py:775 gpixpod.py:1321 msgid "No full resolution image" msgstr "" #: gpixpod.py:640 msgid "Photo Albums" msgstr "" #: gpixpod.py:643 msgid "Previews" msgstr "" #: gpixpod.py:813 msgid "To be created" msgstr "" #: gpixpod.py:816 msgid "Available" msgstr "" #: gpixpod.py:817 #, python-format msgid "" "Photo Album:\t\t%s\n" "Photo Album ID:\t%s\n" "Number of photos:\t%s\n" "Status:\t\t%s" msgstr "" #: gpixpod.py:832 msgid "Saved" msgstr "" #: gpixpod.py:835 msgid "Not saved yet" msgstr "" #: gpixpod.py:836 #, python-format msgid "" "Photo Album: %s \n" "Photo ID: %s \n" "Photo path: %s \n" "Original size: %sx%s \n" "Status: %s" msgstr "" #: gpixpod.py:847 msgid "Full resolution photo not found. Maybe have you deleted it?" msgstr "" #: gpixpod.py:949 msgid "Rename photo album" msgstr "" #: gpixpod.py:949 #, python-format msgid "Enter the new name for the album \"%s\"" msgstr "" #: gpixpod.py:1062 msgid "" "iPod ejected, but the Do not disconnect message seems to be " "still shown on the iPod." msgstr "" #: gpixpod.py:1063 msgid "" "To let disappear the message from the iPod, make your eject " "executable setuid root" msgstr "" #: gpixpod.py:1064 msgid "(chmod +s `which eject` as root)" msgstr "" #: gpixpod.py:1070 msgid "There are unsaved changes" msgstr "" #: gpixpod.py:1070 msgid "Do you want to save your changes before closing this program?" msgstr "" #: gpixpod.py:1237 msgid " (with full resolution)" msgstr "" #: gpixpod.py:1239 msgid " (without full resolution)" msgstr "" #: gpixpod.py:1327 msgid "Not existing photo!" msgstr "" #: gpixpod.py:1327 msgid "The number is not a valid Photo ID." msgstr "" #: gpixpod.py:1334 msgid "Delete all photo album references?" msgstr "" #: gpixpod.py:1334 msgid "No photos will be deleted, but all the photo album references." msgstr "" #: gpixpod.py:1376 msgid "available after saved" msgstr "" #: gpixpod.py:1385 msgid "Full resolution image will be deleted after saved" msgstr "" #: gpixpod.py:1394 msgid "Are you sure to delete the whole album?" msgstr "" #: gpixpod.py:1471 msgid "Previously unreferenced photos" msgstr "" #: gpixpod.py:1485 msgid "No unreferenced photos" msgstr "" #: gpixpod.py:1485 msgid "" "There are not unreferenced photos. Each existing photo is linked to at least " "one photo album." msgstr "" #: gpixpod.py:1489 msgid "Are you sure to remove thumbnails cache?" msgstr "" #: gpixpod.py:1490 msgid "" "Thumbnails cache is automatically removed when saving, only in rare cases " "you would remove it manually.\n" msgstr "" #: gpixpod.py:1491 msgid "" "It is recommended to cancel right away, unless you know what are you " "doing." msgstr "" #: gpixpod.py:1495 msgid "Thumbnails cache removed!" msgstr "" #: gpixpod.py:1502 msgid "Nothing has been modified. Photo Database seems in a proper status." msgstr "" #: gpixpod.py:1504 msgid "" "Photo Database has been modified. Please save now to complete the rescue." msgstr "" #: gpixpod.py:1527 #, python-format msgid "%s photos in %s albums" msgstr "" #: gpixpod.py:1660 msgid "" "Full resolution copies of your photos will be uploaded on your iPod" msgstr "" #: gpixpod.py:1662 msgid "" "Full resolution copies of your photos will not be uploaded on your iPod" msgstr "" #: gpixpod.py:1663 msgid "" "This is now the default action of drag & drop and of the Add Photo button. You can always use the Add Photo with/without full resolution buttons to override this setting" msgstr "" #: gpixpod.py:1697 msgid "GPixPod development needs your help." msgstr "" #: gpixpod.py:1698 msgid "" "If you like GPixPod, please consider buying it from http://www.flagar.com/" "en/software/gpixpod or donating by PayPal (then you could ask the " "author to remove this message). Proceed now?" msgstr "" #: gpixpod.py:1724 #, python-format msgid "iPod %s and existing Photo Database found." msgstr "" #: gpixpod.py:1724 #, python-format msgid "" "Do you want to load the Photo Database found in the iPod attached to %" "s?" msgstr "" #: gpixpod.py:1727 #, python-format msgid "iPod %s found, attached to %s." msgstr "" #: gpixpod.py:1730 msgid "" "But model not autodetected. You should really specify your iPod model " "in Preferences now." msgstr "" #: gpixpod.py:1740 msgid "All the unsaved changes will be lost." msgstr "" #: gpixpod.py:1742 msgid "There are not unsaved changes." msgstr "" #: gpixpod.py:1743 msgid "iPod disconnected. Do you want to exit now?" msgstr "" #: gpixpod.py:1746 msgid "" "Please reconnect the iPod and without loading again the Photo " "Database" msgstr "" #: gpixpod.py:1747 msgid "" "Not reconnecting means that you could not save your changes, resulting in a " "crash. And re-opening would abort all changes" msgstr "" #: gpixpod_cli.py:76 #, python-format msgid "Loading Photo Database '%s'... " msgstr "" #: gpixpod_cli.py:82 #, python-format msgid "Selected iPod model is: %s" msgstr "" #: gpixpod_cli.py:84 #, python-format msgid "Autodetected iPod model is: %s" msgstr "" #: gpixpod_cli.py:87 #, python-format msgid "Photo Database contains %s albums" msgstr "" #: gpixpod_cli.py:89 #, python-format msgid "- '%s', containing %s pictures" msgstr "" #: gpixpod_cli.py:95 #, python-format msgid "Photo album '%s' does not exist, creating it... " msgstr "" #: gpixpod_cli.py:101 #, python-format msgid "created (n. %s)." msgstr "" #: gpixpod_cli.py:104 msgid "Full resolution image will be regularly copied" msgstr "" #: gpixpod_cli.py:106 msgid "Full resolution image will NOT be copied" msgstr "" #: gpixpod_cli.py:108 msgid "clockwise" msgstr "" #: gpixpod_cli.py:108 msgid "counter-clockwise" msgstr "" #: gpixpod_cli.py:109 #, python-format msgid "Detected portrait images will be automatically rotated %s" msgstr "" #: gpixpod_cli.py:110 #, python-format msgid "Image uploading behaviour is: %s" msgstr "" #: gpixpod_cli.py:111 #, python-format msgid "Adding pictures to the photo album '%s'" msgstr "" #: gpixpod_cli.py:117 msgid "Press Ctrl-c to stop adding images" msgstr "" #: gpixpod_cli.py:128 #, python-format msgid "" "\n" "Photo '%s' is already stored in Photo Database." msgstr "" #: gpixpod_cli.py:131 #, python-format msgid "" "\n" "Adding photo '%s'... " msgstr "" #: gpixpod_cli.py:141 #, python-format msgid "added (%s of %s - %s%%)." msgstr "" #: gpixpod_cli.py:149 msgid "Saving Photo Database... " msgstr "" #: gpixpod_cli.py:159 msgid "saved" msgstr "" #: gpixpod_cli.py:162 msgid "" "\n" "No changes done. Nothing to save!" msgstr "" #: gpixpod_cli.py:165 msgid "Saving synchronization status... " msgstr "" #: gpixpod_cli.py:170 msgid "saved." msgstr "" #: gpixpod_cli.py:172 msgid "All done." msgstr "" #: gpixpod_cli.py:187 msgid "The mountpoint of your connected iPod" msgstr "" #: gpixpod_cli.py:188 msgid "" "Manually provide iPod model (Nano|Photo|Color|5G) avoiding autodetection" msgstr "" #: gpixpod_cli.py:189 msgid "Skip synchronization and duplicate checking" msgstr "" #: gpixpod_cli.py:190 msgid "Do not display detailed output" msgstr "" #: gpixpod_cli.py:191 msgid "Name of the photo album to use. It will be created if absent." msgstr "" #: gpixpod_cli.py:192 msgid "Add images from directories recursively" msgstr "" #: gpixpod_cli.py:193 msgid "Do not copy full resolution image" msgstr "" #: gpixpod_cli.py:194 msgid "Zoom images instead of fit" msgstr "" #: gpixpod_cli.py:195 msgid "Stretch images instead of fit" msgstr "" #: gpixpod_cli.py:196 msgid "Rotate automatically portrait images clockwise" msgstr "" #: gpixpod_cli.py:197 msgid "Rotate automatically portrait images counter-clockwise" msgstr "" #: gpixpod_cli.py:219 msgid "Invalid iPod mountpoint" msgstr "" #: gpixpod.glade.h:1 msgid " " msgstr "" #: gpixpod.glade.h:2 msgid "10 seconds" msgstr "" #: gpixpod.glade.h:3 msgid "15 seconds" msgstr "" #: gpixpod.glade.h:4 msgid "20 seconds" msgstr "" #: gpixpod.glade.h:5 msgid "3 seconds" msgstr "" #: gpixpod.glade.h:6 msgid "30 seconds" msgstr "" #: gpixpod.glade.h:7 msgid "5 seconds" msgstr "" #: gpixpod.glade.h:8 msgid "90° Clockwise" msgstr "" #: gpixpod.glade.h:9 msgid "90° Counter-clockwise" msgstr "" #: gpixpod.glade.h:10 msgid "Default behaviour for images" msgstr "" #: gpixpod.glade.h:12 msgid "Image behaviour" msgstr "" #: gpixpod.glade.h:13 msgid "Photos" msgstr "" #: gpixpod.glade.h:14 msgid "Rotate portrait images automatically" msgstr "" #: gpixpod.glade.h:15 msgid "Slideshow current iPod image" msgstr "" #: gpixpod.glade.h:17 msgid "TV output iPod image" msgstr "" #: gpixpod.glade.h:18 msgid "Thumbnails cache" msgstr "" #: gpixpod.glade.h:19 msgid "iPod options" msgstr "" #: gpixpod.glade.h:20 msgid "iPod info" msgstr "" #: gpixpod.glade.h:21 msgid "About GPixPod" msgstr "" #: gpixpod.glade.h:22 msgid "About GPixPod by Flavio Gargiulo" msgstr "" #: gpixpod.glade.h:23 msgid "Add Al_bum" msgstr "" #: gpixpod.glade.h:24 msgid "Add Album" msgstr "" #: gpixpod.glade.h:25 msgid "Add Full Resolution to photo" msgstr "" #: gpixpod.glade.h:26 msgid "Add Photo reference" msgstr "" #: gpixpod.glade.h:27 msgid "Add Photo(s)" msgstr "" #: gpixpod.glade.h:28 msgid "Add _Photo" msgstr "" #: gpixpod.glade.h:29 msgid "Add a full resolution image to the current photo" msgstr "" #: gpixpod.glade.h:30 msgid "Add a new photo album" msgstr "" #: gpixpod.glade.h:31 msgid "Add a photo to the current selected album" msgstr "" #: gpixpod.glade.h:32 msgid "" "Add a reference to an existing photo in the currently selected photo album" msgstr "" #: gpixpod.glade.h:33 msgid "Add info - GPixPod" msgstr "" #: gpixpod.glade.h:34 msgid "Advanced options" msgstr "" #: gpixpod.glade.h:35 msgid "Ask before opening default Photo Database" msgstr "" #: gpixpod.glade.h:36 msgid "" "Autodetect every time\n" "iPod Nano\n" "iPod Photo\n" "iPod Color\n" "iPod Video/5G" msgstr "" #: gpixpod.glade.h:41 msgid "Autodetect iPod connection/disconnection" msgstr "" #: gpixpod.glade.h:42 msgid "Available storage:" msgstr "" #: gpixpod.glade.h:43 msgid "Cancel" msgstr "" #: gpixpod.glade.h:44 msgid "Check the Photo Database for incongruences and try to fix it" msgstr "" #: gpixpod.glade.h:45 msgid "Choose File - GPixPod" msgstr "" #: gpixpod.glade.h:46 msgid "Copy full resolution image" msgstr "" #: gpixpod.glade.h:47 msgid "Copy full resolution image on the iPod" msgstr "" #: gpixpod.glade.h:48 msgid "Defaults" msgstr "" #: gpixpod.glade.h:49 msgid "Delete reference" msgstr "" #: gpixpod.glade.h:50 msgid "Delete the current selected item" msgstr "" #: gpixpod.glade.h:51 msgid "Delete the references related to the current selected item" msgstr "" #: gpixpod.glade.h:52 msgid "Device:" msgstr "" #: gpixpod.glade.h:53 msgid "Disabled" msgstr "" #: gpixpod.glade.h:54 msgid "Disconnect iPod" msgstr "" #: gpixpod.glade.h:55 msgid "E_ject iPod" msgstr "" #: gpixpod.glade.h:56 msgid "Eject iPod" msgstr "" #: gpixpod.glade.h:57 msgid "Fit" msgstr "" #: gpixpod.glade.h:58 msgid "" "Flavio Gargiulo (Italian)\n" "Diego Belotti (Spanish)" msgstr "" #: gpixpod.glade.h:60 msgid "Full Resolution" msgstr "" #: gpixpod.glade.h:61 msgid "Full resolution image" msgstr "" #: gpixpod.glade.h:62 msgid "GPixPod Preferences" msgstr "" #: gpixpod.glade.h:63 msgid "Generate a new photo album containing all unreferenced orphaned photos" msgstr "" #: gpixpod.glade.h:64 msgid "" "Help GPixPod development with a PayPal donation! You will choose the amount, " "freely." msgstr "" #: gpixpod.glade.h:65 msgid "" "If you really want to clean cache, do it only after saved and quit right " "after!" msgstr "" #: gpixpod.glade.h:66 msgid "Informations about the connected iPod" msgstr "" #: gpixpod.glade.h:67 msgid "Keep stored on the computer" msgstr "" #: gpixpod.glade.h:68 msgid "Keep stored on the iPod" msgstr "" #: gpixpod.glade.h:69 msgid "List _previews" msgstr "" #: gpixpod.glade.h:70 msgid "Mountpoint:" msgstr "" #: gpixpod.glade.h:71 msgid "New Album from unreferenced photos" msgstr "" #: gpixpod.glade.h:72 msgid "OK" msgstr "" #: gpixpod.glade.h:73 msgid "Organize photos on your iPod, freely!\n" msgstr "" #: gpixpod.glade.h:75 msgid "Photo Album" msgstr "" #: gpixpod.glade.h:76 msgid "Photo Database:" msgstr "" #: gpixpod.glade.h:77 msgid "Please specify info:" msgstr "" #: gpixpod.glade.h:78 msgid "Progress status..." msgstr "" #: gpixpod.glade.h:79 msgid "Remove Full Resolution from photo" msgstr "" #: gpixpod.glade.h:80 msgid "Remove the full resolution image from the current photo" msgstr "" #: gpixpod.glade.h:81 msgid "Rename the currently selected photo album" msgstr "" #: gpixpod.glade.h:82 msgid "Scan directories recursively" msgstr "" #: gpixpod.glade.h:83 msgid "Select iPod folder mountpoint" msgstr "" #: gpixpod.glade.h:84 msgid "Show fullscreen" msgstr "" #: gpixpod.glade.h:85 msgid "Skip duplicates, images already in Photo Database" msgstr "" #: gpixpod.glade.h:86 msgid "Slideshow" msgstr "" #: gpixpod.glade.h:87 msgid "Start Slideshow" msgstr "" #: gpixpod.glade.h:88 msgid "Storage:" msgstr "" #: gpixpod.glade.h:89 msgid "Stretch" msgstr "" #: gpixpod.glade.h:90 msgid "Thumbnails" msgstr "" #: gpixpod.glade.h:91 msgid "Tool_bar" msgstr "" #: gpixpod.glade.h:92 msgid "Total storage:" msgstr "" #: gpixpod.glade.h:93 msgid "Used for photos:" msgstr "" #: gpixpod.glade.h:94 msgid "Used storage:" msgstr "" #: gpixpod.glade.h:95 msgid "Zoom" msgstr "" #: gpixpod.glade.h:97 #, no-c-format msgid "Zoom (%):" msgstr "" #: gpixpod.glade.h:98 msgid "_About" msgstr "" #: gpixpod.glade.h:99 msgid "_Clean cache" msgstr "" #: gpixpod.glade.h:100 msgid "_Delete" msgstr "" #: gpixpod.glade.h:101 msgid "_Details pane" msgstr "" #: gpixpod.glade.h:102 msgid "_Donate!" msgstr "" #: gpixpod.glade.h:103 msgid "_Edit" msgstr "" #: gpixpod.glade.h:104 msgid "_Export" msgstr "" #: gpixpod.glade.h:105 msgid "_File" msgstr "" #: gpixpod.glade.h:106 msgid "_Help" msgstr "" #: gpixpod.glade.h:107 msgid "_Rename album" msgstr "" #: gpixpod.glade.h:108 msgid "_Rescue database" msgstr "" #: gpixpod.glade.h:109 msgid "_Tools" msgstr "" #: gpixpod.glade.h:110 msgid "_View" msgstr "" #: gpixpod.glade.h:111 msgid "iPod TV output image" msgstr "" #: gpixpod.glade.h:112 msgid "iPod fullscreen image" msgstr "" #: gpixpod.glade.h:113 msgid "iPod info" msgstr "" #: gpixpod.glade.h:114 msgid "iPod list image" msgstr "" #: gpixpod.glade.h:115 msgid "iPod model:" msgstr "" #: gpixpod.glade.h:116 msgid "iPod model: " msgstr "" #: gpixpod.glade.h:117 msgid "iPod mountpoint: " msgstr "" #: gpixpod.glade.h:118 msgid "iPod slideshow image" msgstr "" #: gpixpod.glade.h:119 msgid "i_Pod info" msgstr "" gpixpod-0.6.2/Makefile0000644000175000017500000000045010507301076014101 0ustar flagarflagarall: python2.4 setup.py build clean: python2.4 setup.py clean --all install: python2.4 setup.py install --no-compile --root $(DESTDIR) --install-lib /usr/lib/gpixpod chmod 0755 $(DESTDIR)/usr/lib/gpixpod/gpixpod.py $(DESTDIR)/usr/lib/gpixpod/gpixpod_cli.py $(DESTDIR)/usr/lib/gpixpod/mh.py gpixpod-0.6.2/INSTALL0000644000175000017500000000052110507301076013471 0ustar flagarflagarLaunch gpixpod.py from the main directory. To install, simply: Copy the directory to /usr/local Launch /usr/local/GPixPod/gpixpod.py If using Ubuntu / Debian GNU/Linux, download and install directly the .deb package. You could try the RPM package if using other GNU/Linux distributions, however this has been autogenerated by alien. gpixpod-0.6.2/README0000644000175000017500000000331210507301076013321 0ustar flagarflagar*********************** GPixPod ************************** Organize photos on your iPod, freely! ========================================================== GPixPod has been originally created to support the latest (5G video) Apple iPod, but now it works with iPod Nano and iPod Photo, too. GPixPod let you manage your collection of photos uploading them to your iPod, organizing them in photo albums. Instead of the iTunes synchronization approach, GPixPod let you choose your manual operations on your photos and photo albums, such as adding/deleting individual photos and photo albums, and renaming photo albums. Lot of work has been done, specially building the parser for the Photo Database file format, mh.py. This is my first Python and PyGTK application, thus please forgive my eventual errors and/or unproper coding. Please forgive my bad English, too! The home page of GPixPod is at http://www.gpixpod.org with the development blog and a wiki. SourceForge project page is at http://sf.net/projects/gpixpod. Visit also http://www.flagar.com/en/software/gpixpod GPixPod has been developed by Flavio Gargiulo (FLAGAR.com) but nothing could have been done without the documentation provided by many contributors on the iPodLinux wiki (http://ipodlinux.org/ITunesDB) and without the original concept code by Henryk Plotz (http://www.ploetzli.ch/ipod) GPixPod is provided without any kind of support/warranty, under the terms and conditions of the GNU GPL v. 2 license (see the file COPYING). Use it at your complete risk! Please create backups of your Photos and of your Photo Database before trying! Please send any your comment, bugs, tips to: Flavio Gargiulo Thanks for using GPixPod! gpixpod-0.6.2/TODO0000644000175000017500000000063610507301076013137 0ustar flagarflagar(NOT ordered by importance) - Save texts as picture sequences - Add a sketch note as photo straight from GPixPod - Support for iPod Color to be tested - Optimize image loading speed, do it better - Submit packages to GNU/Linux distributions - Fetch images from online web services - Convert presentations and import them as photo albums - Update the source code on the wiki - Web services - Personalized services gpixpod-0.6.2/CHANGELOG0000644000175000017500000002121710513175360013661 0ustar flagarflagarGPixPod 0.6.2 - Fix for iPod Nano, odd code for the save thumbnails menu - Fix for single folder selection in the filechooser - New right mouse button context menu in the treeview - Double click to expand or collapse albums in the treeview - Some other cleanup for main menu and treeview previews GPixPod 0.6.1 - Reacting to double-click on image displaying fullscreen - Slideshow at intervals of 3, 5, 10, 15, 20, or 30 seconds - Slideshow keys combinations (PageUp/P - PageUp/N - Esc/Q) - New Export menu with save thumbnails options - Fixed iPod Photo/Color fullscreen thumbnail - Made HAL autodetection completely disabled by default (it segfaults in Ubuntu Edgy) - Using mmpython to get image date from EXIF headers, for the path to store the image (patch by William Waghorn) - Fixed proper size of the album icon when adding a new one - Fixed bash script as suggested at #1549407 - New packages with correct dependencies GPixPod 0.6 - Bottle-neck image formats code converted to C for speed! - Rebuilding thumbs only when needed for much faster saving - Safe CLI app SIGTERM and CTRL-C interrupt, saving before - Pause save & resume later or just stop at reached point - Thumbnails in treeview to preview photos - New "View" menu to hide/show GUI sections - New "Photo Album" notebook page - Rename album option now also in the "Edit" menu - New "Rescue" option tries to fix Photo DB if inconsistent - Fixed exception when using sync file from CLI - Fixed exception when adding photos and thumbnails cache directories are missing (deleted) - Removed wrong filechooser options when saving images - Properly managing special empty database iTunes-formatted - Made all dialogs modal to avoid multiple instances - Updated translated strings - Some other GUI clean-ups (ugly borders removed, etc.) GPixPod 0.5.2 - Fixed bug when adding a photo with GUI for the first time - Automatic rotation option for detected portrait images - Fixed exceptions when trying to set missing title icons - Fixed previously buggy sync used to avoid duplicates - Automatic hiding of the scrollbars in image details pane GPixPod 0.5.1 - Simplified full resolution image options support, with option now straight in the filechooser, instead of menu. - Manually select iPod model if autodetection doesn't work - Alert box to specify iPod model when autodetection fails - Scanning directories recursively option when adding photos - Option to avoid known duplicates when adding photos - Fixed bug #1523592: often too many open file descriptors - Fixed exception with CLI when config dir is missing - Fixed exception when removing thumbnails cache - Specified correct minimum Glade dependencies GPixPod 0.5.0 - Fit, zoom, stretch as uploading behaviours for images: global option in preferences, single action per image. Default is 'Fit'. A behaviour affects only thumbnails. - New basic command-line interface automatically activated when passing options to gpixpod. Look at the man page or use --help. Don't pass any option to get regular GUI. - Basic synchronization using new command line interface - Better packaging in both python and debian ways - Updated preferences dialog - Fixed menu disabling "new album from unrefer.." at start GPixPod 0.4.4 - Updated parser code to properly handle Photo Database files probably generated on Macs by iPhoto, containing irregular type-2 string MHOD in MHBA and label in MHII - New iPod info dialog launchable from the help menu GPixPod 0.4.3 - Avoid managing iPodLinux ext2/ext3 partitions! - Fixed crash when rotating images, affecting Photo iPods - Clockwise rotation code rewritten, used when displaying - Fixed iPod Photo fullscreen image display - Improved graphics with simpler icon and more refined logo GPixPod 0.4.2 - Multi-threading! Adding photos and saving actions should be faster, specially on multi-core/processor machines - Full progress indication now when opening/saving Photo Database/thumbnails, thanks to threads implementation - Stop/Abort update operations before actually saving - Photo references! Link photos in multiple places/albums - Delete photo references, and find unreferenced photos - Main website URL updated: http://www.gpixpod.org - Fixed exception when exiting after deleted thumbs cache - Fixed debian package dependencies GPixPod 0.4.1 - Blazing-fast opening with thumbs checksum instead of copy - Thumbs update on saving; extracting only on checksum fail - Option to choose where thumbnails cache should be kept - Speed-up in some RGB565 functions using operator module - Some speed-up when open/save in extract/rebuild thumbs - Fixed treeview when selecting an album for the first time - Fixed addition/removal/display of photos with accented or other not ASCII characters - Fixed cross-platform pack/unpack specifying little-endian - Maybe it now supports the latest model, iPod Nano 1Gb GPixPod 0.4.0 - Now loading properly Photo Databases generated by iTunes with the option to not copy full resolution images - Spanish translation by Diego Belotti - Global option to copy / not copy full resolution images - New Add Photo (with/without) full resol. single actions - Adding/removing full resol. images for existing photos - Displaying iPod thumbnails in new notebook right pane - Little optimization of the full resolution image display GPixPod 0.3.4 - Image details available if not saved yet, too. Status shown. - Image in details zoomed to all available space by default - Change the zoom for the image displayed in the details pane - Save the currently displayed image on the details pane - Expand/collapse photo album in the treeview with single click - Default album name selected when creating new photo album - Existing photo album name as default text when renaming it - Fixed an exception with gettext in iPod/HAL import code GPixPod 0.3.3 - Drag & Drop of supported image files added for the treeview. - Added gettext support for internationalization - Added Italian translation - Fixed a bug when browsing the filechooser with the keyboard - Fixed a bug in the details pane: previous image not cleared if the next one is not found - Removed iPod Photo/Nano warnings. Modified iPod Color warning - Fixed exception about filechooser when creating new photo db. GPixPod 0.3.2 - Preview and file details when choosing images to import - Dialog for adding and renaming albums now accepts - Refined preferences, filechooser for iPod mountpoint - Last Photo Database and images directories now remembered - Photo selection in treeview now with single-click - Message in details warning about missing full res. photo - Fixed exception if requesting details of a new added photo GPixPod 0.3.1 - Fixed ratio scaling when converting images to interlaced UYVY (used in the iPod for TV output) - Single function to load pixbuf properly scaled and with alpha channel removed - Fixed HALdetect function, parsing only when modules available - Fixed a bug when scaling fullscreen iPod thumbnail - Fixed gpixpod.py to launch if passed to the python interpreter - Fixed exception when displaying details of a new added photo - Fixed details still displayed after deleted photo, now cleared - Fixed details cleared when double-click a photo album - Using webbrowser module for links - iPod Color treated as iPod Photo: testing feedback needed - Added menu entry to make a PayPal donation - More tooltips in menu - Debian/Ubuntu package - Cross-platform fixes and win32 installation package - Correct dependencies are python 2.4+ and gtk/pygtk 2.8+ GPixPod 0.3 - Now iPod Photo seems supported fine (probably iPod Color, too) - All gdk-pixbuf image formats now supported for photo import. Thus in addition to JPEGs, now also PNGs, BMPs, GIFs, SVGs... - Preferences dialog: automatic detection/opening, mountpoint, etc. - Refined image conversion functions to keep ratio when scaling - Added basic image conversion functions to rotate CCW and CW GPixPod 0.2.3 - Now iPod Nano seems supported fine. GPixPod 0.2.2 - Added "Eject iPod" feature, with the "Do not disconnect" iPod message checking. - Easy swap/not swap bytes in image conversion functions GPixPod 0.2.1 - Fixed 4 bugs that caused iPod Nano not interpreting Photo DB and therefore not displaying photo albums. Thumbnails still have problems. - Added debug logging in Photos directory, file .gpixpod.log GPixPod 0.2 - Added highly experimental support for iPod Photo, Color and Nano Create backups of your Photos and Photo Database before trying! - Added in GUI 'New' action, to create Photo Database from scratch - Added iPod model detection - Added DBUS/HAL autodetection implementation thanks to the contribution by Abaakouk Mehdi GPixPod 0.1.1 - Solved a small bug when creating database from scratch GPixPod 0.1 - First public release! gpixpod-0.6.2/COPYING0000644000175000017500000004313310507301076013501 0ustar flagarflagar GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 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. 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. gpixpod-0.6.2/KNOWNBUGS0000644000175000017500000000031010507301076013734 0ustar flagarflagarGPixPod 0.4.4 ------------- No known bugs anymore! Anyway, - iPod Photo/Color full screen image support is to be reconfirmed - iPod Nano/Video Photo Database files generated on Macs to be tested more gpixpod-0.6.2/MANIFEST0000644000175000017500000000070410507301076013574 0ustar flagarflagarMakefile INSTALL README TODO CHANGELOG COPYING KNOWNBUGS MANIFEST gpixpod gpixpod.1.gz GPixPod.desktop gpixpod.py gpixpod_cli.py ipodhal.py mh.py setup.py utils.py gpixpod.glade GPixPod.png GPixPod_icon.png ipod.png photoalbum.png po/msg.pot po/it/it.po po/it/LC_MESSAGES/gpixpod.mo po/es/es.po po/es/LC_MESSAGES/gpixpod.mo imgconvert/imgconvert.h imgconvert/imgconvert.c imgconvert/imgconvertmodule.h imgconvert/imgconvertmodule.c imgconvert/Makefile gpixpod-0.6.2/gpixpod0000644000175000017500000000007410507301076014040 0ustar flagarflagar#!/bin/bash exec python2.4 /usr/lib/gpixpod/gpixpod.py "$@" gpixpod-0.6.2/gpixpod.1.gz0000644000175000017500000000142310513202107014605 0ustar flagarflagarG-Egpixpod.1uTMs@ ¥q  0RHôM'30f-ۢղM_NZ.\z{Rݟ*!%ԫJB]අ٠:"<=yn5W_ ܐOpVmx} w_ա πC=#G`{h)rU/jU]Yﯾ_~,V+zTPFOEAN@[ ދLa7h#z2==$^p @)r]z]- dHMn#}oX2"*PV' Z &Aσb3%lUGOŊQrS+vox-n;miVK³ ڋm! V^P6XՑ ĥffk $zA q"E)L(Sh0@[yE˻Y Y֫nڸ,LDi,dJ=ʃ* .y-_ޖLAC܋iehdid/#]`z4"iSrd¤ (KZS 7ے6G崌[(&2BL$&GwR5Uf@CGT Vɩ/`$7\dz6[0EQaT{~.̃*ǮbBUqN c L_f^xDzmUL79Q~܏#-a:;ٿ f#1u([՝܌T&4g\CHgpixpod-0.6.2/GPixPod.desktop0000644000175000017500000000062310507301076015350 0ustar flagarflagar[Desktop Entry] Encoding=UTF-8 Version=1.0 Type=Application Exec=/usr/bin/gpixpod TryExec=/usr/bin/gpixpod Icon=/usr/share/gpixpod/GPixPod_icon.png X-GNOME-DocPath= Terminal=false Name=GPixPod GenericName=iPod photo manager GenericName[it]=Gestore di foto per iPod Comment=Organize photos on your iPod, freely! Comment[it]=Archivia foto sul tuo iPod, liberamente! Categories=GNOME;Application;Graphics; gpixpod-0.6.2/gpixpod.py0000755000175000017500000030004110513200715014462 0ustar flagarflagar#!/usr/bin/env python # GPixPod - organize photos on your iPod, freely! # Copyright (C) 2006 Flavio Gargiulo (FLAGAR.com) # # 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. # # 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. import gtk, gtk.glade, sys, os, gobject, cPickle, webbrowser, re, locale, gettext, signal, copy if os.path.basename(sys.argv[0]) == 'gpixpod.py': if os.path.dirname(sys.argv[0]) != '': os.chdir(os.path.dirname(sys.argv[0])) from threading import Thread import imgconvert from mh import * from utils import * # Internationalization #locale.setlocale(locale.LC_ALL, 'it_IT.UTF-8') #locale.setlocale(locale.LC_ALL, '') gettext.bindtextdomain('gpixpod', 'po') gettext.textdomain('gpixpod') gettext.install('gpixpod', 'po', True) gtk.glade.bindtextdomain('gpixpod', 'po') class GPixPod: """ Main GUI interface """ def __init__(self): """ Initialize variables and the main window """ # Preferences self.homedir = os.path.expanduser('~') self.preferencesdir = os.path.join(self.homedir, '.gpixpod') if not os.path.isdir(self.preferencesdir): os.mkdir(self.preferencesdir) self.preferencesfile = os.path.join(self.preferencesdir, 'config') # Default preferences self.prefs = self.defaultprefs = {'ipod_model':None, 'ipod_autodetect':False, 'ipod_mountpoint':"/mnt/ipod", 'ipod_askbeforeopen':True, 'photo_copyfullres':True, 'path_lastphotodb':self.homedir, 'path_lastimages':self.homedir, 'path_lastsavedimages':self.homedir, 'path_thumbscache':os.path.join(self.preferencesdir, '.SingleThumbs'), 'photo_behaviour':'Fit', 'photo_autorotate':'No', 'gui_toolbar':True, 'gui_treeviewpreviews':False, 'gui_detailspane':True} # Overriding default preferences try: self.LoadPreferences() except IOError: print _("Using default preferences") def GUI(self): """ Build the GUI, of course to be called right after instantiation and application launch """ self.win_callbacks = {'on_addalbum1_activate':(self.ShowGetLine, self.AddAlbum, _('Add new photo album'), _('Enter the name of the new album:'), _('New Photo Album')), 'on_addalbumbutton_clicked':(self.ShowGetLine, self.AddAlbum, _('Add new photo album'), _('Enter the name of the new album:'), _('New Photo Album')), 'on_addphoto1_activate':(self.ShowChooser, self.AddPhotoChooser, _('Images'), 'PIXBUF', True, self.prefs['path_lastimages'], True), 'on_addphotobutton_clicked':(self.ShowChooser, self.AddPhotoChooser, _('Images'), 'PIXBUF', True, self.prefs['path_lastimages'], True), 'on_rename_album1_activate':self.ShowRenameAlbum, 'on_add_full_resolution_activate':(self.ShowChooser, self.AddFullResolution, _('Image'), 'PIXBUF', False, self.prefs['path_lastimages'], True), 'on_remove_full_resolution_activate':self.RemoveFullResolution, 'on_add_photo_reference1_activate':(self.ShowGetLine, self.AddReference, _('Choose photo'), _('Enter a Photo ID:'), '100'), 'on_delete_reference1_activate':self.DeleteReference, 'on_donate1_activate':(self.LaunchBrowser, 'https://www.paypal.com/xclick/business=flagar%40gmail.com&item_name=Helping+GPixPod+development&no_shipping=1&tax=0¤cy_code=EUR&lc=US'), 'on_ipodinfo1_activate':self.ShowIpodInfo, 'on_about1_activate':self.ShowAbout, 'on_delete1_activate':self.Delete, 'on_aboutbutton_clicked':self.ShowAbout, 'on_deletebutton_clicked':self.Delete, 'on_preferences1_activate':self.ShowPreferences, 'on_eject1_activate':self.Eject, 'on_ejectbutton_clicked':self.Eject, 'on_quit1_activate':self.Quit, 'on_window1_destroy':self.Quit, 'on_quitbutton_clicked':self.Quit, 'on_window1_destroy':self.Quit, 'on_open1_activate':(self.ShowChooser, self.Open, 'iPod Photo Database', 'Photo Database', False, self.prefs['path_lastphotodb']), 'on_openbutton_clicked':(self.ShowChooser, self.Open, 'iPod Photo Database', 'Photo Database', False, self.prefs['path_lastphotodb']), 'on_new1_activate':(self.ShowChooser, self.CreateNew, None, None, False, None, False, gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER, _('Select the iPod folder mountpoint')), 'on_treeview1_row_activated':self.DetailsDoubleClick, 'on_treeview1_cursor_changed':self.Details, 'on_notebook1_switch_page':self.NotebookPage, 'on_expander1_activate':(self.ExpandThumbnail, 0), 'on_expander2_activate':(self.ExpandThumbnail, 1), 'on_expander3_activate':(self.ExpandThumbnail, 2), 'on_expander4_activate':(self.ExpandThumbnail, 3), 'on_save1_activate':self.Save, 'on_savebutton_clicked':self.Save, 'on_treeview1_unselect_all':self.HideActions, 'on_treeview1_button_press_event':self.TreeviewClick, #'on_window1_state_changed':self.Opening, 'on_spinbutton1_value_changed':self.ZoomImage, 'on_saveimagebutton_clicked':(self.ShowChooser, self.SaveImage, _('Images'), 'PIXBUF', False, self.prefs['path_lastsavedimages'], False, gtk.FILE_CHOOSER_ACTION_SAVE, _('Save image')), 'on_save1thumb_activate':(self.SaveThumb, 0), 'on_save2thumb_activate':(self.SaveThumb, 1), 'on_save3thumb_activate':(self.SaveThumb, 2), 'on_save4thumb_activate':(self.SaveThumb, 3), 'on_savefullres_activate':(self.ShowChooser, self.SaveImage, _('Images'), 'PIXBUF', False, self.prefs['path_lastsavedimages'], False, gtk.FILE_CHOOSER_ACTION_SAVE, _('Save image')), 'on_showfullscreen_activate':self.ShowFullScreen, 'on_startslideshow3_activate':(self.ShowFullScreen, True, 3), 'on_startslideshow5_activate':(self.ShowFullScreen, True, 5), 'on_startslideshow10_activate':(self.ShowFullScreen, True, 10), 'on_startslideshow15_activate':(self.ShowFullScreen, True, 15), 'on_startslideshow20_activate':(self.ShowFullScreen, True, 20), 'on_startslideshow30_activate':(self.ShowFullScreen, True, 30), 'on_new_album_from_unreferenced_photos1_activate':self.NewAlbumFromUnreferencedPhotos, 'on_cleancache1_activate':self.CleanCache, 'on_rescue_database1_activate':self.Rescue, 'on_progresspausebutton_clicked':self.PauseProgress, 'on_progressstopbutton_clicked':self.StopProgress, 'on_toolbar2_activate':self.ToggleToolbar, 'on_list_previews1_activate':self.TogglePreviews, 'on_details_pane1_activate':self.ToggleDetails} self.win = gtk.glade.XML('gpixpod.glade', 'window1', 'gpixpod') self.win.signal_autoconnect(self.win_callbacks) self.window = self.win.get_widget('window1') self.treeview = self.win.get_widget('treeview1') self.notebook = self.win.get_widget('notebook1') if os.path.isfile('GPixPod_icon.png'): self.icon = 'GPixPod_icon.png' elif os.path.isfile('/usr/share/gpixpod/GPixPod_icon.png'): self.icon = '/usr/share/gpixpod/GPixPod_icon.png' else: self.icon = None if os.path.isfile('GPixPod.png'): self.logo = 'GPixPod.png' elif os.path.isfile('/usr/share/gpixpod/GPixPod.png'): self.logo = '/usr/share/gpixpod/GPixPod.png' else: self.logo = None if os.path.isfile('ipod.png'): self.ipodicon = 'ipod.png' elif os.path.isfile('/usr/share/gpixpod/ipod.png'): self.ipodicon = '/usr/share/gpixpod/ipod.png' else: self.ipodicon = None if os.path.isfile('photoalbum.png'): self.photoalbumicon = 'photoalbum.png' elif os.path.isfile('/usr/share/gpixpod/photoalbum.png'): self.photoalbumicon = '/usr/share/gpixpod/photoalbum.png' else: self.photoalbumicon = None if self.icon: self.window.set_icon_from_file(self.icon) if self.photoalbumicon: self.win.get_widget('detailsphotoalbumimage').set_from_file(self.photoalbumicon) self.current_photo_id = None self.imageipodthumb0 = self.win.get_widget('imageipodthumb0') self.imageipodthumb1 = self.win.get_widget('imageipodthumb1') self.imageipodthumb2 = self.win.get_widget('imageipodthumb2') self.imageipodthumb3 = self.win.get_widget('imageipodthumb3') self.label = self.win.get_widget('label1') self.label.set_line_wrap(True) self.image = self.win.get_widget('image1') self.savefilename = None self.imagehbox = self.win.get_widget('imagehbox') self.imagehbox.hide() self.spinbutton = self.win.get_widget('spinbutton1') self.statusbar = self.win.get_widget('statusbar1') self.statusbar_context = self.statusbar.get_context_id('GPixPod') self.statusbar.push(self.statusbar_context, _('Please connect the iPod...')) self.progressbar = self.win.get_widget('progressbar1') self.progresslabel = self.win.get_widget('label3') self.progresspausebutton = self.win.get_widget('progresspausebutton') self.progressstopbutton = self.win.get_widget('progressstopbutton') #self.current_album = 0 # Lists to mark the new changes self.albums_to_add = [] self.albums_to_delete = [] self.albums_to_rename = [] # the entry is the corresponding tuple of album ID and new name self.photos_to_delete = [] self.photos_to_add = [] # the entry is the corresponding tuple of filename path, parent album ID, and full resolution option self.fullres_to_add = [] # the entry is a tuple of 2 elements: filename and photo ID self.fullres_to_delete = [] # the entry is the photo ID self.references_to_add = [] # the entry is a tuple of 2 elements: a photo ID and an album ID self.references_album_to_delete = [] # the entry is the album ID self.references_photo_to_delete = [] # the entry is a tuple of 2 elements: a photo ID and an album ID # Setting sensitiveness self.SetSensitive(('save1', 'savebutton', 'addalbum1', 'addalbumbutton', 'rename_album1', 'eject1', 'ejectbutton', 'ipodinfo1', 'spinbutton1', 'saveimagebutton', 'add_full_resolution', 'remove_full_resolution', 'new_album_from_unreferenced_photos1', 'list_previews1', 'expander1', 'expander2', 'expander3', 'expander4', 'cleancache1', 'rescue_database1', 'savefullres', 'save1thumb', 'save2thumb', 'save3thumb', 'save4thumb'), False) self.HideActions(None) # hide "Add Photo" and "Delete" by default self.tosave = False self.DB = None # Used to check if a Photo Database is loaded # Updating GUI sections as previous status self.win.get_widget('details_pane1').set_active(self.prefs['gui_detailspane']) self.win.get_widget('toolbar2').set_active(self.prefs['gui_toolbar']) # Continue starting if os.name == 'nt' and not os.path.isfile('DONATED'): self.DonateForGPixPod() gobject.timeout_add(300000, self.DonateForGPixPod) #if sys.modules.has_key('ipodhal') and self.prefs['ipod_autodetect']: # If ipodhal.py has been imported successfully (python-dbus/hal/gnomevfs are available), if self.prefs['ipod_autodetect']: gobject.idle_add(self.HALdetect) # Checking in a loop events of connection and removal of an iPod else: self.Opening(None) def SetSensitive(self, widget_list, state): """ Change sensitivity in the same way for a group of widgets """ for widget_name in widget_list: self.win.get_widget(widget_name).set_sensitive(state) def StatusBarNotify(self, sentence): """ Display a sentence on the statusbar """ self.statusbar.push(self.statusbar_context, sentence) def HALdetect(self): """ Detect using HAL """ if self.prefs['ipod_autodetect']: try: if 'ipodhal' not in sys.modules: import ipodhal #ipodHal will connect to HAL via DBUS #and 2 signal are implemented to detect ipod ipod_hal = ipodhal.iPodHal() #Connect to some signal ipod_hal.connect("ipod-added", self.OnIpodConnect) ipod_hal.connect("ipod-removed", self.OnIpodDisconnect) #Launch the detection #if ipod already present signal 'ipod-added' is emit now ipod_hal.start_monitor() return True except ImportError: print _("iPod HAL autodetection disabled") return False else: return False def Opening(self, widget): """ Prepare to open the Photo Database, trying to find it and evaluating command line argument """ #if len(sys.argv) > 1: # self.DBOpen(sys.argv[1]) #else: standard_path = self.prefs['ipod_mountpoint'] standard_loc = os.path.join(standard_path, 'Photos', 'Photo Database') if os.path.isfile(standard_loc): if self.prefs['ipod_askbeforeopen'] == False or self.Ask(_('Photo Database found.'), _('Do you want to load the Photo Database found in the iPod attached to %s?') % standard_path): self.DBOpen(standard_loc) else: self.ShowChooser(None, self.Open, 'iPod Photo Database', 'Photo Database', False, self.prefs['path_lastphotodb']) elif os.path.isdir(os.path.join(standard_path, 'iPod_Control')): if self.prefs['ipod_askbeforeopen'] == False or self.Ask(_('iPod found, attached to %s.') % standard_path, _('But Photo Database not found. Do you want to create a new one?')): self.DBOpen(standard_loc) else: self.ShowChooser(None, self.Open, 'iPod Photo Database', 'Photo Database', False, self.prefs['path_lastphotodb']) else: self.ShowChooser(None, self.Open, 'iPod Photo Database', 'Photo Database', False, self.prefs['path_lastphotodb']) def ToSave(self, unsaved=True): """ Mark the Photo Database to be saved, new changes are happened. """ if unsaved: self.tosave = True if self.window.get_title()[0] != '*': # Already marked as unsaved, thus we avoid multiple alterisks! ;-) self.window.set_title('*%s' % self.window.get_title()) self.win.get_widget('save1').set_sensitive(True) self.win.get_widget('savebutton').set_sensitive(True) else: self.tosave = False self.window.set_title(self.window.get_title()[1:]) self.win.get_widget('save1').set_sensitive(False) self.win.get_widget('savebutton').set_sensitive(False) def Open(self, widget): # To be called from signal of the file chooser """ Let select the Photo Database by file selection """ filename = self.filechooser.get_filename() self.filechooser.destroy() self.DBOpen(filename) def CreateNew(self, widget): """ Create a new Photo Database from scratch, after got the iPod mountpoint from the user """ mountpoint = self.filechooser.get_filename() self.filechooser.destroy() filename = os.path.join(mountpoint, 'Photos', 'Photo Database') if os.path.isfile(filename): if self.Ask(_('A Photo Database already exists'), _('Do you want to overwrite it completely?')): os.remove(filename) if not os.path.isfile(filename): self.DBOpen(filename) self.DB.dbcopyfullres = self.prefs['photo_copyfullres'] def CheckIpodModel(self, photodb_filename): """ Check for a supported iPod model """ ipod_mountpoint = photodb_filename.replace(os.path.join('', 'Photos', 'Photo Database'), '') self.ipod_model = getIpodModel(ipod_mountpoint) if self.ipod_model == '5G' or self.ipod_model == 'Photo' or self.ipod_model == 'Color': pass elif self.ipod_model == 'Nano': self.win.get_widget('expander1label').set_markup(_('Fullscreen iPod image')) self.win.get_widget('expander2label').set_markup(_('Small iPod thumbnail')) self.win.get_widget('expander3').hide() self.win.get_widget('expander4').hide() self.win.get_widget('save1thumb').hide() self.win.get_widget('save3thumb').hide() else: self.Say(_('You are using an iPod %s. Please quit now: your model is not supported, and it never will be.') % self.ipod_model, _('It does not have a color screen and it does not support photos!'), gtk.MESSAGE_ERROR) if self.Ask(_('Quit now?')): gtk.main_quit() sys.exit() def ToggleProgress(self): """ Show/hide progress bar and related label at the bottom of the main window """ if self.progressbar.get_property('visible'): self.progressbar.hide() self.progresslabel.hide() else: self.progressbar.show() self.progresslabel.show() def PauseProgress(self, widget): """ Temporarily stop work managed by the progressbar after clicked the (therefore available) pause button, to continue saving later """ self.work_left = False self.progresspausebutton.set_sensitive(False) self.progresspausebutton.hide() self.progressstopbutton.set_sensitive(False) self.progressstopbutton.hide() self.ToggleProgress() self.SetSensitive(('treeview1', 'open1', 'openbutton', 'addalbum1', 'addalbumbutton', 'cleancache1', 'rescue_database1', 'save1', 'savebutton'), True) def StopProgress(self, widget): """ Stop completely the work managed by the progressbar, after clicked the stop button, and save the Photo Database at the current status """ self.work_left = False self.photos_to_add = [] self.photos_to_delete = [] self.albums_to_add = [] self.albums_to_delete = [] self.albums_to_rename = [] self.fullres_to_add = [] self.fullres_to_delete = [] self.references_to_add = [] self.references_photo_to_delete = [] self.references_album_to_delete = [] self.SaveDB() def Kill(self, signal_no, stack): """ React to SIGTERM saving the Photo Database before quitting. """ if self.tosave and self.DB != None: print _("Interruption requested. Please wait while saving at the current status...") self.work_left = False self.Rescue(None) self.SaveDB(False) gtk.main_quit() def DBOpen(self, filename): """ Actually open the Photo Database and process it """ self.CheckIpodModel(filename) # The while loop is needed to update the GTK widgets. # The GTK interface is event-driven, so functions and code from outside the mainloop # will prevent to properly update widgets. # The while loop below together with another one, "while gtk.events_pending()" and # the initialization of another gtk.main_iteration() seems to solve everything. self.syncfn = os.path.join(self.preferencesdir, 'sync') try: syncf = open(self.syncfn) self.sync = list(cPickle.load(syncf)) syncf.close() except: self.sync = [] self.work_left = True while self.work_left: self.statusbar.push(self.statusbar_context, _('Opening %s...') % filename) self.ToggleProgress() self.progresslabel.set_text(_('Opening Photo Database... It could take several minutes, depending on its size!')) while gtk.events_pending(): gtk.main_iteration() self.DB = MH(filename, self.prefs['path_thumbscache'], False, ipodmodel=self.prefs['ipod_model']) # Delay opening, to control progress thopen = Thread(target=self.DB.Open) thopen.start() # Actually opening and processing Photo Database while thopen.isAlive(): self.progressbar.set_fraction(self.DB.progress) while gtk.events_pending(): gtk.main_iteration() self.progressbar.set_fraction(0.5) while gtk.events_pending(): gtk.main_iteration() # Creating initial converted thumbs directories self.converted_thumbs_dir = os.path.join(self.DB.dbdir, '.ConvertedThumbs') if not os.path.isdir(self.converted_thumbs_dir): os.mkdir(self.converted_thumbs_dir) for convthumb in self.DB.thumbsizes: if not os.path.isdir(os.path.join(self.converted_thumbs_dir, '%sx%s' % (convthumb[0], convthumb[1]))): os.mkdir(os.path.join(self.converted_thumbs_dir, '%sx%s' % (convthumb[0], convthumb[1]))) self.progressbar.set_fraction(0.7) while gtk.events_pending(): gtk.main_iteration() # If database is empty with no photos yet, getting copy / not copy full resolution image option from preferences if len(self.DB.Photos()) == 0: self.DB.dbcopyfullres = self.prefs['photo_copyfullres'] self.progressbar.set_fraction(0.8) while gtk.events_pending(): gtk.main_iteration() # Updating GUI if len(self.treeview.get_columns()) == 2: self.treeview.remove_column(self.columnpx) self.treeview.remove_column(self.column) elif len(self.treeview.get_columns()) == 1: self.treeview.remove_column(self.column) self.Populate() self.window.set_title('%s - GPixPod' % filename) self.SetSensitive(('addalbum1', 'addalbumbutton', 'new_album_from_unreferenced_photos1', 'cleancache1', 'rescue_database1', 'list_previews1'), True) self.progressbar.set_fraction(1) while gtk.events_pending(): gtk.main_iteration() self.ToggleProgress() self.statusbar.push(self.statusbar_context, _('Photo Database %s opened.') % filename) while gtk.events_pending(): gtk.main_iteration() self.work_left = False self.prefs['path_lastphotodb'] = self.DB.dbdir # Remembering the directory of the last opened Photo Database def Save(self, widget): """ Save the changes to the Photo Database """ # The while loop is needed to update the GTK widgets. # The GTK interface is event-driven, so functions and code from outside the mainloop # will prevent to properly update widgets. # The while loop below together with another one, "while gtk.events_pending()" and # the initialization of another gtk.main_iteration() seems to solve everything. self.work_left = True while self.work_left: self.ToggleProgress() self.SetSensitive(('treeview1', 'open1', 'openbutton', 'save1', 'savebutton', 'addalbum1', 'addalbumbutton', 'addphoto1', 'addphotobutton', 'delete1', 'deletebutton', 'new_album_from_unreferenced_photos1', 'cleancache1', 'rescue_database1'), False) self.progresspausebutton.show() self.progresspausebutton.set_sensitive(True) self.progressstopbutton.show() self.progressstopbutton.set_sensitive(True) if len(self.photos_to_delete) > 0 or len(self.albums_to_delete) > 0: self.DB.RemoveThumbsCache(os.path.join(self.DB.dbdir, '.ConvertedThumbs')) fullres_to_add_len = len(self.fullres_to_add) self.progresslabel.set_text(_('Adding full resolution images to photos...')) self.progressbar.set_fraction(0) while gtk.events_pending(): gtk.main_iteration() for x in range(fullres_to_add_len): if not self.work_left: break fullres_to_add_item = self.fullres_to_add.pop(0) self.sync.append(fullres_to_add_item[0]) self.DB.AddFullResolutionToPhoto(*fullres_to_add_item) self.progressbar.set_fraction((x + 1.0)/fullres_to_add_len) while gtk.events_pending(): gtk.main_iteration() #self.fullres_to_add = [] # Reset if not self.work_left: break fullres_to_delete_len = len(self.fullres_to_delete) self.progresslabel.set_text(_('Deleting full resolution images from photos...')) self.progressbar.set_fraction(0) while gtk.events_pending(): gtk.main_iteration() for x in range(fullres_to_delete_len): if not self.work_left: break fullres_to_delete_item = self.fullres_to_delete.pop(0) if fullres_to_delete_item in self.sync: self.sync.remove(fullres_to_delete_item) self.DB.RemoveFullResolutionFromPhoto(fullres_to_delete_item) self.progressbar.set_fraction((x + 1.0)/fullres_to_delete_len) while gtk.events_pending(): gtk.main_iteration() #self.fullres_to_delete = [] # Reset if not self.work_left: break albums_to_add_len = len(self.albums_to_add) self.progresslabel.set_text(_('Adding albums...')) self.progressbar.set_fraction(0) while gtk.events_pending(): gtk.main_iteration() for x in range(albums_to_add_len): # Adding photo albums if not self.work_left: break self.DB.AddAlbum(self.albums_to_add.pop(0)) self.progressbar.set_fraction((x + 1.0)/albums_to_add_len) while gtk.events_pending(): gtk.main_iteration() #self.albums_to_add = [] # Reset if not self.work_left: break references_to_add_len = len(self.references_to_add) self.progresslabel.set_text(_('Adding photo references...')) self.progressbar.set_fraction(0) while gtk.events_pending(): gtk.main_iteration() for x in range(references_to_add_len): if not self.work_left: break self.DB.AddPhotoToAlbum(*self.references_to_add.pop(0)) self.progressbar.set_fraction((x + 1.0)/references_to_add_len) while gtk.events_pending(): gtk.main_iteration() if not self.work_left: break albums_to_rename_len = len(self.albums_to_rename) self.progresslabel.set_text(_('Renaming albums...')) while gtk.events_pending(): gtk.main_iteration() for x in range(albums_to_rename_len): # Renaming photo albums if not self.work_left: break self.DB.RenameAlbum(*self.albums_to_rename.pop(0)) self.progressbar.set_fraction((x + 1.0)/albums_to_rename_len) while gtk.events_pending(): gtk.main_iteration() #self.albums_to_rename = [] # Reset if not self.work_left: break photos_to_add_len = len(self.photos_to_add) self.progresslabel.set_text(_('Adding photos...')) self.progressbar.set_fraction(0) while gtk.events_pending(): gtk.main_iteration() for x in range(photos_to_add_len): # Adding photos if not self.work_left: break photo_to_add = self.photos_to_add.pop(0) self.sync.append(photo_to_add) self.DB.AddPhoto(*photo_to_add) self.progressbar.set_fraction((x + 1.0)/photos_to_add_len) self.progressbar.set_text(_('%s of %s - %s%%') % (x+1, photos_to_add_len, 100*(x+1)/photos_to_add_len)) while gtk.events_pending(): gtk.main_iteration() #self.photos_to_add = [] # Reset if not self.work_left: break references_photo_to_delete_len = len(self.references_photo_to_delete) self.progresslabel.set_text(_('Deleting photo references...')) self.progressbar.set_fraction(0) while gtk.events_pending(): gtk.main_iteration() for x in range(references_photo_to_delete_len): if not self.work_left: break self.DB.RemovePhotoFromAlbum(*self.references_photo_to_delete.pop(0)) self.progressbar.set_fraction((x + 1.0)/references_photo_to_delete_len) while gtk.events_pending(): gtk.main_iteration() if not self.work_left: break references_album_to_delete_len = len(self.references_album_to_delete) self.progresslabel.set_text(_('Deleting whole photo album references...')) self.progressbar.set_fraction(0) while gtk.events_pending(): gtk.main_iteration() for x in range(references_album_to_delete_len): if not self.work_left: break self.DB.RemoveAlbum(self.references_album_to_delete.pop(0)) self.progressbar.set_fraction((x + 1.0)/references_album_to_delete_len) while gtk.events_pending(): gtk.main_iteration() if not self.work_left: break photos_to_delete_len = len(self.photos_to_delete) self.progresslabel.set_text(_('Deleting photos...')) self.progressbar.set_fraction(0) while gtk.events_pending(): gtk.main_iteration() for x in range(photos_to_delete_len): # Deleting photos if not self.work_left: break photo_to_delete = self.photos_to_delete.pop(0) if photo_to_delete in self.sync: self.sync.remove(photo_to_delete) self.DB.RemovePhoto(photo_to_delete) self.progressbar.set_fraction((x + 1.0)/photos_to_delete_len) self.progressbar.set_text(_('%s of %s - %s%%') % (x+1, photos_to_delete_len, 100*(x+1)/photos_to_delete_len)) while gtk.events_pending(): gtk.main_iteration() #self.photos_to_delete = [] # Reset if not self.work_left: break albums_to_delete_len = len(self.albums_to_delete) self.progresslabel.set_text(_('Deleting albums...')) self.progressbar.set_fraction(0) while gtk.events_pending(): gtk.main_iteration() for x in range(albums_to_delete_len): # Deleting photo albums if not self.work_left: break self.DB.RemoveAlbumAndPhotos(self.albums_to_delete.pop(0)) self.progressbar.set_fraction((x + 1.0)/albums_to_delete_len) self.progressbar.set_text(_('%s of %s - %s%%') % (x+1, albums_to_delete_len, 100*(x+1)/albums_to_delete_len)) while gtk.events_pending(): gtk.main_iteration() #self.albums_to_delete = [] # Reset if not self.work_left: break self.SaveDB() def SaveDB(self, reopen=True): """ Finish the saving process actually saving the Photo Database and re-opening it """ try: # Saving Photo Database procedure self.progresspausebutton.set_sensitive(False) self.progresspausebutton.hide() self.progressstopbutton.set_sensitive(False) self.progressstopbutton.hide() self.progresslabel.set_text(_('Saving Photo Database...')) self.progressbar.set_text(_('Please wait... (it could take several minutes!)')) while gtk.events_pending(): gtk.main_iteration() self.DB.progress = 0.0 thsave = Thread(target=self.DB.Save) thsave.start() # Actually saving the new-generated Photo Database file while thsave.isAlive(): self.progressbar.set_fraction(self.DB.progress) while gtk.events_pending(): gtk.main_iteration() self.ToSave(False) # Marking as saved self.statusbar.push(self.statusbar_context, _('Photo Database saved.')) self.sync = set(self.sync) syncf = open(self.syncfn, 'w+') cPickle.dump(self.sync, syncf) syncf.close() self.ToggleProgress() if reopen: self.DBOpen(self.DB.dbname) # Re-opening the Photo Database to let user eventually continue work self.SetSensitive(('treeview1', 'open1', 'openbutton', 'addalbum1', 'addalbumbutton', 'cleancache1', 'rescue_database1', 'new_album_from_unreferenced_photos1'), True) while gtk.events_pending(): gtk.main_iteration() self.work_left = False except KeyboardInterrupt: pass def GetDBLists(self): # Probably not very useful. Maybe to delete, after merging in Populate() and checking all other possible dependencies. """ Refresh list variables for photo albums and photos """ self.albums = self.DB.Albums() self.photos = self.DB.Photos() def Populate(self): """ Fill the tree view with the Photo Database contents """ self.tree = gtk.TreeStore(gtk.gdk.Pixbuf, gobject.TYPE_STRING) self.GetDBLists() for album in self.albums[1:]: albumroot = self.tree.append(None, [None, '%s' % album.name]) for pic in album.pics: if len(self.photos) <= (pic-100) or self.photos[pic-100].fullres == None: fullresname = _('No full resolution image') else: fullresname = self.photos[pic-100].fullres.split(':')[-1] self.tree.append(albumroot, [None, '%s. %s' % (str(pic), fullresname)]) self.treeview.set_model(self.tree) self.cell = gtk.CellRendererText() #self.cell.set_property('editable', True) #self.cell.connect('edited', self.Rename) self.column = gtk.TreeViewColumn(_('Photo Albums'), self.cell, markup=1) self.cellpx = gtk.CellRendererPixbuf() #self.cell2.set_property('stock-id', gtk.STOCK_ABOUT); self.columnpx = gtk.TreeViewColumn(_('Previews'), self.cellpx, pixbuf=0) if self.win.get_widget('list_previews1').get_active(): self.treeview.append_column(self.columnpx) self.treeview.append_column(self.column) # Enable Drag & Drop (DND) for the treeview targets = [('text/plain', 1, 1), ('TEXT', 1, 2), ('STRING', 1, 3)] # Drag outside not working and not so much useful: to delete # targets2 = gtk.target_list_add_image_targets() # targets2 = gtk.target_list_add_text_targets(targets2) # self.treeview.enable_model_drag_source(gtk.gdk.BUTTON1_MASK, targets2, gtk.gdk.ACTION_COPY) self.treeview.enable_model_drag_dest(targets, gtk.gdk.ACTION_COPY) # self.treeview.connect("drag_data_get", self.TreeviewDrag) self.treeview.connect("drag_data_received", self.TreeviewDrop) self.win.get_widget('list_previews1').set_active(self.prefs['gui_treeviewpreviews']) def PopulateIcons(self): """ Populate left-side list thumbnails previews icons """ if self.photoalbumicon: albumpx = gtk.gdk.pixbuf_new_from_file_at_size(self.photoalbumicon, 48, 48) else: albumpx = None a = 0 for album in self.albums[1:]: try: self.tree.set_value(self.tree.get_iter((a,)), 0, albumpx) except: pass p = 0 for pic in album.pics: convthumb = os.path.join(self.DB.dbdir, '.ConvertedThumbs', '%sx%s' % (self.DB.thumbsizes[-1][0], self.DB.thumbsizes[-1][1]), '%s.png' % str(pic)) if not os.path.isfile(convthumb): thumb = os.path.join(self.DB.sglthumbsdir, '%sx%s' % (self.DB.thumbsizes[-1][0], self.DB.thumbsizes[-1][1]), str(pic)) thumbf = open(thumb) thumbcon = thumbf.read() thumbf.close() imgconvert.fromRGB565(thumbcon, self.DB.thumbsizes[-1][0], self.DB.thumbsizes[-1][1], True, False, convthumb) try: self.tree.set_value(self.tree.get_iter((a, p)), 0, gtk.gdk.pixbuf_new_from_file(convthumb)) except: pass while gtk.events_pending(): gtk.main_iteration() p += 1 a += 1 def DestroyIcons(self): """ Destroy left-side list thumbnails previews icons """ a = 0 for album in self.albums[1:]: try: self.tree.set_value(self.tree.get_iter((a,)), 0, None) except: pass p = 0 for pic in album.pics: try: self.tree.set_value(self.tree.get_iter((a, p)), 0, None) except: pass while gtk.events_pending(): gtk.main_iteration() p += 1 a += 1 def TogglePreviews(self, widget): """ Toggle left-side list previews """ self.prefs['gui_treeviewpreviews'] = self.win.get_widget('list_previews1').get_active() if self.prefs['gui_treeviewpreviews']: if len(self.treeview.get_columns()) == 1: self.treeview.insert_column(self.columnpx, 0) self.PopulateIcons() else: self.DestroyIcons() if len(self.treeview.get_columns()) == 2: self.treeview.remove_column(self.columnpx) def ToggleToolbar(self, widget): """ Toggle the toolbar """ self.prefs['gui_toolbar'] = self.win.get_widget('toolbar2').get_active() if self.prefs['gui_toolbar']: self.win.get_widget('toolbar1').show() else: self.win.get_widget('toolbar1').hide() def ToggleDetails(self, widget): """ Toggle right-side details pane """ self.prefs['gui_detailspane'] = self.win.get_widget('details_pane1').get_active() if self.prefs['gui_detailspane']: self.win.get_widget('notebook1').show() self.Details(None) else: self.win.get_widget('notebook1').hide() def DBPath2RealPath(self, dbpath): """ Convert a Photo Database path (fullres) to real path """ realpath = u"%s" % dbpath realpath = realpath.encode('utf-8') realpath = os.path.join(self.DB.dbdir, *realpath.split(':')[1:]) return realpath def Details(self, widget): #, path, column): """ React to single-click on the tree view, displaying the details if a photo is selected. """ self.ShowActions(None) self.win.get_widget('remove_full_resolution').set_sensitive(False) self.win.get_widget('add_full_resolution').set_sensitive(False) self.win.get_widget('delete_reference1').set_sensitive(False) self.win.get_widget('rename_album1').set_sensitive(False) self.SetSensitive(('savefullres', 'save1thumb', 'save2thumb', 'save3thumb', 'save4thumb'), False) item_selected = self.treeview.get_selection().get_selected()[1] path = self.tree.get_path(item_selected) if self.win.get_widget('details_pane1').get_active(): self.ClearDetails() if len(path) > 1: # We are interacting with an image. if self.win.get_widget('details_pane1').get_active(): self.imagehbox.show() if self.ipod_model == 'Nano': self.SetSensitive(('expander1', 'expander2'), True) else: self.SetSensitive(('expander1', 'expander2', 'expander3', 'expander4'), True) if self.ipod_model == 'Nano': self.SetSensitive(('save1thumb', 'save2thumb'), True) else: self.SetSensitive(('save1thumb', 'save2thumb', 'save3thumb', 'save4thumb'), True) album = None if len(self.albums) > (path[0]+1): album = self.albums[path[0]+1] if album != None and len(album.pics) > path[1] and self.photos_to_add.count(album.pics[path[1]]) == 0 and self.photos_to_delete.count(album.pics[path[1]]) == 0: # Not added now, the image was already there; neither deleted. self.current_photo_id = album.pics[path[1]] self.win.get_widget('delete_reference1').set_sensitive(True) # Do deleting reference possible, since image is already stored. if self.DB.HasFullResolution(self.current_photo_id): self.win.get_widget('savefullres').set_sensitive(True) if self.win.get_widget('details_pane1').get_active(): self.notebook.set_current_page(1) #fullres = '%s' % self.photos[album.pics[path[1]]-100].fullres.replace('\000', '') # Removing null characters fullres = u"%s" % self.photos[album.pics[path[1]]-100].fullres fullres = fullres.encode('utf-8') photofile = os.path.join(self.DB.dbdir, *fullres.split(':')[1:]) # Converting from iPod (Mac) relative path to absolute native path self.ShowImage(photofile, path[0]+1, album.pics[path[1]]) if self.fullres_to_delete.count(self.current_photo_id) == 0: self.win.get_widget('remove_full_resolution').set_sensitive(True) else: if self.win.get_widget('details_pane1').get_active(): self.label.set_markup(_('No full resolution image')) self.notebook.set_current_page(2) self.win.get_widget('label4').set_sensitive(False) self.win.get_widget('expander1').emit('activate') fullres_add_sensitive = True for fullres_addition in self.fullres_to_add: if fullres_addition[1] == self.current_photo_id: fullres_add_sensitive = False break self.win.get_widget('add_full_resolution').set_sensitive(fullres_add_sensitive) else: # The image has been just added itemtext = self.tree[(path[0], path[1])][1] itemdot = itemtext.find('.') for new_photo in self.photos_to_add: if new_photo[1] == (path[0]+1) and os.path.basename(new_photo[0]) == itemtext[itemdot+2:]: if self.win.get_widget('details_pane1').get_active(): self.ShowImage(new_photo[0], path[0]+1, itemtext[:itemdot], False) self.current_photo_id = None # Thumbnails do not exist yet break #self.label.set_text(_('Image has been just added. Its details will be available after saved.')) elif len(path) == 1: # We are interacting with an album. self.win.get_widget('rename_album1').set_sensitive(True) self.win.get_widget('delete_reference1').set_sensitive(True) if self.win.get_widget('details_pane1').get_active(): self.notebook.set_current_page(0) self.ShowAlbum(path[0]+1) def ToggleAlbum(self, widget): """ Expand or collapse an album """ path = self.GetCurrentTVPath() if self.treeview.row_expanded(path): # The album row is already expanded, so we collapse it. self.treeview.collapse_row(path) else: self.treeview.expand_to_path(path) def ExpandPath(self, widget, path): """ Expand treeview path """ self.treeview.expand_to_path(path) def CollapsePath(self, widget, path): """ Collapse treeview path """ self.treeview.collapse_row(path) def ShowAlbum(self, album_id): """ Show album details in the proper (first) notebook page """ album_iter = self.tree.get_iter((album_id-1,)) album_name = self.tree.get_value(album_iter, 1) album_n_pics = self.tree.iter_n_children(album_iter) if album_id >= len(self.albums): statuscolor = '#990000' statusname = _('To be created') else: statuscolor = '#009900' statusname = _('Available') self.win.get_widget('detailsphotoalbumlabel').set_markup(_('Photo Album:\t\t%s\nPhoto Album ID:\t%s\nNumber of photos:\t%s\nStatus:\t\t%s') % (album_name, album_id, album_n_pics, statuscolor, statusname)) def ShowImage(self, photofile, album_id, photo_id, saved=True): """ Show image in the details pane on the right """ self.ShowAlbum(album_id) if os.path.isfile(photofile): self.details_photofile = photofile self.savefilename = os.path.basename(photofile) self.SetSensitive(('spinbutton1', 'saveimagebutton'), True) pixbuf = gtk.gdk.pixbuf_new_from_file(photofile) width = pixbuf.get_width() height = pixbuf.get_height() if saved: imgstatuscolor = '#009900' imgstatusname = _('Saved') else: imgstatuscolor = '#990000' imgstatusname = _('Not saved yet') self.label.set_markup(_('Photo Album: %s \nPhoto ID: %s \nPhoto path: %s \nOriginal size: %sx%s \nStatus: %s') % (self.albums[album_id].name, photo_id, photofile, width, height, imgstatuscolor, imgstatusname)) win_width, win_height = self.window.get_size() ratio_width, ratio_height = getRatioSize(width, height, win_width-316, win_height-196) pixbuf = pixbuf.scale_simple(ratio_width, ratio_height, gtk.gdk.INTERP_BILINEAR) # To update, offering the possibility to change the zoom level self.image.set_from_pixbuf(pixbuf) self.spinbutton.set_value(ratio_width*100/width) #self.current_album = path[0]+1 else: self.details_photofile = self.savefilename = None self.ClearDetails() self.label.set_text(_('Full resolution photo not found. Maybe have you deleted it?')) def SaveImage(self, widget): """ Save the image currently displayed in the details pane on the right """ if self.details_photofile != None: photosavename = self.filechooser.get_filename() self.filechooser.destroy() shutil.copy(self.details_photofile, photosavename) self.prefs['path_lastsavedimages'] = os.path.dirname(photosavename) def ZoomImage(self, widget): """ Change the zoom of the image currently displayed in the details pane on the right """ if self.details_photofile != None: while gtk.events_pending(): gtk.main_iteration() pixbuf = gtk.gdk.pixbuf_new_from_file(self.details_photofile) new_width = int(pixbuf.get_width()*self.spinbutton.get_value()/100) new_height = int(pixbuf.get_height()*self.spinbutton.get_value()/100) pixbuf = pixbuf.scale_simple(new_width, new_height, gtk.gdk.INTERP_BILINEAR) self.image.set_from_pixbuf(pixbuf) while gtk.events_pending(): gtk.main_iteration() def DetailsDoubleClick(self, widget, path, column): """ React to double-click on the tree view, asking to rename if a photo album is selected. """ if len(path) == 1: # The double-click occurred on an album self.ToggleAlbum(None) elif len(path) > 1: self.ShowFullScreen(None) def ShowFullScreen(self, widget, startslideshow=False, slideshowinterval=3): """ Show the currently selected photo full screen """ try: photoid = self.GetCurrentTVPhotoID() self.slideshow_current_photoid = photoid self.slideshow = gtk.glade.XML('gpixpod.glade', 'slideshowwindow', 'gpixpod') self.slideshow_callbacks = {'on_slideshowclosebutton_clicked':self.SlideShowQuit, 'on_slideshowbackbutton_clicked':self.SlideShowBack, 'on_slideshowforwardbutton_clicked':self.SlideShowForward} self.slideshow.signal_autoconnect(self.slideshow_callbacks) self.slideshowwin = self.slideshow.get_widget('slideshowwindow') self.slideshow.get_widget('slideshowviewport').modify_bg(gtk.STATE_NORMAL, gtk.gdk.Color()) self.slideshowwin.fullscreen() self.SlideShowImage(photoid) if startslideshow: self.SlideShowT = gobject.timeout_add(slideshowinterval*1000, self.SlideShowStart) while gtk.events_pending(): gtk.main_iteration() except: pass def SlideShowImage(self, photoid): """ Display the image in the slideshow window """ gdkwin = self.slideshowwin.window gdkwin.set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH)) if photoid > 100: self.slideshow.get_widget('slideshowbackbutton').set_sensitive(True) else: self.slideshow.get_widget('slideshowbackbutton').set_sensitive(False) if photoid-99 < len(self.photos): self.slideshow.get_widget('slideshowforwardbutton').set_sensitive(True) else: self.slideshow.get_widget('slideshowforwardbutton').set_sensitive(False) if self.DB.HasFullResolution(photoid): photo = self.DBPath2RealPath(self.photos[photoid-100].fullres) else: photo = self.ConvertThumbnail(photoid, 0) toolbarh = self.slideshow.get_widget('toolbar3').size_request()[1] pixbuf = gtk.gdk.pixbuf_new_from_file_at_size(photo, gtk.gdk.screen_width(), gtk.gdk.screen_height()-toolbarh-5) self.slideshow.get_widget('slideshowimage').set_from_pixbuf(pixbuf) gdkwin.set_cursor(None) def SlideShowBack(self, widget): """ Display previous photo """ self.slideshow_current_photoid -= 1 self.SlideShowImage(self.slideshow_current_photoid) def SlideShowForward(self, widget): """ Display next photo """ self.slideshow_current_photoid += 1 self.SlideShowImage(self.slideshow_current_photoid) def SlideShowQuit(self, widget): """ Close the slideshow window """ self.slideshowwin.destroy() try: gobject.source_remove(self.SlideShowT) except: pass def SlideShowStart(self): """ Start automatic slideshow """ if self.slideshow_current_photoid-99 < len(self.photos): self.SlideShowForward(None) return True else: return False #gobject.source_remove(self.SlideShowT) def ShowRenameAlbum(self, widget): """ Show the dialog to rename a photo album """ path = self.GetCurrentTVPath() self.ShowGetLine(None, self.RenameAlbum, _('Rename photo album'), _('Enter the new name for the album "%s"') % self.tree[(path[0],)][1], self.tree[(path[0],)][1][3:-4]) def ClearDetails(self): """ Clear the displayed photo details (right pane) with default values """ self.image.set_from_pixbuf(None) self.label.set_text('') self.CollapseThumbnails() self.SetSensitive(('spinbutton1', 'saveimagebutton', 'expander1', 'expander2', 'expander3', 'expander4'), False) def NotebookPage(self, widget, page, page_num): """ Manage page switching on the notebook (details pane on the right) """ if page_num == 1: # Then we are processing the thumbnails page pass # self.win.get_widget('expander1').emit('activate') # if self.current_photo_id != None and self.current_photo_id > 0: # thumbs = [] # convthumbs = [] # for thumb in self.DB.thumbsizes: # thumbs.append(os.path.join(self.DB.dbdir, '.SingleThumbs', '%sx%s' % (thumb[0], thumb[1]), str(self.current_photo_id))) # convthumbs.append(os.path.join(self.DB.dbdir, '.ConvertedThumbs', '%sx%s' % (thumb[0], thumb[1]), '%s.png' % str(self.current_photo_id))) # if not os.path.isfile(convthumbs[0]): # while gtk.events_pending(): # gtk.main_iteration() # fromInterlacedUYVY(thumbs[0], convthumbs[0]) # while gtk.events_pending(): # gtk.main_iteration() # self.imageipodthumb0.set_from_file(convthumbs[0]) # for thumbx in (1, 2, 3): # if not os.path.isfile(convthumbs[thumbx]): # while gtk.events_pending(): # gtk.main_iteration() # fromRGB565(thumbs[thumbx], self.DB.thumbsizes[thumbx][0], self.DB.thumbsizes[thumbx][1], savefilename=convthumbs[thumbx]) # while gtk.events_pending(): # gtk.main_iteration() # self.win.get_widget('imageipodthumb%s' % thumbx).set_from_file(convthumbs[thumbx]) # self.current_photo_id = None def ConvertThumbnail(self, photo_id, thumb_index): """ Convert the thumbnail of the specified index for the photo ID """ if photo_id > 0: thumb = os.path.join(self.DB.sglthumbsdir, '%sx%s' % (self.DB.thumbsizes[thumb_index][0], self.DB.thumbsizes[thumb_index][1]), str(photo_id)) convthumb = os.path.join(self.DB.dbdir, '.ConvertedThumbs', '%sx%s' % (self.DB.thumbsizes[thumb_index][0], self.DB.thumbsizes[thumb_index][1]), '%s.png' % str(photo_id)) thumbf = open(thumb) thumbcon = thumbf.read() thumbf.close() if thumb_index == 0 and self.ipod_model != 'Nano': if not os.path.isfile(convthumb): #fromInterlacedUYVY(thumb, convthumb) imgconvert.fromInterlacedUYVY(thumbcon, convthumb) else: if not os.path.isfile(convthumb): if thumb_index == 0 and self.ipod_model == 'Nano': #fromRGB565(thumb, self.DB.thumbsizes[thumb_index][0], self.DB.thumbsizes[thumb_index][1], False, savefilename=convthumb) imgconvert.fromRGB565(thumbcon, self.DB.thumbsizes[thumb_index][0], self.DB.thumbsizes[thumb_index][1], False, True, convthumb) elif thumb_index == 1 and (self.ipod_model == 'Photo' or self.ipod_model == 'Color'): #fromRGB565(thumb, self.DB.thumbsizes[thumb_index][1], self.DB.thumbsizes[thumb_index][0], True, True, savefilename=convthumb) imgconvert.fromRGB565(thumbcon, self.DB.thumbsizes[thumb_index][0], self.DB.thumbsizes[thumb_index][1], True, True, convthumb) else: #fromRGB565(thumb, self.DB.thumbsizes[thumb_index][0], self.DB.thumbsizes[thumb_index][1], savefilename=convthumb) imgconvert.fromRGB565(thumbcon, self.DB.thumbsizes[thumb_index][0], self.DB.thumbsizes[thumb_index][1], True, False, convthumb) return convthumb def ExpandThumbnail(self, widget, thumb_index): """ Expand and display the thumbnail with the specified index in the second page of the right pane """ expander = self.win.get_widget('expander%s' % (thumb_index+1)) gdkwin = widget.window if expander.get_expanded() == False and self.current_photo_id != None and self.current_photo_id > 0: gdkwin.set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH)) convthumb = self.ConvertThumbnail(self.current_photo_id, thumb_index) while gtk.events_pending(): gtk.main_iteration() self.win.get_widget('imageipodthumb%s' % thumb_index).set_from_file(convthumb) gdkwin.set_cursor(None) def CollapseThumbnails(self): """ Collapse all the thumbnails expanders and reset their images """ self.win.get_widget('expander1').set_expanded(False) self.win.get_widget('expander2').set_expanded(False) self.win.get_widget('expander3').set_expanded(False) self.win.get_widget('expander4').set_expanded(False) self.imageipodthumb0.set_from_file(None) self.imageipodthumb1.set_from_file(None) self.imageipodthumb2.set_from_file(None) self.imageipodthumb3.set_from_file(None) def SaveThumb(self, widget, thumb_index): """ Prompt to save the thumb with the specified index for the currently selected photo """ if self.ipod_model == 'Nano': if thumb_index == 1: thumb_index = 0 elif thumb_index == 3: thumb_index = 1 if self.current_photo_id != None: self.savethumbindex = thumb_index savefilename = '%s-%sx%s.png' % (self.current_photo_id, self.DB.thumbsizes[thumb_index][0], self.DB.thumbsizes[thumb_index][1]) self.ShowChooser(None, self.ActuallySaveThumb, _('Images'), 'PIXBUF', False, self.prefs['path_lastsavedimages'], False, gtk.FILE_CHOOSER_ACTION_SAVE, _('Save image'), savefilename=savefilename) def ActuallySaveThumb(self, widget): """ Actually save the thumb as triggered by the SaveThumb method """ thumbsavefn = self.filechooser.get_filename() thumb = self.ConvertThumbnail(self.current_photo_id, self.savethumbindex) shutil.copyfile(thumb, thumbsavefn) self.filechooser.destroy() def Eject(self, widget): """ Eject the connected detected iPod """ ejecting_i, ejecting_o = os.popen4('eject %s' % self.ipod_mountpoint) ejecting_i.close() ejecting_result = ejecting_o.read() if ejecting_result == 'eject: unable to eject, last error: Invalid argument\n': self.Say(_('iPod ejected, but the Do not disconnect message seems to be still shown on the iPod.'), '%s %s' % (_('To let disappear the message from the iPod, make your eject executable setuid root'), _('(chmod +s `which eject` as root)'))) ejecting_o.close() def Quit(self, widget): """ Manage quitting, asking to save if there are unsaved changes. """ if self.win.get_widget('save1').get_property('sensitive'): if self.Ask(_('There are unsaved changes'), _('Do you want to save your changes before closing this program?'), gtk.BUTTONS_YES_NO): self.Save(None) self.WritePreferences() if self.DB != None: self.DB.RemoveThumbsCache(os.path.join(self.DB.dbdir, '.ConvertedThumbs')) gtk.main_quit() def ShowGetLine(self, widget, ok_function, title=None, message=None, entrytext=None): """ Show a dialog to let enter a new line (e.g. to add a new album, or to renaming an existing one) """ self.getline = gtk.glade.XML('gpixpod.glade', 'dialog2', 'gpixpod') self.getlineinput = self.getline.get_widget('dialog2') if self.icon: self.getlineinput.set_icon_from_file(self.icon) self.getlineinput.set_default_response(gtk.RESPONSE_OK) self.getline_callbacks = {'on_cancelbutton1_clicked':self.DestroyGetLine, 'on_okbutton1_clicked':ok_function} self.getline.signal_autoconnect(self.getline_callbacks) if title != None: self.getlineinput.set_title(title) if message != None: label = self.getline.get_widget('label2') label.set_markup(message) if entrytext != None: entry = self.getline.get_widget('entry1') entry.set_text(entrytext) entry.select_region(0, -1) def DestroyGetLine(self, widget): """ Actually destroy the GetLine dialog when receiving the destroy signal """ self.getlineinput.destroy() def AddPhotoChooser(self, widget): """ Switch AddPhoto (with/without full resolution) action according to the fullres checkbutton """ if self.chooser.get_widget('fullrescheckbutton').get_active(): self.AddPhoto(None, None, True) else: self.AddPhoto(None, None, False) def ShowChooser(self, widget, ok_function, filter_name=None, pattern=None, multiple=False, path=None, preview=False, chooser_type=None, title=None, savefilename=None): """ Show file chooser, to select and open file based on the pattern specified, passing to the specified function """ self.chooser_callbacks = {'on_button1_clicked':self.DestroyChooser, 'on_button2_clicked':ok_function} self.chooser = gtk.glade.XML('gpixpod.glade', 'filechooserdialog1', 'gpixpod') self.chooser.signal_autoconnect(self.chooser_callbacks) self.filechooser = self.chooser.get_widget('filechooserdialog1') if self.icon: self.filechooser.set_icon_from_file(self.icon) self.filechooser.set_select_multiple(multiple) if path != None: self.filechooser.set_current_folder(path) if filter_name != None and pattern != None: filter = gtk.FileFilter() filter.set_name(filter_name) if pattern == 'PIXBUF' and chooser_type == None: # Making sure we are not saving, too! filter.add_pixbuf_formats() self.chooser.get_widget('imagebehaviourexpander').show_all() self.chooser.get_widget('imagebehaviourexpander').set_sensitive(True) self.chooser.get_widget('autorotateexpander').show_all() self.chooser.get_widget('autorotateexpander').set_sensitive(True) if self.prefs['photo_behaviour'] == 'Zoom': self.chooser.get_widget('imagebehaviour_radiobutton2').set_active(True) elif self.prefs['photo_behaviour'] == 'Stretch': self.chooser.get_widget('imagebehaviour_radiobutton3').set_active(True) else: self.chooser.get_widget('imagebehaviour_radiobutton1').set_active(True) if self.prefs['photo_autorotate'] == 'CW': self.chooser.get_widget('autorotatecwfradiobutton').set_active(True) elif self.prefs['photo_autorotate'] == 'CCW': self.chooser.get_widget('autorotateccwfradiobutton').set_active(True) else: self.chooser.get_widget('autorotatefradiobutton').set_active(True) self.chooser.get_widget('fullrescheckbutton').show() self.chooser.get_widget('fullrescheckbutton').set_active(self.DB.dbcopyfullres) self.chooser.get_widget('fullrescheckbutton').set_sensitive(True) self.chooser.get_widget('recursivecheckbutton').show() self.chooser.get_widget('recursivecheckbutton').set_sensitive(True) self.chooser.get_widget('duplicatescheckbutton').show() self.chooser.get_widget('duplicatescheckbutton').set_sensitive(True) else: filter.add_pattern(pattern) self.filechooser.add_filter(filter) if chooser_type != None: self.filechooser.set_action(chooser_type) if chooser_type == gtk.FILE_CHOOSER_ACTION_SAVE: self.filechooser.set_do_overwrite_confirmation(True) if savefilename != None: self.filechooser.set_current_name(savefilename) elif self.savefilename != None: self.filechooser.set_current_name(self.savefilename) if title != None: self.filechooser.set_title(title) if preview: self.chooser.signal_connect('on_filechooserdialog1_selection_changed', self.ChooserUpdatePreview) self.filechooser_previewbox = gtk.VBox() self.filechooser_previewbox.set_size_request(200, 200) self.filechooser_preview = gtk.Image() self.filechooser_previewlabel = gtk.Label() self.filechooser_previewbox.pack_start(self.filechooser_preview) self.filechooser_previewbox.pack_start(self.filechooser_previewlabel) self.filechooser.set_preview_widget(self.filechooser_previewbox) self.filechooser_previewbox.show_all() def ChooserUpdatePreview(self, widget): """ Update the preview widget set in the file chooser on change of selection """ preview_filename = self.filechooser.get_preview_filename() if preview_filename != None and os.path.isfile(preview_filename): self.filechooser.set_preview_widget_active(True) pixbuf = gtk.gdk.pixbuf_new_from_file(preview_filename) # Calculating proper width/height with ratio within limits new_width = width = pixbuf.get_width() new_height = height = pixbuf.get_height() max_width = 200 max_height = 200 if new_width > max_width or new_height > max_height: new_width = max_width new_height = max_width*height/width if new_height > max_height: new_height = max_height new_width = max_height*width/height pixbuf = pixbuf.scale_simple(new_width, new_height, gtk.gdk.INTERP_TILES) # INTER_TILES is a bit faster, since quality is not needed here self.filechooser_preview.set_from_pixbuf(pixbuf) preview_info = gtk.gdk.pixbuf_get_file_info(preview_filename) # a tuple: pixbuf dictionary, width, height preview_stat = os.stat(preview_filename) preview_date = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(preview_stat[8])) # Last modified date of the image file preview_size = preview_stat[6]/1024 # Size in Kb self.filechooser_previewlabel.set_markup('%s, %sx%s, %s KB\n%s' % (preview_info[0]['name'], width, height, preview_size, preview_date)) else: self.filechooser.set_preview_widget_active(False) def DestroyChooser(self, widget): """ Actually destroy the FileChooser dialog when receiving the destroy signal """ self.filechooser.destroy() def AddAlbum(self, widget, album_name=None): """ Add a new album to the tree view, marking to actually add it when saving """ if album_name == None: album_name = self.getline.get_widget('entry1').get_text() self.getlineinput.destroy() self.albums_to_add.append(album_name) # Mark the new album to be added when saving #self.GetDBLists() if self.photoalbumicon and self.win.get_widget('list_previews1').get_active(): albumpx = gtk.gdk.pixbuf_new_from_file_at_size(self.photoalbumicon, 48, 48) else: albumpx = None self.tree.append(None, [albumpx, '%s' % album_name]) # Adding the new album to the tree view self.ToSave() # Marking as unsaved def RenameAlbum(self, widget): """ Rename an album in the tree view, marking to actually renaming it when saving """ new_album_name = self.getline.get_widget('entry1').get_text() self.getlineinput.destroy() item_selected = self.treeview.get_selection().get_selected()[1] path_selected = self.tree.get_path(item_selected) self.albums_to_rename.append((path_selected[0]+1, new_album_name)) # Mark the album to be actually renamed when saving self.tree.set_value(self.tree.get_iter((path_selected[0],)), 1, '%s' % new_album_name) # Updating tree view row self.ToSave() # Marking as unsaved def GetImageBehaviour(self): """ Get selected image behaviour """ behaviour = 'Fit' if self.chooser.get_widget('imagebehaviour_radiobutton2').get_active(): behaviour = 'Zoom' if self.chooser.get_widget('imagebehaviour_radiobutton3').get_active(): behaviour = 'Stretch' return behaviour def AddPhoto(self, widget, filenames=None, copyfullres=None): # Third argument added to let the function being called by DND """ Add new photo(s) to the tree view, marking to actually add it/them when saving """ if copyfullres == True: fullreslabel = _(' (with full resolution)') elif copyfullres == False: fullreslabel = _(' (without full resolution)') else: copyfullres = self.DB.dbcopyfullres fullreslabel = '' path = self.GetCurrentTVPath() album_selected = self.tree.get_iter((path[0],)) # The parent album is obtained from the first element of the path if filenames == None: filenames = self.filechooser.get_filenames() if len(filenames) == 0: filenames_folder = self.filechooser.get_current_folder() if filenames_folder: filenames = [filenames_folder] behaviour = self.GetImageBehaviour() if self.chooser.get_widget('imagebehaviour_radiobutton2').get_active(): behaviour = 'Zoom' if self.chooser.get_widget('imagebehaviour_radiobutton3').get_active(): behaviour = 'Stretch' if self.chooser.get_widget('autorotatecwfradiobutton').get_active(): autorotate = 'CW' elif self.chooser.get_widget('autorotateccwfradiobutton').get_active(): autorotate = 'CCW' else: autorotate = None if self.chooser.get_widget('recursivecheckbutton').get_active(): filenames = expand_paths(filenames, True) if self.chooser.get_widget('duplicatescheckbutton').get_active(): filenames2 = [] for filename in filenames: if filename not in self.sync: filenames2.append(filename) filenames = filenames2 self.filechooser.destroy() if len(filenames) > 0: self.prefs['path_lastimages'] = os.path.dirname(filenames[-1]) # Remembering last images directory for filename in filenames: if os.path.isfile(filename) and gtk.gdk.pixbuf_get_file_info(filename): self.photos_to_add.append((filename, path[0]+1, copyfullres, behaviour, autorotate)) # Mark the new photo to be actually added when saving new_photo_id = len(self.photos)+99+len(self.photos_to_add) #self.GetDBLists() # Or completely rePopulate? # Adding the new photo to the tree view self.tree.append(album_selected, [None, '%s. %s%s' % (new_photo_id, os.path.basename(filename), fullreslabel)]) self.ToSave() # Marking as unsaved # def AddPhotoWithFR(self, widget): # """ Add new photo(s) including full resolution image, regardless of the global setting """ # path = self.GetCurrentTVPath() # album_selected = self.tree.get_iter((path[0],)) # filenames = self.filechooser.get_filenames() # behaviour = self.GetImageBehaviour() # self.filechooser.destroy() # for filename in filenames: # if os.path.isfile(filename): # self.photos_to_add.append((filename, path[0]+1, True, behaviour)) # new_photo_id = len(self.photos)+99+len(self.photos_to_add) # self.tree.append(album_selected, ['%s (%s). %s' % (new_photo_id, _('with full resolution'), os.path.basename(filename))]) # self.ToSave() # self.prefs['path_lastimages'] = os.path.dirname(filename) # # def AddPhotoWithoutFR(self, widget): # """ Add new photo(s) excluding full resolution image, regardless of the global setting """ # path = self.GetCurrentTVPath() # album_selected = self.tree.get_iter((path[0],)) # filenames = self.filechooser.get_filenames() # behaviour = self.GetImageBehaviour() # self.filechooser.destroy() # for filename in filenames: # if os.path.isfile(filename): # self.photos_to_add.append((filename, path[0]+1, False, behaviour)) # new_photo_id = len(self.photos)+99+len(self.photos_to_add) # self.tree.append(album_selected, ['%s (%s). %s' % (new_photo_id, _('without full resolution'), os.path.basename(filename))]) # self.ToSave() # self.prefs['path_lastimages'] = os.path.dirname(filename) def AddReference(self, widget, photo_id=None): """ Add a reference to an already existing photo in the currently selected photo album """ path = self.GetCurrentTVPath() album_selected = self.tree.get_iter((path[0],)) album_id = path[0]+1 if photo_id == None: photo_id = self.getline.get_widget('entry1').get_text() self.getlineinput.destroy() photo_id = int(photo_id) if len(self.photos) > (photo_id-100): self.references_to_add.append((photo_id, album_id)) if self.photos[photo_id-100].fullres == None: fullresname = _('No full resolution image') else: fullresname = self.photos[photo_id-100].fullres.split(':')[-1] self.tree.append(album_selected, [None, '%s. %s' % (photo_id, fullresname)]) self.ToSave() else: self.Say(_('Not existing photo!'), _('The number is not a valid Photo ID.'), gtk.MESSAGE_WARNING) def DeleteReference(self, widget): """ Remove the reference related to the currently selected photo, or to all children photos if a photo album is selected instead """ path = self.GetCurrentTVPath() item_selected = self.treeview.get_selection().get_selected()[1] if len(path) == 1: if self.Ask('%s' % _('Delete all photo album references?'), _('No photos will be deleted, but all the photo album references.')): self.references_album_to_delete.append(path[0]+1) self.tree.remove(item_selected) self.ToSave() self.HideActions(None) self.ClearDetails() elif len(path) > 1: self.references_photo_to_delete.append((self.GetCurrentTVPhotoID(), path[0]+1)) self.tree.remove(item_selected) self.ToSave() self.HideActions(None) self.ClearDetails() def GetCurrentTVPath(self): """ Get the path of the currently selected item in the treeview """ item_selected = self.treeview.get_selection().get_selected()[1] path = self.tree.get_path(item_selected) return path def GetCurrentTVText(self): """ Get the text of the currently selected item in the treeview """ path = self.GetCurrentTVPath() text = self.tree[(path[0], path[1])][1] return text def SetCurrentTVText(self, text): """ Set the specified text on the currently selected item in the treeview """ path = self.GetCurrentTVPath() self.tree[(path[0], path[1])][1] = text def GetCurrentTVPhotoID(self): """ Get the photo ID from the currently selected item in the treeview """ text = self.GetCurrentTVText() dot = text.find('.') return int(text[:dot]) def AddFullResolution(self, widget): """ Add a full resolution image to the current photo """ filename = self.filechooser.get_filename() self.filechooser.destroy() photo_id = self.GetCurrentTVPhotoID() self.fullres_to_add.append((filename, photo_id)) self.SetCurrentTVText('%s. %s (%s)' % (photo_id, os.path.basename(filename), _('available after saved'))) self.win.get_widget('add_full_resolution').set_sensitive(False) self.ToSave() def RemoveFullResolution(self, widget): """ Remove the full resolution image from the current photo """ if self.Ask('Remove full resolution photo?', 'Are you sure to remove the full resolution image from the currently selected photo?'): photo_id = self.GetCurrentTVPhotoID() self.fullres_to_delete.append(photo_id) self.SetCurrentTVText('%s. %s' % (photo_id, _('Full resolution image will be deleted after saved'))) self.win.get_widget('remove_full_resolution').set_sensitive(False) self.ToSave() def Delete(self, widget): """ Delete either a photo or a whole album from the tree view, marking to actually delete it when saving """ item_selected = self.treeview.get_selection().get_selected()[1] path_selected = self.tree.get_path(item_selected) if len(path_selected) == 1: # then we are deleting an album if self.Ask(_('Are you sure to delete the whole album?')): self.tree.remove(item_selected) # Removing the photo album and its children from tree view self.albums_to_delete.append(path_selected[0]+1) # Marking the album to be deleted self.ToSave() # Marking as unsaved else: # then the request of deletion is for a photo # Getting the photo id from the content of the row of the tree view, the first part (before the first dot, see the row in the GUI) photo_id = int(self.tree.get_value(item_selected, 1).split('.')[0]) self.photos_to_delete.append(photo_id) # Marking the photo to be actually deleted when saving self.tree.remove(item_selected) # Removing the photo from the tree view self.ToSave() # Marking as unsaved self.ClearDetails() # We clear the image details (right pane) self.HideActions(None) # After deleting, we lose the selection: thus we should at least hide the related actions. self.SetSensitive(('savefullres', 'save1thumb', 'save2thumb', 'save3thumb', 'save4thumb'), False) # not working yet and to delete # def TreeviewDrag(self, treeview, context, selection, target_id, etime): # """ Manage dragging out from the treeview """ # treeselection = treeview.get_selection() # model, iter = treeselection.get_selected() # path = model.get_path(iter) # data = model.get_value(iter, 0) # #selection.set(selection.target, 8, data) # album = self.albums[path[0]+1] # fullres = '%s' % self.photos[album.pics[path[1]]-100].fullres.replace('\000', '') # Removing null characters # photofile = os.path.join(self.DB.dbdir, *fullres.split(':')[1:]) # Converting from iPod (Mac) relative path to absolute native path # print photofile # if os.path.isfile(photofile): # pixbuf = gtk.gdk.pixbuf_new_from_file(photofile) # selection.set('image/pixbuf', 8, pixbuf) def TreeviewDrop(self, treeview, context, x, y, selection, info, etime): """ Manage dropping inside the treeview """ model = treeview.get_model() sel = treeview.get_selection() data = selection.data data = data.replace('file://', '') data = data.split() drop_info = treeview.get_dest_row_at_pos(x, y) if drop_info: path, position = drop_info sel.select_path(path[:1]) else: sel.select_path((len(model)-1)) data2 = [] for filename in data: filenamepart, ext = os.path.splitext(filename) if re.match('[jJ][pP][eE]?[gG]|[pP][nN][gG]|[sS][vV][gG]|[wW][bB][mM][pP]|[wW][mM][fF]|[bB][mM][pP]|[gG][iI][fF]|[tT][iI][fF][fF]|[xX][pP][mM]', ext[1:]) != None: if gtk.gdk.pixbuf_get_file_info(filename) != None: data2.append(filename) if len(data) > 0: self.AddPhoto(None, data2) # if context.action == gtk.gdk.ACTION_MOVE: # context.finish(True, True, etime) # return def TreeviewClick(self, treeview, event): """ React to mouse (right) button click on the treeview """ if event.button == 3: pathinfo = treeview.get_path_at_pos(int(event.x), int(event.y)) if pathinfo: path = pathinfo[0] menu = gtk.Menu() if len(path) == 1: # Right click is happened on an album if self.treeview.row_expanded(path): collapseitem = gtk.MenuItem(_("Collapse")) collapseitem.connect("activate", self.CollapsePath, path) menu.append(collapseitem) else: expanditem = gtk.MenuItem(_("Expand")) expanditem.connect("activate", self.ExpandPath, path) menu.append(expanditem) renameitem = gtk.MenuItem(_("Rename")) renameitem.connect("activate", self.ShowRenameAlbum) menu.append(renameitem) deleteitem = gtk.ImageMenuItem(gtk.STOCK_DELETE) deleteitem.connect("activate", self.Delete) menu.append(deleteitem) menu.show_all() menu.popup(None, None, None, event.button, event.time) def ShowActions(self, widget): """ Show "Add Photo" and "Delete" actions (to be called when a treeview item is selected) """ self.SetSensitive(('addphoto1', 'addphotobutton', 'delete1', 'deletebutton', 'add_photo_reference1', 'delete_reference1', 'add_full_resolution', 'remove_full_resolution', 'showfullscreen', 'startslideshow'), True) def HideActions(self, widget): """ Hide "Add Photo" and "Delete" actions (to be called when no treeview items are selected) """ self.SetSensitive(('addphoto1', 'addphotobutton', 'delete1', 'deletebutton', 'add_photo_reference1', 'delete_reference1', 'add_full_resolution', 'remove_full_resolution', 'showfullscreen', 'startslideshow'), False) def NewAlbumFromUnreferencedPhotos(self, widget): """ Generate a new photo album containing all the currently unreferenced (orphaned) photos """ photos = self.DB.FindLostPhotos() if len(photos) > 0: self.AddAlbum(None, str(_('Previously unreferenced photos'))) album_id = len(self.albums)+len(self.albums_to_add)-len(self.albums_to_delete)-1 self.treeview.get_selection().select_path((album_id-1,)) for photo_id in photos: self.AddReference(None, int(photo_id)) #if len(self.references_photo_to_delete) > 0: # for photo_id in self.references_photo_to_delete: # self.AddReference(None, photo_id[0]) #if len(self.references_album_to_delete) > 0: # for album_id in self.references_album_to_delete: # if len(self.albums) > album_id: # for photo_id in self.albums[album_id].pics: # self.AddReference(None, photo_id) else: self.Say('%s' % _('No unreferenced photos'), _('There are not unreferenced photos. Each existing photo is linked to at least one photo album.')) def CleanCache(self, widget): # More useful for development purposes than for users, maybe to be moved away """ Clean the single thumbnails cache """ if self.Ask(_('Are you sure to remove thumbnails cache?'), '%s%s' % (_('Thumbnails cache is automatically removed when saving, only in rare cases you would remove it manually.\n'), _('It is recommended to cancel right away, unless you know what are you doing.'))): self.DB.RemoveThumbsCache() # Actually removing cache self.DB.RemoveThumbsCache(os.path.join(self.DB.dbdir, '.ConvertedThumbs')) # Removing PNG converted thumbs, too self.win.get_widget('cleancache1').set_sensitive(False) self.Say('%s' % _('Thumbnails cache removed!')) def Rescue(self, widget): """ Check the database for incongruences and try to fix it """ dblinescopy = copy.deepcopy(self.DB.dblines) self.DB.Rescue() if dblinescopy == self.DB.dblines: self.StatusBarNotify(_('Nothing has been modified. Photo Database seems in a proper status.')) else: self.StatusBarNotify(_('Photo Database has been modified. Please save now to complete the rescue.')) self.ToSave() def ShowIpodInfo(self, widget): """ Show the iPod info dialog """ self.ipodinfo = gtk.glade.XML('gpixpod.glade', 'ipodinfodialog', 'gpixpod') self.ipodinfodlg = self.ipodinfo.get_widget('ipodinfodialog') self.ipodinfo.signal_connect('on_ipodinfookbutton_clicked', self.DestroyIpodInfo) if self.ipodicon: self.ipodinfodlg.set_icon_from_file(self.ipodicon) self.ipodinfo.get_widget('ipodimage').set_from_file(self.ipodicon) try: i,o = os.popen4('df -h %s' % self.ipod_mountpoint) dfo = o.read() i.close(); o.close() dfo = dfo[dfo.find('\n')+1:].split() self.ipodinfo.get_widget('ipodinfospace').set_fraction(int(dfo[4][:-1])/100.0) self.ipodinfo.get_widget('ipodinfospace').set_text(dfo[4]) self.ipodinfo.get_widget('ipodinfototalspace').set_markup('%s' % dfo[1]) self.ipodinfo.get_widget('ipodinfousedspace').set_markup('%s' % dfo[2]) self.ipodinfo.get_widget('ipodinfoavailablespace').set_markup('%s' % dfo[3]) self.ipodinfo.get_widget('ipodinfodevice').set_markup('%s' % dfo[0]) self.ipodinfo.get_widget('ipodinfomountpoint').set_markup('%s' % dfo[5]) self.ipodinfo.get_widget('ipodinfophotodb').set_markup(_('%s photos in %s albums') % (len(self.DB.Photos()), len(self.DB.Albums())-1)) self.ipodinfo.get_widget('ipodinfomodel').set_markup('%s' % self.ipod_model) i,o = os.popen4('du -sh %s' % self.DB.dbdir) duo = o.read() duo = duo.split()[0] self.ipodinfo.get_widget('ipodinfousedforphotos').set_markup('%s' % duo) i.close(); o.close() except: pass def DestroyIpodInfo(self, widget): """ Destroy th iPod info dialog """ self.ipodinfodlg.destroy() def ShowAbout(self, widget): """ Show the about dialog """ gtk.about_dialog_set_url_hook(self.LaunchBrowser) self.about = gtk.glade.XML('gpixpod.glade', 'aboutdialog1', 'gpixpod') self.aboutdlg = self.about.get_widget('aboutdialog1') if self.icon: self.aboutdlg.set_icon_from_file(self.icon) if self.logo: self.aboutdlg.set_logo(gtk.gdk.pixbuf_new_from_file(self.logo)) def ShowPreferences(self, widget): """ Show the preferences dialog """ self.preferences = gtk.glade.XML('gpixpod.glade', 'preferencesdialog', 'gpixpod') self.preferencesdlg = self.preferences.get_widget('preferencesdialog') if self.icon: self.preferencesdlg.set_icon_from_file(self.icon) self.LoadDefaultPreferences() self.preferences_callbacks = {'on_cancelbutton2_clicked':self.DestroyPreferences, 'on_okbutton2_clicked':self.StorePreferences, 'on_defaultsbutton_clicked':self.RestoreDefaultPreferences, 'on_copyfullrescheckbutton_toggled':self.CopyFullResolution} self.preferences.signal_autoconnect(self.preferences_callbacks) def LoadDefaultPreferences(self): """ Load the default preferences """ self.preferences.get_widget('autodetectcheckbutton').set_active(self.prefs['ipod_autodetect']) self.preferences.get_widget('copyfullrescheckbutton').set_active(self.prefs['photo_copyfullres']) self.preferences.get_widget('askbeforeopencheckbutton').set_active(self.prefs['ipod_askbeforeopen']) self.preferences.get_widget('impfilechooserbutton').set_current_folder(self.prefs['ipod_mountpoint']) if self.prefs['path_thumbscache'] == None: self.preferences.get_widget('thumbscacheipodradiobutton').set_active(True) else: self.preferences.get_widget('thumbscachehomeradiobutton').set_active(True) if self.prefs['photo_behaviour'] == 'Zoom': self.preferences.get_widget('imagebehaviourzoomradiobutton').set_active(True) elif self.prefs['photo_behaviour'] == 'Stretch': self.preferences.get_widget('imagebehaviourstretchradiobutton').set_active(True) else: self.preferences.get_widget('imagebehaviourfitradiobutton').set_active(True) if self.prefs['photo_autorotate'] == 'CW': self.preferences.get_widget('autorotatecwradiobutton').set_active(True) elif self.prefs['photo_autorotate'] == 'CCW': self.preferences.get_widget('autorotateccwradiobutton').set_active(True) else: self.preferences.get_widget('autorotateradiobutton').set_active(True) ipodm = self.preferences.get_widget('ipodmodelcombobox') if self.prefs['ipod_model'] == 'Nano': ipodm.set_active(1) elif self.prefs['ipod_model'] == 'Photo': ipodm.set_active(2) elif self.prefs['ipod_model'] == 'Color': ipodm.set_active(3) elif self.prefs['ipod_model'] == '5G': ipodm.set_active(4) else: ipodm.set_active(0) def RestoreDefaultPreferences(self, widget): """ Restore the default preferences """ self.prefs = self.defaultprefs self.LoadDefaultPreferences() def DestroyPreferences(self, widget): """ Destroy the preferences dialog after received the destroy signal """ self.preferencesdlg.destroy() def StorePreferences(self, widget): """ Store the preferences """ self.prefs['ipod_autodetect'] = self.preferences.get_widget('autodetectcheckbutton').get_active() self.prefs['photo_copyfullres'] = self.preferences.get_widget('copyfullrescheckbutton').get_active() self.prefs['ipod_mountpoint'] = self.preferences.get_widget('impfilechooserbutton').get_current_folder() self.prefs['ipod_askbeforeopen'] = self.preferences.get_widget('askbeforeopencheckbutton').get_active() if self.preferences.get_widget('thumbscachehomeradiobutton').get_active(): self.prefs['path_thumbscache'] = os.path.join(self.preferencesdir, '.SingleThumbs') else: self.prefs['path_thumbscache'] = None if self.preferences.get_widget('imagebehaviourzoomradiobutton').get_active(): self.prefs['photo_behaviour'] = 'Zoom' elif self.preferences.get_widget('imagebehaviourstretchradiobutton').get_active(): self.prefs['photo_behaviour'] = 'Stretch' else: self.prefs['photo_behaviour'] = 'Fit' if self.preferences.get_widget('autorotatecwradiobutton').get_active(): self.prefs['photo_autorotate'] = 'CW' elif self.preferences.get_widget('autorotateccwradiobutton').get_active(): self.prefs['photo_autorotate'] = 'CCW' else: self.prefs['photo_autorotate'] = 'No' ipodm = self.preferences.get_widget('ipodmodelcombobox').get_active() if ipodm == 1: self.prefs['ipod_model'] = 'Nano' elif ipodm == 2: self.prefs['ipod_model'] = 'Photo' elif ipodm == 3: self.prefs['ipod_model'] = 'Color' elif ipodm == 4: self.prefs['ipod_model'] = '5G' else: self.prefs['ipod_model'] = None self.WritePreferences() self.LoadPreferences() self.preferencesdlg.destroy() def WritePreferences(self): """ Write the preferences on file """ prf = open(self.preferencesfile, 'w+') cPickle.dump(self.prefs, prf) prf.close() def LoadPreferences(self): """ Load the preferences """ prf = open(self.preferencesfile, 'r') loadedprefs = cPickle.load(prf) # Overriding default preferences for prefkey, prefvalue in loadedprefs.iteritems(): self.prefs[prefkey] = prefvalue prf.close() def CopyFullResolution(self, widget): """ Choose to copy / not copy full resolution photo on the iPod """ if widget.get_active(): s = _('Full resolution copies of your photos will be uploaded on your iPod') else: s = _('Full resolution copies of your photos will not be uploaded on your iPod') self.Say(s, _('This is now the default action of drag & drop and of the Add Photo button. You can always use the Add Photo with/without full resolution buttons to override this setting')) def Ask(self, sentence, secondary=None, buttons=gtk.BUTTONS_OK_CANCEL): """ Pop-up a question message dialog """ self.questionbox = gtk.MessageDialog(self.window, gtk.DIALOG_MODAL, gtk.MESSAGE_QUESTION, buttons) if self.icon: self.questionbox.set_icon_from_file(self.icon) self.questionbox.set_markup(sentence) if secondary != None: self.questionbox.format_secondary_markup(secondary) response = self.questionbox.run() self.questionbox.destroy() if response == -5: # The numeric code (-5) is returned from GTK when pressing the OK button return True else: return False def Say(self, sentence, secondary=None, msg_type=gtk.MESSAGE_INFO): """ Pop-up one button info message box """ self.messagebox = gtk.MessageDialog(self.window, gtk.DIALOG_MODAL, msg_type, gtk.BUTTONS_OK) if self.icon: self.messagebox.set_icon_from_file(self.icon) self.messagebox.set_markup(sentence) if secondary != None: self.messagebox.format_secondary_markup(secondary) self.messagebox.run() self.messagebox.destroy() def LaunchBrowser(self, widget, link): """ Launch a browser for the specified link """ webbrowser.open(link) def DonateForGPixPod(self): """ If running on Windows, ask for a donation sometimes """ if self.Ask(_('GPixPod development needs your help.'), _('If you like GPixPod, please consider buying it from http://www.flagar.com/en/software/gpixpod or donating by PayPal (then you could ask the author to remove this message). Proceed now?')): self.LaunchBrowser(None, 'https://www.paypal.com/xclick/business=flagar%40gmail.com&item_name=Helping+GPixPod+development&no_shipping=1&tax=0¤cy_code=EUR&lc=US') return True def CheckForVFATFS(self, mountpoint): """ Avoid querying about managing iPodLinux ext2/ext3 partitions, which should not contain Photos/Photo Databases! """ result = False mounts = open('/proc/mounts') for line in mounts: contents = line.split() if contents[1] == mountpoint and contents[2] == 'vfat': result = True break mounts.close() return result def OnIpodConnect(self, ipod_hal, ipod_udi): """ Do stuff when an iPod has been connected """ self.SetSensitive(('eject1', 'ejectbutton', 'ipodinfo1'), True) mountpoint = ipod_hal.get_ipod_mount_point(ipod_udi) self.ipod_mountpoint = mountpoint standard_loc = os.path.join(mountpoint, 'Photos', 'Photo Database') if self.CheckForVFATFS(mountpoint): model = getIpodModel(mountpoint) if model in ('5G', 'Nano', 'Photo', 'Color'): if os.path.isfile(standard_loc): if self.prefs['ipod_askbeforeopen'] == False or self.Ask(_('iPod %s and existing Photo Database found.') % model, _('Do you want to load the Photo Database found in the iPod attached to %s?') % mountpoint): self.DBOpen(standard_loc) else: if self.Ask(_('iPod %s found, attached to %s.') % (model, mountpoint), _('But Photo Database not found. Do you want to create a new one?')): self.DBOpen(standard_loc) else: self.Say(_('iPod found, attached to %s.') % mountpoint, _('But model not autodetected. You should really specify your iPod model in Preferences now.')) self.ShowPreferences(None) def OnIpodDisconnect(self, ipod_hal, ipod_udi): """ Do stuff when the iPod has been disconnected """ mountpoint = ipod_hal.get_ipod_mount_point(ipod_udi) if self.CheckForVFATFS(mountpoint): self.SetSensitive(('eject1', 'ejectbutton', 'ipodinfo1'), False) if self.DB != None: if self.win.get_widget('save1').get_property('sensitive'): unsaved_changes = _('All the unsaved changes will be lost.') else: unsaved_changes = _('There are not unsaved changes.') if self.Ask(_('iPod disconnected. Do you want to exit now?'), unsaved_changes): gtk.main_quit() else: self.Say(_('Please reconnect the iPod and without loading again the Photo Database'), _('Not reconnecting means that you could not save your changes, resulting in a crash. And re-opening would abort all changes'), gtk.MESSAGE_WARNING) if __name__ == '__main__': # FIXME: add exceptions handler! if len(sys.argv) > 1: import gpixpod_cli gpixpod_cli.option_parse() else: gpixpod = GPixPod() signal.signal(signal.SIGTERM, gpixpod.Kill) gpixpod.GUI() gtk.main() gpixpod-0.6.2/gpixpod_cli.py0000755000175000017500000002414610507301076015327 0ustar flagarflagar#!/usr/bin/env python # GPixPod - organize photos on your iPod, freely! # Copyright (C) 2006 Flavio Gargiulo (FLAGAR.com) # # 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. # # 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. from glob import glob from threading import Thread import gettext import gtk, sys, os, mh, optparse, cPickle, time import signal from utils import * gettext.bindtextdomain('gpixpod', 'po') gettext.textdomain('gpixpod') gettext.install('gpixpod', 'po', True) class GPixPodCLI: """ GPixPod command line interface class """ def __init__(self, verbose, ipod, model, album, recursive, fullres, behaviour, autorotate, force, args): """ Initialize class getting command line options and arguments """ try: homedir = os.path.expanduser('~') prefsdir = os.path.join(homedir, '.gpixpod') if not os.path.isdir(prefsdir): os.mkdir(prefsdir) prefsfile = open(os.path.join(prefsdir, 'config')) self.prefs = cPickle.load(prefsfile) self.thumbscache = None if 'path_thumbscache' in self.prefs: self.thumbscache = self.prefs['path_thumbscache'] if 'ipod_model' in self.prefs: self.ipodmodel = self.prefs['ipod_model'] prefsfile.close() except: self.thumbscache = None self.ipodmodel = None if model != None: self.ipodmodel = model if not force: try: syncfile = open(os.path.join(os.path.expanduser('~'), '.gpixpod', 'sync')) self.sync = list(cPickle.load(syncfile)) syncfile.close() except: self.sync = [] self.tosave = False self.verbose = verbose self.ipod_mountpoint = ipod self.album_name = album self.album_id = 0 self.recursive = recursive self.fullres = fullres self.behaviour = behaviour self.autorotate = autorotate self.skipsync = force self.filenames = args self.continue_processing = True def Open(self): """ Open the Photo Database and begin the process """ dbfilename = os.path.join(self.ipod_mountpoint, 'Photos', 'Photo Database') if self.verbose: sys.stdout.write(_("Loading Photo Database '%s'... ") % dbfilename) sys.stdout.flush() self.db = mh.MH(dbfilename, self.thumbscache, ipodmodel=self.ipodmodel) if self.verbose: sys.stdout.write("loaded.\n") if self.ipodmodel in ('5G', 'Photo', 'Color', 'Nano'): print _("Selected iPod model is: %s") % self.ipodmodel else: print _("Autodetected iPod model is: %s") % self.db.ipod_model albums = self.db.Albums() if self.verbose: print _("Photo Database contains %s albums") % len(albums[1:]) for album in albums[1:]: print _("- '%s', containing %s pictures") % (album.name, len(album.pics)) for x in range(len(albums)): if albums[x].name == self.album_name: self.album_id = x if self.album_id == 0: if self.verbose: sys.stdout.write(_("Photo album '%s' does not exist, creating it... ") % self.album_name) sys.stdout.flush() self.album_id = len(albums) self.db.AddAlbum(self.album_name) self.tosave = True if self.verbose: print _("created (n. %s).") % self.album_id if self.verbose: if self.fullres: print _("Full resolution image will be regularly copied") else: print _("Full resolution image will NOT be copied") if self.autorotate: autorotate_methods = {'CW':_('clockwise'), 'CCW':_('counter-clockwise')} print _("Detected portrait images will be automatically rotated %s") % autorotate_methods[self.autorotate] print _("Image uploading behaviour is: %s") % self.behaviour sys.stdout.write(_("Adding pictures to the photo album '%s'") % self.album_name) def Process(self): """ Process images addition """ if self.verbose: sys.stdout.write("\n") sys.stdout.write(_("Press Ctrl-c to stop adding images")) sys.stdout.flush() fls = expand_paths(self.filenames, self.recursive) x = 0 fls_len = len(fls) while x < fls_len: if not self.continue_processing: break if gtk.gdk.pixbuf_get_file_info(fls[x]): if not self.skipsync and fls[x] in self.sync: if self.verbose: sys.stdout.write(_("\nPhoto '%s' is already stored in Photo Database.") % fls[x]) else: if self.verbose: sys.stdout.write(_("\nAdding photo '%s'... ") % fls[x]) sys.stdout.flush() addp = Thread(target=self.db.AddPhoto, args=[fls[x], self.album_id, self.fullres, self.behaviour, self.autorotate]) addp.start() addp.join() #self.db.AddPhoto(fls[x], self.album_id, self.fullres, self.behaviour, self.autorotate) self.tosave = True if not self.skipsync: self.sync.append(fls[x]) if self.verbose: sys.stdout.write(_("added (%s of %s - %s%%).") % (x+1, len(fls), 100*(x+1)/len(fls))) x += 1 def Save(self): """ Save the Photo Database and terminate the process """ sys.stdout.write("\n") if self.tosave: if self.verbose: sys.stdout.write(_("Saving Photo Database... ")) sys.stdout.flush() self.db.progress = 0.0 thsave = Thread(target=self.db.Save) thsave.start() # Actually saving the new-generated Photo Database file if self.verbose: while thsave.isAlive(): progress = str(int(self.db.progress*100)).rjust(4) sys.stdout.write('\b\b\b\b\b%s%%' % progress) #time.sleep(0.01) print "\b\b\b\b%s." % _("saved") else: if self.verbose: print _("\nNo changes done. Nothing to save!") if not self.skipsync: if self.verbose: sys.stdout.write(_("Saving synchronization status... ")) syncfile = open(os.path.join(os.path.expanduser('~'), '.gpixpod', 'sync'), 'w+') cPickle.dump(set(self.sync), syncfile) syncfile.close() if self.verbose: print _("saved.") if self.verbose: print _("All done.") def Stop(self): """ Stop the process, saving at the reached point """ sys.stdout.write("\n%s" % _("Interruption requested. Please wait while saving at the current status...")) sys.stdout.flush() self.continue_processing = False def Kill(self, signal_no, stack): """ Kill the program saving the Photo Database """ self.Stop() def option_parse(): parser = optparse.OptionParser() parser.add_option('-i', '--ipod', dest='ipod', help=_('The mountpoint of your connected iPod')) parser.add_option('-m', '--model', dest='model', default=None, help=_('Manually provide iPod model (Nano|Photo|Color|5G) avoiding autodetection')) parser.add_option('-f', '--force', dest='force', action='store_true', help=_('Skip synchronization and duplicate checking')) parser.add_option('-q', '--quiet', dest='verbose', default=True, action='store_false', help=_('Do not display detailed output')) parser.add_option('-a', '--album', dest='album', default='New Photo Album', help=_('Name of the photo album to use. It will be created if absent.')) parser.add_option('-r', '--recursive', dest='recursive', action='store_true', help=_('Add images from directories recursively')) parser.add_option('-t', '--no-fullresolution', dest='fullres', default=True, action='store_false', help=_('Do not copy full resolution image')) parser.add_option('-z', '--zoom', dest='zoom', action='store_true', help=_('Zoom images instead of fit')) parser.add_option('-s', '--stretch', dest='stretch', action='store_true', help=_('Stretch images instead of fit')) parser.add_option('-j', '--rotate-cw', dest='rotatecw', action='store_true', help=_('Rotate automatically portrait images clockwise')) parser.add_option('-k', '--rotate-ccw', dest='rotateccw', action='store_true', help=_('Rotate automatically portrait images counter-clockwise')) opts, args = parser.parse_args() if opts.ipod and os.path.isdir(opts.ipod): behaviour = 'Fit' if opts.stretch: behaviour = 'Stretch' elif opts.zoom: behaviour = 'Zoom' autorotate = None if opts.rotatecw: autorotate = 'CW' elif opts.rotateccw: autorotate = 'CCW' gpixpodcli = GPixPodCLI(opts.verbose, opts.ipod, opts.model, opts.album, opts.recursive, opts.fullres, behaviour, autorotate, opts.force, args) signal.signal(signal.SIGTERM, gpixpodcli.Kill) gpixpodcli.Open() try: gpixpodcli.Process() except KeyboardInterrupt: gpixpodcli.Stop() gpixpodcli.Save() else: print _("Invalid iPod mountpoint") print parser.get_usage() if __name__ == '__main__': option_parse() gpixpod-0.6.2/ipodhal.py0000644000175000017500000000756610507301076014452 0ustar flagarflagar# GPixPod - the free and open source way to manage photos on your POD! # Copyright (C) 2006 Flavio Gargiulo (FLAGAR.com) # # This file has been contributed by Abaakouk Mehdi. # # 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. # # 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. import dbus import dbus.glib import gnomevfs import gobject class iPodHal(gobject.GObject): __gsignals__ = { "ipod-removed" : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_STRING,)) , "ipod-added" : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_STRING,)) } def __init__(self): self.verbose = False gobject.GObject.__init__(self) self.ipod_udi = [] def start_monitor(self): self.bus = dbus.SystemBus() self.vol_monitor = gnomevfs.VolumeMonitor() self.vol_monitor.connect("volume_mounted",self.__volume_mounted_cb) self.vol_monitor.connect("volume_unmounted",self.__volume_unmounted_cb) volume = gnomevfs.Volume() for volume in self.vol_monitor.get_mounted_volumes(): device_udi = volume.get_hal_udi() if device_udi!=None: device_dbus_obj = self.bus.get_object("org.freedesktop.Hal" ,device_udi) device = device_dbus_obj.GetAllProperties(dbus_interface="org.freedesktop.Hal.Device") if self.is_ipod(device): self.ipod_udi.append(device_udi) self.emit("ipod-added",device_udi) if self.verbose: print "IPOD found: ",device_udi def __volume_mounted_cb(self,monitor,volume): device_udi = volume.get_hal_udi() device_dbus_obj = self.bus.get_object("org.freedesktop.Hal" ,device_udi) device = device_dbus_obj.GetAllProperties(dbus_interface="org.freedesktop.Hal.Device") if self.is_ipod(device): self.ipod_udi.append(device_udi) self.emit("ipod-added",device_udi) def __volume_unmounted_cb(self,monitor,volume): device_udi = volume.get_hal_udi() device_dbus_obj = self.bus.get_object("org.freedesktop.Hal" ,device_udi) device = device_dbus_obj.GetAllProperties(dbus_interface="org.freedesktop.Hal.Device") if device_udi in self.ipod_udi: self.ipod_udi.remove(device_udi) self.emit("ipod-removed",device_udi) def get_ipod_mount_point(self,device_udi): device_dbus_obj = self.bus.get_object("org.freedesktop.Hal" ,device_udi) device = device_dbus_obj.GetAllProperties(dbus_interface="org.freedesktop.Hal.Device") if device.has_key("volume.is_mounted") and device["volume.is_mounted"] and\ device.has_key("volume.mount_point") and device["volume.mount_point"]!="": return device["volume.mount_point"] else: return "" def is_ipod(self,device): if device.has_key("info.parent"): device_dbus_obj = self.bus.get_object("org.freedesktop.Hal" , device["info.parent"]) device = device_dbus_obj.GetAllProperties(dbus_interface="org.freedesktop.Hal.Device") if device.has_key("storage.model") and device["storage.model"]=="iPod": return True return False gpixpod-0.6.2/mh.py0000755000175000017500000014377310507301076013442 0ustar flagarflagar#!/usr/bin/env python # GPixPod - organize photos on your iPod, freely! # Copyright (C) 2006 Flavio Gargiulo (FLAGAR.com) # # 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. # # 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. import os, sys, shutil, time, cPickle from utils import * from imgconvert import * from struct import * from zlib import adler32 # Seems the fastest checksum method from threading import Thread # Using threads when generating thumbs class MH: """ Process the MH database """ formats = {'mhfd':'<4s16I64x', 'mhsd':'<4s3I80x', 'mhli':'<4s2I80x', 'mhii':'<4s12I100x', 'mhod':'<4s2I1H2B8x', 'mhni':'<4s6I2h2H40x', 'mhla':'<4s2I80x', 'mhba':'<4s15I84x', 'mhia':'<4s4I20x', 'mhlf':'<4s2I80x', 'mhif':'<4s5I100x'} lengths = dict(map(lambda(x): (x[0], calcsize(x[1])), formats.items())) latest = {'mhfd':0, 'mhsd':0, 'mhli':0, 'mhii':0, 'mhoc':0, 'mhni':0, 'mhod':0, 'mhos':0, 'mhla':0, 'mhba':0, 'mhia':0, 'mhlf':0, 'mhif':0} parents = {'mhfd':'mhfd', 'mhsd':'mhfd', 'mhli':'mhsd', 'mhii':'mhli', 'mhoe':'mhii', 'mhoc':'mhii', 'mhni':'mhoc', 'mhod':'mhni', 'mhos':'mhod', 'mhla':'mhsd', 'mhba':'mhla', 'mhob':'mhba', 'mhoa':'mhob', 'mhia':'mhba', 'mhlf':'mhsd', 'mhif':'mhlf'} # MHOE: container MHOD for full res.; MHOC: container MHOD for thumb; MHOD: string MHOD type-3; MHOS: string MHOD type-3 subheader; # MHOB: string MHOD type-1; MHOA: string MHOD type-1 subheader. #thumbdbsizes = ((712, 480), (320, 240), (123, 88), (53, 41)) MOVED TO __INIT__ #thumbsizes = ((720, 480), (320, 240), (130, 88), (50, 41)) MOVED TO __INIT__ #thumbdims = tuple(map(lambda(x): x[0]*x[1]*2, thumbsizes)) MOVED TO __INIT__ #thumbfnames = (1019, 1024, 1015, 1036) # Default thumbnails filename parts removed_photos_ids = [] new_photos_added = [] thumbs_extracted = False progress = 0.0 def __init__(self, dir='/mnt/ipod/Photos/Photo Database', sglthumbsdir=None, opening=True, ipodmodel=None): """ Initial setup for the Photo Database file """ # Figuring out the full filename and main path if dir.find('Photo Database') == -1: self.dbname = os.path.join(dir, 'Photo Database') self.dbdir = dir else: self.dbname = dir self.dbdir = dir.replace('Photo Database', '') # Single thumbnails dir if sglthumbsdir == None: self.sglthumbsdir = os.path.join(self.dbdir, '.SingleThumbs') else: self.sglthumbsdir = sglthumbsdir # Configuration files: thumbnails checksum pickle file self.thumbschecksum = os.path.join(os.path.expanduser('~'), '.gpixpod', 'checksum') # Detecting iPod model and setting thumbnail infos if ipodmodel in ('5G', 'Photo', 'Color', 'Nano'): self.ipod_model = ipodmodel else: self.ipod_model = getIpodModel(self.dbdir.replace('Photos', '')) if self.ipod_model == '5G': # Supported for sure: developer's one self.thumbdbsizes = ((712, 480), (320, 240), (123, 88), (53, 41)) self.thumbsizes = ((720, 480), (320, 240), (130, 88), (50, 41)) self.thumbfnames = (1019, 1024, 1015, 1036) elif self.ipod_model == 'Photo': # Tested and verified self.thumbdbsizes = ((712, 452), (235, 146), (130, 87), (43, 30)) self.thumbsizes = ((720, 480), (220, 176), (130, 88), (42, 30)) self.thumbfnames = (1019, 1013, 1015, 1009) elif self.ipod_model == 'Color': # Guessed (same as Photo?) self.thumbdbsizes = ((712, 452), (235, 146), (130, 87), (43, 30)) self.thumbsizes = ((720, 480), (220, 176), (130, 88), (42, 30)) self.thumbfnames = (1019, 1013, 1015, 1009) elif self.ipod_model == 'Nano': # Tested and verified self.thumbdbsizes = ((176, 132), (45, 37)) self.thumbsizes = ((176, 132), (42, 37)) self.thumbfnames = (1023, 1032) else: # Using iPod 5G values as default for other, unsupported models. The GUI will warn. self.thumbdbsizes = ((712, 480), (320, 240), (123, 88), (53, 41)) self.thumbsizes = ((720, 480), (320, 240), (130, 88), (50, 41)) self.thumbfnames = (1019, 1024, 1015, 1036) self.thumbdims = tuple(map(lambda(x): x[0]*x[1]*2, self.thumbsizes)) if opening: self.Open() def Open(self): """ Actually open and process the Photo Database file """ self.dbcreatedfromscratch = False try: self.db = open(self.dbname, 'rb') except IOError: print "Directory does not contain a Photo Database file" print "A new one will be created based on the path specified" # FromScratch opening should be modelled better as in exceptions as in questions before overwriting self._FromScratch() self.dbcreatedfromscratch = True if not os.path.isdir(self.dbdir): os.makedirs(self.dbdir) # Creating thumbnails files if they don't exist yet if not os.path.isdir(os.path.join(self.dbdir, 'Thumbs')): os.mkdir(os.path.join(self.dbdir, 'Thumbs')) for x in self.thumbfnames: t = open(os.path.join(self.dbdir, 'Thumbs', 'F%s_1.ithmb' % x), 'ab') t.close() self._SaveDB() self.db = open(self.dbname, 'rb') self._FirstScan() # If creating from scratch, copying full resolution images by default if self.dbcreatedfromscratch: self.dbcopyfullres = True if not self._ThumbsChecksumVerify(): print "Thumbnails cache checksum failed. Extracting from *.ithumb files." self._ThumbsExtract() self.WriteDatabaseLog() # Enfast saving, avoiding rebuilding thumbs if there are no deletions self.PX_ADDED = [] self.PX_REMOVED = [] def _PhotoCreationDate(self, filename): """ Try to get the date of the image from the EXIF headers, otherwise from the file """ try: import mmpython.image.EXIF f = open(filename, 'rb') tags = mmpython.image.EXIF.process_file(f) f.close() # e.g. 'EXIF DateTimeOriginal' = '2004:12:15 16:49:49' if tags.has_key('EXIF DateTimeOriginal'): return time.strptime(tags['EXIF DateTimeOriginal'].printable, "%Y:%m:%d %H:%M:%S") except ImportError, e: pass # Use the content modification time st = os.stat(filename) return time.localtime(st.st_mtime) def _FromScratchApply(self): """ Annul already opened Photo Database applying from scratch method """ self._FromScratch() self.dbcreatedfromscratch = True # Creating thumbnails files if they don't exist yet if not os.path.isdir(os.path.join(self.dbdir, 'Thumbs')): os.mkdir(os.path.join(self.dbdir, 'Thumbs')) for x in self.thumbfnames: t = open(os.path.join(self.dbdir, 'Thumbs', 'F%s_1.ithmb' % x), 'ab') t.close() self._SaveDB() self.db = open(self.dbname, 'rb') self._FirstScan() self.dbcopyfullres = True def _FromScratch(self): """ Define a new self.dblines from scratch to generate a new empty database file """ # Template for an iPod video 5G self.dblines = [] self.dblines.append(['mhfd', ['mhfd', 132, 1252, 0, 1, 3, 0, 100, 0, 0, 0, 0, 2, 0, 0, 0, 0], 0]) self.dblines.append(['mhsd', ['mhsd', 96, 188, 1], 0]) self.dblines.append(['mhli', ['mhli', 92, 0], 1]) self.dblines.append(['mhsd', ['mhsd', 96, 380, 2], 0]) self.dblines.append(['mhla', ['mhla', 92, 1], 3]) self.dblines.append(['mhba', ['mhba', 148, 192, 1, 0, 100, 0, 65536, 0, 0, 0, 0, 0, 0, 0, 100], 4]) self.dblines.append(['mhob', ['mhod', 24, 44, 1, 0, 1], 5]) self.dblines.append(['mhoa', [7, 1, 0, 'Library'.encode('utf-8')], 6]) self.dblines.append(['mhsd', ['mhsd', 96, 684, 3], 0]) self.dblines.append(['mhlf', ['mhlf', 92, 4], 5]) self.dblines.append(['mhif', ['mhif', 124, 124, 0, 1019, 691200], 6]) self.dblines.append(['mhif', ['mhif', 124, 124, 0, 1015, 22880], 6]) self.dblines.append(['mhif', ['mhif', 124, 124, 0, 1024, 153600], 6]) self.dblines.append(['mhif', ['mhif', 124, 124, 0, 1036, 4100], 6]) # Modifying template for iPod Photo/Color if self.ipod_model == 'Photo' or self.ipod_model == 'Color': self.dblines[-1][1][5] = 2520 # Modifying dimension of the 42x30 thumbnail self.dblines[-2][1][5] = 77440 # Modifying dimension of the 220x176 thumbnail # Updating thumbnails filenames parts self.dblines[-4][1][4] = self.thumbfnames[0] self.dblines[-3][1][4] = self.thumbfnames[2] self.dblines[-2][1][4] = self.thumbfnames[1] self.dblines[-1][1][4] = self.thumbfnames[3] # Modifying template for iPod Nano elif self.ipod_model == 'Nano': del self.dblines[-4:-2] # deleting missing thumbnails types # Updating thumbnails filename parts self.dblines[-2][1][4] = self.thumbfnames[0] self.dblines[-1][1][4] = self.thumbfnames[1] self.dblines[-1][1][5] = 3108 # Modifying dimension of the 42x37 thumbnail self.dblines[-2][1][5] = 46464 # Modifying dimension of the 176x132 thumbnail self.dblines[-3][1][2] = 2 # Modifying MHLF indicating only 2 thumbnails types self.dblines[-4][1][2] -= 248 # Updating parent MHSD total length, after having deleted 2 MHIF self.dblines[0][1][2] -= 248 # Updating MHFD, too def _FirstScan(self): """ Scan the database file """ self.secondscan = False # Manage different format Photo Database probably generated by iPhoto self.restartfromscratch = False # Restart from scratch if the Photo Database empty format generated by iTunes is in an odd status self.db.seek(0, 2); dbsize = self.db.tell() self.db.seek(0); self.dblines = [] self.dbcopyfullres = False while self.db.tell() < dbsize: dbline = unpack('<4s', self.db.read(4))[0] self.db.seek(-4, 1) self.dblines.append([dbline, list(unpack(self.formats[dbline], self.db.read(self.lengths[dbline]))), self.latest[self.parents[dbline]]]) if dbline == 'mhlf': # We really need to already have the MHIFs. Otherwise we get a crash. if self.dblines[-1][1][2] == 0 and self.dblines[2][0] == 'mhli' and self.dblines[2][1][2] == 0: # No MHIFs, no images self.restartfromscratch = True # Thus it is better to restart from scratch. elif dbline == 'mhod': mhod_type = self.dblines[-1][1][3] if mhod_type == 5: self.dblines[-1][0] = 'mhoe' self.dbcopyfullres = True elif mhod_type == 2: if self.dblines[-4][0] == 'mhba': # Then we are handling one of those recent iPod Nanoes with different Photo Database format, with a second string MHOD in photo albums MHBAs # after the first string MHOD of its name, probably stored by iPhoto and containing a transition effect ignored by the iPod, and then ignored # (deleted) also here, as documented in the iPodLinux.org/iTunesDB page #mhoflength = self.dblines[-1][1][2]-24 # Got the length of this second string MHOD, we subtract its total length from its parents MHBA and MHSD #self.dblines[self.latest['mhba']][1][2] -= mhoflength #self.dblines[self.latest['mhba']][1][2] -= 24 #self.dblines[self.latest['mhsd']][1][2] -= mhoflength #self.dblines[self.latest['mhsd']][1][2] -= 24 #del self.dblines[-1] #self.db.read(mhoflength) self.dblines[-1][0] = 'mhob' mhodsubname = 'mhoa' self.secondscan = True else: self.dblines[-1][0] = 'mhoc' elif mhod_type == 1: self.dblines[-1][0] = 'mhob' mhodsubname = 'mhoa' elif mhod_type == 3: mhodsubname = 'mhos' mhod_name = self.dblines[-1][0] self.dblines[-1][2] = self.latest[self.parents[mhod_name]] self.latest[mhod_name] = len(self.dblines)-1 if mhod_name == 'mhod' or mhod_name == 'mhob' or mhod_name == 'mhog': # string MHOD mhodslen = unpack('> logfile, dbline logfile.close() def _SaveDB(self, filename=None): """ Write all the changes back to the database file """ if filename == None: filename = self.dbname if filename == self.dbname and os.path.isfile(filename): # if saving to the same file, creating backup backup_filename = os.path.join(self.dbdir, 'Photo Database.gpixpodbak') if os.path.isfile(backup_filename): os.remove(backup_filename) os.rename(self.dbname, backup_filename) self._PrepareToSave() db = open(filename, 'w+b') try: for x in range(0, len(self.dblines)): if self.dblines[x][0] == 'mhos' or self.dblines[x][0] == 'mhoa': db.write(pack('<3I%ss%sx' % (self.dblines[x][1][0], self.dblines[x-1][1][5]), *self.dblines[x][1])) elif self.dblines[x][0] == 'mhoe' or self.dblines[x][0] == 'mhoc' or self.dblines[x][0] == 'mhob': db.write(pack(self.formats['mhod'], *self.dblines[x][1])) else: db.write(pack(self.formats[self.dblines[x][0]], *self.dblines[x][1])) except error: print x, self.dblines[x][0] db.close() def Save(self, filename=None): """ Save all """ self._UpdateThumbsOffsets() self._ThumbsRebuild() self._UpdateThumbsIDs() self._ThumbsChecksumStore() self._SaveDB(filename) def Albums(self): """ Get current photo albums """ result = [] for x in range(0, len(self.dblines)): if self.dblines[x][0] == 'mhba': albumpics = [] picsn = self.dblines[x][1][4] for y in range(0, picsn): albumpics.append(self.dblines[x+3+y][1][4]) album = Album(self.dblines[x+2][1][3].decode("utf-8"), albumpics) result.append(album) return result def Photos(self): """ Get current photos details """ result = [] for x in range(0, len(self.dblines)): if self.dblines[x][0] == 'mhii': if self.dblines[x][1][3] == 5 or self.dblines[x][1][3] == 3: # Photo has a full resolution copy. fullres = self.dblines[x+4][1][3].decode("utf-16-le") else: fullres = None photo = Photo(self.dblines[x][1][4], fullres) result.append(photo) return result def AddAlbum(self, name): """ Add a new album """ namelen = len(name) namepad = getPadding(namelen) mhob_totlen = 24+12+namelen+namepad for x in range(0, len(self.dblines)): if self.dblines[x][0] == 'mhla': self.dblines[x][1][2] += 1 # Incrementing number of albums self.dblines[x-1][1][2] += 148+mhob_totlen # Incrementing total length of parent MHSD mhla = x if self.dblines[x][0] == 'mhba': last_mhba = x # Getting the latest album list offset: needed to generate new IDs self.dblines[0][1][7] += 1 # Incrementing next ID field in MHFD # If iPod Nano, assuming 4 elements at the end of the list: MHSD, MHLF and 2 MHIF if self.ipod_model == 'Nano': afteralb = 4 # Else assuming 6 elements at the end of the list: MHSD, MHLF and 4 MHIF else: afteralb = 6 self.dblines.insert(len(self.dblines)-afteralb, ['mhba', ['mhba', 148, 148+mhob_totlen, 1, 0, (self.dblines[last_mhba][1][5]+1), 0, 393216, 0, 0, 0, 0, 0, 0, 0, \ (self.dblines[last_mhba][1][15]+self.dblines[last_mhba][1][4]+1)], mhla]) self.dblines.insert(len(self.dblines)-afteralb, ['mhob', ['mhod', 24, mhob_totlen, 1, 0, namepad], len(self.dblines)-6]) self.dblines.insert(len(self.dblines)-afteralb, ['mhoa', [namelen, 1, 0, name], len(self.dblines)-6]) def _GetMHNIs(self, mhii): """ Get MHNI offsets for the specified MHII and for the current iPod model """ mhii_children = self.dblines[mhii][1][3] if mhii_children == 5: return (mhii+6, mhii+10, mhii+14, mhii+18) elif mhii_children == 4: return (mhii+2, mhii+6, mhii+10, mhii+14) elif mhii_children == 3: return (mhii+6, mhii+10) elif mhii_children == 2: return (mhii+2, mhii+6) # Maybe we should use iPod 5G MHNI offsets as default in 'else'? def _HasFullResolution(self, mhii): """ Return True if the photo of the specified MHII has actually a full resolution image attached """ # Do it checking children if self.dblines[mhii][1][3] == 5 or self.dblines[mhii][1][3] == 3: return True elif self.dblines[mhii][1][3] == 4 or self.dblines[mhii][1][3] == 2: return False # Raise exception with 'else'? def HasFullResolution(self, photo_id): """ Return True if the photo with the specified ID has actually a full resolution image attached """ for x in range(0, len(self.dblines)): if self.dblines[x][0] == 'mhii' and self.dblines[x][1][4] == photo_id: return self._HasFullResolution(x) break def _ThumbsFilenames(self): """ Get thumbs filenames """ j = os.path.join photos = [str(x[1][4]) for x in self.dblines if x[0] == 'mhii'] return [j(self.sglthumbsdir, '%sx%s' % (x[0], x[1]), p) for x in self.thumbsizes for p in photos] def _ThumbsChecksum(self): """ Create a checksum for all single thumbs extracted """ photos = self._ThumbsFilenames() chksum = [] for ph in photos: #pf = open(ph) #chksum.append((ph, adler32(pf.read()))) #pf.close() chksum.append((ph, os.stat(ph)[8])) return dict(chksum) def _ThumbsChecksumStore(self): """ Store a checksum for all single thumbs extracted """ fileWrite(self.thumbschecksum, cPickle.dumps(self._ThumbsChecksum()), 'w+') def _ThumbsChecksumVerify(self): """ Verify the single thumbs against previously created checksums """ try: stored_chksumf = open(self.thumbschecksum) stored_chksum = cPickle.load(stored_chksumf) stored_chksumf.close() chksum = self._ThumbsChecksum() result = True self.progress = pgrs = 0 totnum = len(stored_chksum)*1.0 for t, n in stored_chksum.iteritems(): pgrs += 1 self.progress = pgrs/totnum if chksum[t] != n: print chksum[t] print n result = False break self.progress = 1.0 return result except: self.progress = 1.0 return False def _ThumbsExtract(self): """ Extract all the thumbs in a specified hidden directory with a subdirectory per size format """ j = os.path.join tdirs = [j(self.sglthumbsdir, '%sx%s' % (x[0], x[1])) for x in self.thumbsizes] [os.makedirs(x) for x in tdirs if not os.path.isdir(x)] pics = [str(x[1][4]) for x in self.dblines if x[0] == 'mhii'] ithumbs = [open(j(self.dbdir, 'Thumbs', 'F%s_1.ithmb' % self.thumbfnames[x]), 'rb') for x in self.thumbdims] #[[fileWrite(j(d, x), t.read(s)) for d, t, s in zip(tdirs, ithumbs, self.thumbdims)] for x in pics] totnum = len(tdirs)*len(pics)*1.0 self.progress = pgrs = 0 for x in pics: for d, t, s in zip(tdirs, ithumbs, self.thumbdims): f = open(j(d, x), 'w+b') f.write(t.read(s)) f.close() pgrs += 1 self.progress = pgrs/totnum [x.close() for x in ithumbs] self.thumbs_extracted = True self._ThumbsChecksumStore() self.progress = 1.0 def _ThumbsExtractChecking(self): # Checking should not be needed, so _ThumbsExtract should be normally the one to use """ Extract all the thumbs in a specified hidden directory with a subdirectory per size format, checking database offset and size values """ thumb_dirs = [] thumbdims = list(self.thumbdims) thumbsizes_len = len(self.thumbsizes) for t in range(0, thumbsizes_len): thumb_dirs.append(os.path.join(self.sglthumbsdir, '%sx%s' % (self.thumbsizes[t][0], self.thumbsizes[t][1]))) if not os.path.isdir(thumb_dirs[-1]): os.makedirs(thumb_dirs[-1]) for x in range(0, len(self.dblines)): if self.dblines[x][0] == 'mhii': current_id = self.dblines[x][1][4] for y in self._GetMHNIs(x): d = thumbdims.index(self.dblines[y][1][6]) thumbs = open(os.path.join(self.dbdir, 'Thumbs', 'F%s_1.ithmb' % self.thumbfnames[self.dblines[y][1][6]]), 'rb') thumb = open(os.path.join(thumb_dirs[d], str(current_id)), 'w+b') thumbs.seek(self.dblines[y][1][5]) thumb.write(thumbs.read(self.dblines[y][1][6])) thumb.close() thumbs.close() self.thumbs_extracted = True def _ThumbsRebuild(self): """ Rebuild the thumbnails file from the single thumbs previously extracted """ maindir = self.sglthumbsdir if len(self.PX_REMOVED) > 0: thumbsopenmode = 'w+b' else: thumbsopenmode = 'ab' thumbs = map(lambda(y): open(y, thumbsopenmode), map(lambda(x): os.path.join(self.dbdir, 'Thumbs', 'F%s_1.ithmb' % self.thumbfnames[x]), self.thumbdims)) tdirs = map(lambda(t): os.path.join(maindir, '%sx%s' % (t[0], t[1])), self.thumbsizes) if len(self.PX_REMOVED) > 0: tfiles = map(sorted, map(os.listdir, tdirs)) else: tfiles = [map(str, self.PX_ADDED)]*len(tdirs) #totnum = len(tfiles)*len(tfiles[0])*1.0 tfileslen = len(tfiles[0]) totnum = sum([x*tfileslen for x in self.thumbdims])*1.0 self.progress = pgrs = 0.0 for thmb, tdir, tf in zip(thumbs, tdirs, tfiles): tf = map(lambda(f): os.path.join(tdir, f), tf) #map(lambda(k): thmb.write(k.read()), tf) for kf in tf: k = open(kf, 'rb') thmb.write(k.read()) pgrs += k.tell() self.progress = pgrs/totnum k.close() thmb.close() #self.thumbs_extracted = False #self._ThumbsChecksumStore() self.progress = 1.0 # #for t in range(0, len(self.thumbsizes)): # tdir = os.path.join(maindir, '%sx%s' % (self.thumbsizes[t][0], self.thumbsizes[t][1])) # tdirlist = os.listdir(tdir) # tdirlist.sort() # thumbs = open(os.path.join(self.dbdir, 'Thumbs', 'F%s_1.ithmb' % self.thumbfnames[self.thumbdims[t]]), 'w+b') # for f in tdirlist: # thumbname = os.path.join(tdir, f) # thumb = open(thumbname, 'rb') # thumbs.write(thumb.read()) # thumb.close() # #os.remove(thumbname) # thumbs.close() # #os.rmdir(tdir) #os.rmdir(maindir) #self.thumbs_extracted = False def _UpdateThumbsOffsets(self): """ Update the offsets for each thumbnail in MHNI definitions for all photos """ thumboffsets = [0, 0, 0, 0] thumbdims = list(self.thumbdims) thumbsizes_len = len(self.thumbsizes) for x in range(0, len(self.dblines)): if self.dblines[x][0] == 'mhii': for y in self._GetMHNIs(x): try: d = thumbdims.index(self.dblines[y][1][6]) self.dblines[y][1][5] = thumboffsets[d] thumboffsets[d] += self.dblines[y][1][6] except IndexError: for line in self.dblines: print line raise "Index Error: %s %s" % (y, self.dblines[y]) def RemoveThumbsCache(self, thumbsdir=None): """ Remove the cache of the single thumbnails """ try: if thumbsdir == None: maindir = self.sglthumbsdir else: maindir = thumbsdir if os.path.isdir(maindir): for t in self.thumbsizes: td = os.path.join(maindir, '%sx%s' % (t[0], t[1])) if os.path.isdir(td): for tf in os.listdir(td): tfn = os.path.join(td, tf) if os.path.isfile(tfn): os.remove(tfn) os.rmdir(td) os.rmdir(maindir) except: pass def _RemovePhotoThumbs(self, photoid): """ Remove photo thumbs """ for t in self.thumbsizes: thumbdir = os.path.join(self.sglthumbsdir, '%sx%s' % (t[0], t[1])) os.remove(os.path.join(thumbdir, str(photoid))) def _RemovePhotoFromList(self, photoid): """ Remove a photo from the photos list (MHLI) """ for x in range(0, len(self.dblines)): if self.dblines[x][0] == 'mhii': if self.dblines[x][1][4] == photoid: photo = x elif self.dblines[x][1][4] > photoid: #self.dblines[x][1][4] -= 1 # Adjusting IDs for next photos MOVED TO NEW FUNCTION: it CREATED problems when removing!!! self.dblines[x][1][5] -= 1 # for t in (x+6, x+10, x+14, x+18): # self.dblines[t][1][5] -= self.dblines[t][1][6] # Adjusting thumbnails offsets for next images if self.dblines[x][0] == 'mhba': self.dblines[x][1][5] -= 1 # Related decrement of subsequent album IDs self.dblines[1][1][2] -= self.dblines[photo][1][2] # Decrementing total length of parent MHSD self.dblines[2][1][2] -= 1 # Decrementing total number of pictures self.dblines[0][1][7] -= 1 # Decrementing next ID field in MHFD # Removing thumbnails self._RemovePhotoThumbs(photoid) self.PX_REMOVED.append(photoid) photo_fullres = 0 photo_children = self.dblines[photo][1][3] if self._HasFullResolution(photo): # Then deleting the full resolution image self._DeleteFullResolution(self.dblines[photo+4][1][3]) del self.dblines[photo:photo+(4*photo_children+1)] def _AddPhotoThumbs(self, filename, photoid, behaviour='Fit', autorotate=None): """ Add photo thumbs """ thmbths = [] thumbsizes_len = len(self.thumbsizes) for t in range(0, thumbsizes_len): thmbth = Thread(target=self._AddPhotoThumb, args=[filename, photoid, t, behaviour, autorotate]) thmbth.start() thmbths.append(thmbth) for thmbth in thmbths: thmbth.join() def _AddPhotoThumb(self, filename, photoid, t, behaviour='Fit', autorotate=None): """ Add photo thumb """ if behaviour == 'Zoom': behaviourn = 1; elif behaviour == 'Stretch': behaviourn = 2; else: behaviourn = 0; if autorotate == 'CW': autorotaten = 1; elif autorotate == 'CCW': autorotaten = 2; else: autorotaten = 0; thumbdn = os.path.join(self.sglthumbsdir, '%sx%s' % (self.thumbsizes[t][0], self.thumbsizes[t][1])) thumbfn = os.path.join(thumbdn, str(photoid)) if not os.path.isdir(thumbdn): os.makedirs(thumbdn) if t == 0 and len(self.thumbsizes) != 2: # then is not TV photo neither iPod Nano toInterlacedUYVY(filename, behaviourn, autorotaten, thumbfn) else: if t == 0 and self.ipod_model == 'Nano': toRGB565(filename, self.thumbsizes[t][0], self.thumbsizes[t][1], False, False, behaviourn, autorotaten, thumbfn) elif t == 1 and (self.ipod_model == 'Photo' or self.ipod_model == 'Color'): toRGB565(filename, self.thumbsizes[t][0], self.thumbsizes[t][1], True, True, behaviourn, autorotaten, thumbfn) else: toRGB565(filename, self.thumbsizes[t][0], self.thumbsizes[t][1], True, False, behaviourn, autorotaten, thumbfn) def _AddPhotoToList(self, filename, photofullres=None, behaviour='Fit', autorotate=None): """ Add a new photo to the photos list (MHLI) """ if photofullres == None: # Then using DB global default photofullres = self.dbcopyfullres name = os.path.basename(filename) # check maximum filename string length FIXME picdate = self._PhotoCreationDate(filename) albums = 0 last_mhii = None for x in range(0, len(self.dblines)): if self.dblines[x][0] == 'mhii': last_mhii = x if self.dblines[x][0] == 'mhba': self.dblines[x][1][5] += 1 # Related increment of subsequent album IDs albums += 1 mhli = 2 if last_mhii == None: mhpos = mhii = 3 photo_id = 100 else: if self._HasFullResolution(last_mhii): last_mhii_fullres = 4 else: last_mhii_fullres = 0 mhpos = mhii = last_mhii+last_mhii_fullres+4*len(self.thumbsizes)+1 photo_id = self.dblines[last_mhii][1][4]+1 # Copying full resolution image, if wanted if photofullres: self._CopyFullResolution(filename) fullresdb = (':Full Resolution:%s:%s:%s:%s' % (picdate[0], picdate[1], picdate[2], name)).encode('utf-16-le') fullresdb_len = len(fullresdb) fullresdb_pad = getPadding(fullresdb_len) fres = fullresdb_len+fullresdb_pad totfres = 136+fres freschild = 1 else: totfres = 0 freschild = 0 # Incrementing total length of parent MHSD self.dblines[1][1][2] += 152+(len(self.thumbdbsizes)*180)+totfres # Generating thumbnails self._AddPhotoThumbs(filename, photo_id, behaviour, autorotate) self.PX_ADDED.append(photo_id) # Photo header (MHII) self.dblines.insert(mhpos, ['mhii', ['mhii', 152, 152+(len(self.thumbdbsizes)*180)+totfres, (freschild+len(self.thumbdbsizes)), photo_id, photo_id+albums, 0, 0, 0, 0, appleTime(picdate), appleTime(picdate), 0], mhli]) self.dblines[mhli][1][2] +=1 # Total number of pictures self.dblines[0][1][7] += 1 # Incrementing next ID field in MHFD # Inserting full resolution database details, if wanted if photofullres: self.dblines.insert(mhpos+1, ['mhoe', ['mhod', 24, 136+fres, 5, 0, 0], mhpos]); mhpos+=1 self.dblines.insert(mhpos+1, ['mhni', ['mhni', 76, 112+fres, 1, 1, 0, 0, 0, 0, 0, 0], mhpos]); mhpos+=1 self.dblines.insert(mhpos+1, ['mhod', ['mhod', 24, 36+fres, 3, 0, fullresdb_pad], mhpos]); mhpos+=1 self.dblines.insert(mhpos+1, ['mhos', [fullresdb_len, 2, 0, fullresdb], mhpos]) mhpos+=1 # Thumbs for thumb in range(0, len(self.thumbdbsizes)): if last_mhii == None: offset = 0 else: offset = self.dblines[last_mhii+last_mhii_fullres+2+4*thumb][1][5]+self.thumbdims[thumb] self.dblines.insert(mhpos+1, ['mhoc', ['mhod', 24, 180, 2, 0, 0], mhii]); mhpos+=1 self.dblines.insert(mhpos+1, ['mhni', ['mhni', 76, 156, 1, self.thumbfnames[self.thumbdims[thumb]], offset, self.thumbdims[thumb], 0, 0, self.thumbdbsizes[thumb][1], self.thumbdbsizes[thumb][0]], mhpos]); mhpos+=1 self.dblines.insert(mhpos+1, ['mhod', ['mhod', 24, 80, 3, 0, 2], mhpos]); mhpos+=1 self.dblines.insert(mhpos+1, ['mhos', [42, 2, 0, (':Thumbs:F%s_1.ithmb' % self.thumbfnames[self.thumbdims[thumb]]).encode('utf-16-le')], mhpos]); mhpos+=1 return photo_id def _CopyFullResolution(self, filename): """ Copy the full resolution photo to destination """ name = os.path.basename(filename) picdate = self._PhotoCreationDate(filename) dest_dir = os.path.join(self.dbdir, 'Full Resolution', *map(str, picdate[:3])) if not os.path.isdir(dest_dir): os.makedirs(dest_dir) shutil.copyfile(filename, os.path.join(dest_dir, name)) def _DeleteFullResolution(self, fullres_path): """ Delete the full resolution photo from path (Mac, relative) specified """ #filename = fullres_path.decode('utf-16-le') filename = fullres_path.decode('utf-16-le') filename = filename.encode('utf-8') filename = os.path.join(self.dbdir, *filename.split(':')[1:]) try: if os.path.isfile(filename): os.remove(filename) except OSError: print 'Problems removing %s.' % filename def AddPhotoToAlbum(self, photo_id, album_id=0): # Default: adding to library """ Add a photo to an album """ counter = 0 for x in range(0, len(self.dblines)): if self.dblines[x][0] == 'mhla': self.dblines[x-1][1][2] += 40 # Incrementing total length of parent MHSD if self.dblines[x][0] == 'mhba': if counter == album_id: mhba = x break counter+=1 self.dblines[mhba][1][2] += 40 # Incrementing total length of parent album self.dblines[mhba][1][4] += 1 # Incrementing number of pictures for parent album self.dblines.insert(mhba+2+self.dblines[mhba][1][4], ['mhia', ['mhia', 40, 40, 0, photo_id], mhba]) def RemovePhotoFromAlbum(self, photo_id, album_id): """ Remove a photo reference from a specified photo album """ mhbas = 0 for x in range(0, len(self.dblines)): if self.dblines[x][0] == 'mhla': mhla = x if self.dblines[x][0] == 'mhba': mhba = x mhbas += 1 if self.dblines[x][0] == 'mhia': if self.dblines[x][1][4] == photo_id and album_id+1 == mhbas: self.dblines[mhla-1][1][2] -= 40 self.dblines[mhba][1][4] -= 1 self.dblines[mhba][1][2] -= 40 del self.dblines[x] break def _RemovePhotoFromAlbums(self, photo_id): """ Remove a photo from all the albums which link to it """ photos_to_remove = [] for x in range(0, len(self.dblines)): if self.dblines[x][0] == 'mhla': mhla = x if self.dblines[x][0] == 'mhba': current_mhba = x if self.dblines[x][0] == 'mhia': if self.dblines[x][1][4] == photo_id: photos_to_remove.append(x) self.dblines[current_mhba][1][2] -= 40 self.dblines[current_mhba][1][4] -= 1 self.dblines[mhla-1][1][2] -= 40 #elif self.dblines[x][1][4] > photo_id: # Moved to UpdatePhotoIDs: Created problems when removing! # self.dblines[x][1][4] -= 1 photos_to_remove.sort(reverse=True) for photo in photos_to_remove: del self.dblines[photo] def AddPhoto(self, filename, album_id, photofullres=None, behaviour='Fit', autorotate=None): # photofullres passed as boolean """ Add a new photo to the file database """ photo_id = self._AddPhotoToList(filename, photofullres, behaviour, autorotate) self.AddPhotoToAlbum(photo_id, 0) # First adding to the library if album_id != 0: self.AddPhotoToAlbum(photo_id, album_id) def AddFullResolutionToPhoto(self, filename, photo_id): """ Add a full resolution image to an existing photo, if currently without """ photos = self.Photos() if len(photos) > (photo_id-100): if photos[photo_id-100].fullres == None: for x in range(0, len(self.dblines)): if self.dblines[x][0] == 'mhii' and self.dblines[x][1][4] == photo_id: if self._HasFullResolution(x) == False: name = os.path.basename(filename) picdate = self._PhotoCreationDate(filename) self._CopyFullResolution(filename) fullresdb = (':Full Resolution:%s:%s:%s:%s' % (picdate[0], picdate[1], picdate[2], name)).encode('utf-16-le') fullresdb_len = len(fullresdb) fullresdb_pad = getPadding(fullresdb_len) fres = fullresdb_len+fullresdb_pad totfres = 136+fres # Incrementing total length of parent MHSD self.dblines[1][1][2] += totfres # Incrementing total length and children of parent MHII self.dblines[x][1][2] += totfres self.dblines[x][1][3] += 1 # Inserting full resolution database details mhpos = x self.dblines.insert(mhpos+1, ['mhoe', ['mhod', 24, 136+fres, 5, 0, 0], mhpos]); mhpos+=1 self.dblines.insert(mhpos+1, ['mhni', ['mhni', 76, 112+fres, 1, 1, 0, 0, 0, 0, 0, 0], mhpos]); mhpos+=1 self.dblines.insert(mhpos+1, ['mhod', ['mhod', 24, 36+fres, 3, 0, fullresdb_pad], mhpos]); mhpos+=1 self.dblines.insert(mhpos+1, ['mhos', [fullresdb_len, 2, 0, fullresdb], mhpos]) break def RemoveFullResolutionFromPhoto(self, photo_id): """ Remove the full resolution image attached to an existing photo """ photos = self.Photos() if len(photos) > (photo_id-100): if photos[photo_id-100].fullres != None: for x in range(0, len(self.dblines)): if self.dblines[x][0] == 'mhii' and self.dblines[x][1][4] == photo_id: if self._HasFullResolution(x): self._DeleteFullResolution(self.dblines[x+4][1][3]) # Getting total length of MHOE (full resolution image reference MHOD container) totfres = self.dblines[x+1][1][2] # Decrementing total length of parent MHSD self.dblines[1][1][2] -= totfres # Decrementing total length and children of parent MHII self.dblines[x][1][2] -= totfres self.dblines[x][1][3] -= 1 # Removing full resolution database details lines del self.dblines[x+1:x+5] break def RemovePhoto(self, photo_id): """ Remove a photo from the file database """ self._RemovePhotoFromAlbums(photo_id) self._RemovePhotoFromList(photo_id) def _UpdatePhotoIDs(self): """ Update IDs of the photos avoiding more than +1 differences between numbers """ mhii = 100 IDs = {} for x in range(0, len(self.dblines)): if self.dblines[x][0] == 'mhii': IDs[self.dblines[x][1][4]] = mhii self.dblines[x][1][4] = mhii mhii += 1 if self.dblines[x][0] == 'mhia': self.dblines[x][1][4] = IDs[self.dblines[x][1][4]] def _UpdateThumbsIDs(self): """ Update filenames of the thumbs avoiding more than +1 differences between numbers """ photoids = [str(x[1][4]) for x in self.dblines if x[0] == 'mhii'] thumbnewnames = self._ThumbsFilenames() thumbnames = [] for t in self.thumbsizes: tn = os.path.join(self.sglthumbsdir, '%sx%s' % (t[0], t[1])) tc = [os.path.join(tn, tf) for tf in sorted(os.listdir(tn))] thumbnames.extend(tc) thumbnewnames.sort() thumbnames.sort() [os.rename(old, new) for old, new in zip(thumbnames, thumbnewnames) if old != new] def RemoveAlbum(self, album_id): """ Remove an existing album """ if album_id == 0: print "Could not remove photo library!" sys.exit() try: mhba = 0 for x in range(0, len(self.dblines)): if self.dblines[x][0] == 'mhla': mhla = x if self.dblines[x][0] == 'mhba': if mhba == album_id: self.dblines[mhla][1][2] -= 1 # Decrementing number of albums in album list (MHLA) self.dblines[mhla-1][1][2] -= self.dblines[x][1][2] # Decrementing album total length from parent MHSD del self.dblines[x:x+self.dblines[x][1][4]+3] # Actually deleting slice of the album, related MHOD and children MHIAs break mhba += 1 except IndexError: print "Index error in RemoveAlbum: seems not fatal, but should be fixed" def RemoveAlbumAndPhotos(self, album_id): """ Remove an existing album actually deleting all its photos, too """ albums = self.Albums() album_pics = albums[album_id].pics for pic in album_pics: self.RemovePhoto(pic) mhba = 0 for x in range(0, len(self.dblines)): if self.dblines[x][0] == 'mhla': mhla = x if self.dblines[x][0] == 'mhba': if mhba == album_id: self.dblines[mhla][1][2] -= 1 # Decrementing number of albums in album list (MHLA) self.dblines[mhla-1][1][2] -= self.dblines[x][1][2] # Decrementing album total length from parent MHSD del self.dblines[x:x+3] # Actually deleting slice of the album, related MHOD and string subheader break mhba += 1 def RenameAlbum(self, album_id, new_album_name): # Maybe we should check the length of the provided name... """ Rename a photo album """ new_album_name = new_album_name.encode('utf-8') new_album_name_len = len(new_album_name) new_album_name_pad = getPadding(new_album_name_len) mhba = 0 for x in range(0, len(self.dblines)): if self.dblines[x][0] == 'mhla': mhla = x if self.dblines[x][0] == 'mhba': if mhba == album_id: old_length = self.dblines[x+2][1][0] + self.dblines[x+1][1][5] # Old string name + padding length new_length = new_album_name_len + new_album_name_pad if old_length != new_length: diff_length = new_length - old_length # Needed to update parent containers' total lengths self.dblines[mhla-1][1][2] += diff_length # Updating parent MHSD self.dblines[x][1][2] += diff_length # Updating parent album self.dblines[x+1][1][2] += diff_length # Updating parent string MHOD (MHOB) self.dblines[x+1][1][5] = new_album_name_pad self.dblines[x+2][1][0] = new_album_name_len self.dblines[x+2][1][3] = new_album_name mhba += 1 def FindLostPhotos(self, in_library=False): """ Find photos currently not associated to any album """ lostphotos = [] mhba = 0 for x in range(0, len(self.dblines)): if self.dblines[x][0] == 'mhii': lostphotos.append(self.dblines[x][1][4]) if self.dblines[x][0] == 'mhba': if in_library == True: if mhba == 0: process = True else: process = False else: if mhba > 0: process = True else: process = False mhba += 1 if self.dblines[x][0] == 'mhia' and process == True: if lostphotos.count(self.dblines[x][1][4]) > 0: lostphotos.remove(self.dblines[x][1][4]) return lostphotos def _PrepareToSave(self): """ Adjust the contents list to be written to the database file, updating total lengths, number of children, etc. """ # Adjusting total length of MHFD self.dblines[0][1][2] = 132 for x in range(0, len(self.dblines)): if self.dblines[x][0] == 'mhsd': self.dblines[0][1][2] += self.dblines[x][1][2] self._UpdatePhotoIDs() def Rescue(self): """ Check and fix a Photo Database for broken parts and try to rescue it """ mhfd = 0 mhsd = [] mhii = [] mhba = [] for x in range(len(self.dblines)): if self.dblines[x][0] == 'mhsd': mhsd.append(x) self.dblines[x][1][2] = self.lengths['mhsd'] # Annul total length to single MHSD length elif self.dblines[x][0] == 'mhli': mhli = x self.dblines[x][1][2] = 0 # Annul number of photos self.dblines[mhsd[-1]][1][2] += self.lengths['mhli'] elif self.dblines[x][0] == 'mhii': mhii.append(x) self.dblines[mhsd[-1]][1][2] += self.lengths['mhii'] self.dblines[mhli][1][2] += 1 # Incrementing number of photos self.dblines[x][1][2] = self.lengths['mhii'] # Annul total length to single MHII length self.dblines[x][1][3] = 0 # Annul number of children elif self.dblines[x][0] == 'mhoe' or self.dblines[x][0] == 'mhoc': self.dblines[x+1][1][2] = self.lengths['mhni'] + self.dblines[x+2][1][2] # Total child MHNI length = standard MHNI length + its child string MHOD length self.dblines[x][1][2] = self.dblines[x][1][1] + self.dblines[x+1][1][2] # Total MHOE/MHOC length = its length (usually 24 as MHOD) + its child MHNI total length self.dblines[mhsd[-1]][1][2] += self.dblines[x][1][2] # Incrementing parent MHSD total length self.dblines[mhii[-1]][1][2] += self.dblines[x][1][2] # Incrementing parent MHII total length self.dblines[mhii[-1]][1][3] += 1 # Incrementing parent MHII number of children elif self.dblines[x][0] == 'mhla': mhla = x self.dblines[x][1][2] = 0 # Annul number of albums self.dblines[mhsd[-1]][1][2] += self.lengths['mhla'] elif self.dblines[x][0] == 'mhba': mhba.append(x) self.dblines[mhsd[-1]][1][2] += self.lengths['mhba'] self.dblines[mhla][1][2] += 1 # Incrementing number of albums self.dblines[x][1][2] = self.lengths['mhba'] # Annul total length to single MHBA length self.dblines[x][1][4] = 0 # Annul number of photo refs elif self.dblines[x][0] == 'mhob': self.dblines[mhsd[-1]][1][2] += self.dblines[x][1][2] # Incrementing parent MHSD total length self.dblines[mhba[-1]][1][2] += self.dblines[x][1][2] # Incrementing parent MHBA total length with the total size of the child string MHOD elif self.dblines[x][0] == 'mhia': self.dblines[mhsd[-1]][1][2] += self.dblines[x][1][2] # Incrementing parent MHSD total length self.dblines[mhba[-1]][1][2] += self.dblines[x][1][2] # Incrementing parent MHBA total length with the size of MHIA self.dblines[mhba[-1]][1][4] += 1 # Incrementing number of children photos elif self.dblines[x][0] == 'mhlf': mhlf = x self.dblines[x][1][2] = 0 # Annul number of thumbnails files descriptors self.dblines[mhsd[-1]][1][2] += self.lengths['mhlf'] elif self.dblines[x][0] == 'mhif': self.dblines[mhsd[-1]][1][2] += self.dblines[x][1][2] self.dblines[mhlf][1][2] += 1 # Updating MHFD total length summing the total lenghts of the children MHSDs self.dblines[0][1][2] = self.lengths['mhfd'] for x in mhsd: self.dblines[0][1][2] += self.dblines[x][1][2] # STRUCTURES class Album: """ Album structure """ def __init__(self, name, pics): """ Fill the album structure """ self.name = name self.pics = pics class Photo: """ Photo structure """ def __init__(self, id, fullres): " Fill the photo structure """ self.id = id self.fullres = fullres if __name__ == '__main__': try: DB = MH('./Photo Database') for album in DB.Albums(): print album.name, '-', len(album.pics), 'pictures:', album.pics for photo in DB.Photos(): print photo.id, photo.fullres except KeyboardInterrupt: print "Interruption requested by user" gpixpod-0.6.2/setup.py0000755000175000017500000000462310513202107014154 0ustar flagarflagar#!/usr/bin/env python # GPixPod - organize photos on your iPod, freely! # Copyright (C) 2006 Flavio Gargiulo (FLAGAR.com) # # 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. # # 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. from distutils.core import setup, Extension import os i, o = os.popen4('pkg-config --cflags gtk+-2.0') includes = [x[2:] for x in o.read().split()] i.close() o.close() i, o = os.popen4('pkg-config --libs-only-L gtk+-2.0') libdirs = [x[2:] for x in o.read().split()] i.close() o.close() i, o = os.popen4('pkg-config --libs-only-l gtk+-2.0') libs = [x[2:] for x in o.read().split()] i.close() o.close() imgconvert = Extension('imgconvert', include_dirs = includes, libraries = libs, library_dirs = libdirs, sources = [os.path.join('imgconvert', 'imgconvert.c'), os.path.join('imgconvert', 'imgconvertmodule.c')]) setup(name="gpixpod", version="0.6.2", description="GPixPod - organize photos on your iPod, freely!", author="Flavio Gargiulo", author_email="flagar@gmail.com", url="http://www.gpixpod.org", py_modules=['mh', 'gpixpod', 'gpixpod_cli', 'utils', 'ipodhal'], scripts = ['gpixpod'], ext_modules = [imgconvert], data_files = [('lib/gpixpod', ['gpixpod.glade']), ('lib/gpixpod/po/it/LC_MESSAGES', ['po/it/LC_MESSAGES/gpixpod.mo']), ('lib/gpixpod/po/es/LC_MESSAGES', ['po/es/LC_MESSAGES/gpixpod.mo']), ('share/applications', ['GPixPod.desktop']), ('share/man/man1', ['gpixpod.1.gz']), ('share/doc/gpixpod', ['README', 'CHANGELOG', 'TODO', 'KNOWNBUGS']), ('share/gpixpod', ['GPixPod.png', 'GPixPod_icon.png', 'ipod.png', 'photoalbum.png'])] ) gpixpod-0.6.2/utils.py0000644000175000017500000001302210507301076014152 0ustar flagarflagar# GPixPod - organize photos on your iPod, freely! # Copyright (C) 2006 Flavio Gargiulo (FLAGAR.com) # # 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. # # 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. from struct import * from glob import glob import time, os, cPickle def readChunk(file, offset, size=4): """ Read a chunk of data from a file """ file.seek(offset) return file.read(size) def unpackChunk(file, offset, format=' max_width or new_height > max_height: new_width = max_width new_height = max_width*height/width if new_height > max_height: new_height = max_height new_width = max_height*width/height return (new_width, new_height) def detectIpodModel(ipod_mountpoint): """ Get iPod model """ sysinfo_filename = os.path.join(ipod_mountpoint, 'iPod_Control', 'Device', 'SysInfo') if os.path.isfile(sysinfo_filename): sysinfo_file = open(sysinfo_filename) for sysinfo_line in sysinfo_file: if sysinfo_line.split(':')[0] == 'boardHwSwInterfaceRev': ipod_mcode = sysinfo_line.split(' ')[1] # Infos got from http://ipodlinux.org/Generations if ipod_mcode == '0x00010000': return '1G' elif ipod_mcode == '0x00020000': return '2G' elif ipod_mcode == '0x00030001': return '3G' elif ipod_mcode == '0x00040013': return '1G Mini' elif ipod_mcode == '0x00050013': return '4G' elif ipod_mcode == '0x00060000': return 'Photo' elif ipod_mcode == '0x00060004': return 'Color' elif ipod_mcode == '0x00070002': return '2G Mini' elif ipod_mcode == '0x000B0005': return '5G' elif ipod_mcode == '0x000C0005': return 'Nano' elif ipod_mcode == '0x000C0006': return 'Nano' # Latest, 1Gb model (Nano 2nd gen.?) else: return 'Unrecognized' else: print "Problems opening %s. Does this file exist? If it doesn't exist, then it should be an iPod shuffle?" % sysinfo_filename def retrieveIpodModel(): """ Get manually configured iPod model """ configfile = os.path.join(os.path.expanduser('~'), '.gpixpod', 'config') try: configf = open(configfile) configsettings = cPickle.load(configf) ipodmodel = configsettings['ipod_model'] configf.close() return ipodmodel except: return None def getIpodModel(ipod_mountpoint): """ Get iPod model trying detecting and if unsuccessful, trying reading manually configured model """ ipodmodel = retrieveIpodModel() if ipodmodel == None: ipodmodel = detectIpodModel(ipod_mountpoint) return ipodmodel def expand_paths(mixed, recursive): """ Expand paths for filenames with wildcards (*) and directories with their contents """ # Processing wildcards with glob filenames = filenames2 = [] for x in range(len(mixed)): filenames.extend(glob(mixed[x])) # Extracting contents of the directories, instead of them if recursive for x in range(len(filenames)): if recursive and os.path.isdir(filenames[x]): for topdir, dirnames, files in os.walk(filenames[x]): for y in files: filenames2.append(os.path.join(topdir, y)) elif os.path.isfile(filenames[x]): filenames2.append(filenames[x]) return list(set(filenames2)) gpixpod-0.6.2/gpixpod.glade0000644000175000017500000043036310513202107015113 0ustar flagarflagar True GPixPod GTK_WINDOW_TOPLEVEL GTK_WIN_POS_CENTER False 760 500 True False True False False GDK_WINDOW_TYPE_HINT_NORMAL GDK_GRAVITY_CENTER True False True False 0 True GTK_PACK_DIRECTION_LTR GTK_PACK_DIRECTION_LTR True _File True True gtk-new True True gtk-open True True gtk-save True True True Disconnect iPod E_ject iPod True True gtk-disconnect 1 0.5 0.5 0 0 True gtk-quit True True _Edit True True Add a new photo album Add Al_bum True True gtk-add 1 0.5 0.5 0 0 True Rename the currently selected photo album _Rename album True True Add a photo to the current selected album Add _Photo True True gtk-add 1 0.5 0.5 0 0 True True Delete the current selected item gtk-delete True True True Advanced options True True Add a full resolution image to the current photo Add Full Resolution to photo True True gtk-yes 1 0.5 0.5 0 0 True Remove the full resolution image from the current photo Remove Full Resolution from photo True True gtk-no 1 0.5 0.5 0 0 True True Add a reference to an existing photo in the currently selected photo album Add Photo reference True True gtk-add 1 0.5 0.5 0 0 True Delete the references related to the current selected item Delete reference True True gtk-remove 1 0.5 0.5 0 0 True True Generate a new photo album containing all unreferenced orphaned photos New Album from unreferenced photos True True gtk-execute 1 0.5 0.5 0 0 True True gtk-preferences True True _View True True Tool_bar True True True List _previews True False True _Details pane True True True Show fullscreen True True gtk-fullscreen 1 0.5 0.5 0 0 True Start Slideshow True True 3 seconds True True 5 seconds True True 10 seconds True True 15 seconds True True 20 seconds True True 30 seconds True True _Tools True True _Export True True Full resolution image True True True iPod TV output image True True iPod fullscreen image True True iPod slideshow image True True iPod list image True True If you really want to clean cache, do it only after saved and quit right after! _Clean cache True True gtk-clear 1 0.5 0.5 0 0 True Check the Photo Database for incongruences and try to fix it _Rescue database True True gtk-execute 1 0.5 0.5 0 0 True _Help True True Help GPixPod development with a PayPal donation! You will choose the amount, freely. _Donate! True True gtk-jump-to 1 0.5 0.5 0 0 True True Informations about the connected iPod i_Pod info True True gtk-info 1 0.5 0.5 0 0 True True About GPixPod by Flavio Gargiulo _About True True gtk-about 1 0.5 0.5 0 0 0 False False True GTK_ORIENTATION_HORIZONTAL GTK_TOOLBAR_BOTH True True True gtk-open True True False False True True gtk-save True True False False True True True True True False False True Add Album True gtk-add True True False False True True Add Photo(s) True gtk-add True True False False True True gtk-delete True True False False True True True True True False False True gtk-about True True False False True True True True True False False True Eject iPod True gtk-disconnect True True False False True True gtk-quit True True False False True 0 False False True True 0 300 300 True True GTK_POLICY_AUTOMATIC GTK_POLICY_AUTOMATIC GTK_SHADOW_NONE GTK_CORNER_TOP_LEFT 300 300 True True True False False False False False False False True True True True True GTK_POS_TOP False False True False 0 True 0.5 0.5 0 0 0 True True True False False GTK_JUSTIFY_LEFT False False 0.5 0.5 0 0 PANGO_ELLIPSIZE_NONE -1 False 0 0 False False False True True Photo Album False False GTK_JUSTIFY_LEFT False False 0.5 0.5 0 0 PANGO_ELLIPSIZE_NONE -1 False 0 tab True False 0 True True GTK_POLICY_AUTOMATIC GTK_POLICY_AUTOMATIC GTK_SHADOW_NONE GTK_CORNER_TOP_LEFT True GTK_SHADOW_NONE True 0.5 0.5 0 0 0 True True True False 0 True False False GTK_JUSTIFY_LEFT False False 0.5 0.5 0 0 PANGO_ELLIPSIZE_NONE -1 False 0 0 True True 150 True False 0 True Zoom <i>(%)</i>: False True GTK_JUSTIFY_CENTER False False 0.5 0.5 0 0 PANGO_ELLIPSIZE_NONE -1 False 0 0 False False True True 1 0 False GTK_UPDATE_ALWAYS False False 100 1 1000 1 10 10 0 False False True True gtk-save-as True GTK_RELIEF_NORMAL True 0 False False 0 False False 0 False True False True True Full Resolution False False GTK_JUSTIFY_LEFT False False 0.5 0.5 0 0 PANGO_ELLIPSIZE_NONE -1 False 0 tab True True GTK_POLICY_AUTOMATIC GTK_POLICY_AUTOMATIC GTK_SHADOW_NONE GTK_CORNER_TOP_LEFT True GTK_SHADOW_NONE True False 0 True True False 0 True 0.5 0.5 0 0 True <b>TV output iPod image</b> False True GTK_JUSTIFY_LEFT False False 0.5 0.5 0 0 PANGO_ELLIPSIZE_NONE -1 False 0 label_item 0 False True True True False 0 True 0.5 0.5 0 0 True <b>Fullscreen iPod image</b> False True GTK_JUSTIFY_LEFT False False 0.5 0.5 0 0 PANGO_ELLIPSIZE_NONE -1 False 0 label_item 0 False True True True False 0 True 0.5 0.5 0 0 True <b>Slideshow current iPod image</b> False True GTK_JUSTIFY_LEFT False False 0.5 0.5 0 0 PANGO_ELLIPSIZE_NONE -1 False 0 label_item 0 False True True True False 0 True 0.5 0.5 0 0 True <b>Small iPod thumbnail</b> False True GTK_JUSTIFY_LEFT False False 0.5 0.5 0 0 PANGO_ELLIPSIZE_NONE -1 False 0 label_item 0 False True False True True Thumbnails False False GTK_JUSTIFY_LEFT False False 0.5 0.5 0 0 PANGO_ELLIPSIZE_NONE -1 False 0 tab True True 0 True True Progress status... False False GTK_JUSTIFY_LEFT False False 0.5 0.5 0 0 PANGO_ELLIPSIZE_NONE -1 False 0 0 False False True False 0 GTK_PROGRESS_LEFT_TO_RIGHT 0 0.10000000149 PANGO_ELLIPSIZE_NONE 0 True True False True gtk-media-pause True GTK_RELIEF_NORMAL True 0 False False False True gtk-stop True GTK_RELIEF_NORMAL True 0 False False 0 False False True True 0 False False True True GPixPod 0.6.2 Copyright © 2006 Flavio Gargiulo www.FLAGAR.com Organize photos on your iPod, freely! GPixPod - organize photos on your iPod, freely! Copyright © 2006 Flavio Gargiulo (FLAGAR.com) 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. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. See the file COPYING or go to the url http://www.gnu.org/copyleft/gpl.html to get the full text of the license. True http://gpixpod.sourceforge.net www.GPixPod.org Flavio Gargiulo <flagar@gmail.com> http://www.flagar.com Special thanks and credits go to: - Henryk Plotz, for his original concept code. - Abaakouk Mehdi, for the HAL iPod autodetection code. Flavio Gargiulo <flagar@gmail.com> (Italian) Diego Belotti <diego.belotti@gmail.com> (Spanish) About GPixPod True GTK_FILE_CHOOSER_ACTION_OPEN True False False False Choose File - GPixPod GTK_WINDOW_TOPLEVEL GTK_WIN_POS_CENTER_ON_PARENT True True False True False False GDK_WINDOW_TYPE_HINT_DIALOG GDK_GRAVITY_CENTER True False True False 24 True GTK_BUTTONBOX_END True True True gtk-cancel True GTK_RELIEF_NORMAL True -6 True True True gtk-open True GTK_RELIEF_NORMAL True -5 0 False True GTK_PACK_END True False 0 False True Scan directories recursively True GTK_RELIEF_NORMAL True False False True 0 False False False True Skip duplicates, images already in Photo Database True GTK_RELIEF_NORMAL True False False True 0 False False False True Copy full resolution image True GTK_RELIEF_NORMAL True False False True 0 False False False True False 0 True False 0 True True Disabled True GTK_RELIEF_NORMAL True False False True 0 False False True True 90° Clockwise True GTK_RELIEF_NORMAL True False False True autorotatefradiobutton 0 False False True True 90° Counter-clockwise True GTK_RELIEF_NORMAL True False False True autorotatefradiobutton 0 False False True <b>Rotate portrait images automatically</b> False True GTK_JUSTIFY_LEFT False False 0.5 0.5 0 0 PANGO_ELLIPSIZE_NONE -1 False 0 label_item 0 True True False True False 0 True False 0 True True Fit True GTK_RELIEF_NORMAL True False False True 0 False False True True Zoom True GTK_RELIEF_NORMAL True False False True imagebehaviour_radiobutton1 0 False False True True Stretch True GTK_RELIEF_NORMAL True False False True imagebehaviour_radiobutton1 0 False False True <b>Image behaviour</b> False True GTK_JUSTIFY_LEFT False False 0.5 0.5 0 0 PANGO_ELLIPSIZE_NONE -1 False 0 label_item 0 False False 0 False False True Add info - GPixPod GTK_WINDOW_TOPLEVEL GTK_WIN_POS_CENTER_ON_PARENT True True False True False False GDK_WINDOW_TYPE_HINT_DIALOG GDK_GRAVITY_CENTER True False True True False 0 True GTK_BUTTONBOX_END True True True gtk-cancel True GTK_RELIEF_NORMAL True -6 True True True True gtk-ok True GTK_RELIEF_NORMAL True -5 0 False True GTK_PACK_END True False 0 True Please specify info: False False GTK_JUSTIFY_LEFT False False 0.5 0.5 0 0 PANGO_ELLIPSIZE_NONE -1 False 0 0 False False True True True True True 0 True * True 0 False False 0 True True True GPixPod Preferences GTK_WINDOW_TOPLEVEL GTK_WIN_POS_CENTER_ON_PARENT True True False True False False GDK_WINDOW_TYPE_HINT_DIALOG GDK_GRAVITY_CENTER True False True True False 0 True GTK_BUTTONBOX_END True True True Defaults True GTK_RELIEF_NORMAL True 0 Defaults True True True gtk-cancel True GTK_RELIEF_NORMAL True -6 Cancel True True True gtk-ok True GTK_RELIEF_NORMAL True -5 OK 0 False True GTK_PACK_END True False 0 True 0 0.5 GTK_SHADOW_ETCHED_IN True 0.5 0.5 1 1 0 0 12 0 True False 0 True False 0 True iPod model: False False GTK_JUSTIFY_LEFT False False 0.5 0.5 0 0 PANGO_ELLIPSIZE_NONE -1 False 0 0 False False True Autodetect every time iPod Nano iPod Photo iPod Color iPod Video/5G False True 0 True True 0 True True True True Autodetect iPod connection/disconnection True GTK_RELIEF_NORMAL True False False True 0 False False True False 0 True iPod mountpoint: False False GTK_JUSTIFY_LEFT False False 0.5 0.5 0 0 PANGO_ELLIPSIZE_NONE -1 False 0 0 False False True Select iPod folder mountpoint GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER True False False -1 0 True True 0 True True True True Ask before opening default Photo Database True GTK_RELIEF_NORMAL True False False True 0 False False True <b>iPod options</b> False True GTK_JUSTIFY_LEFT False False 0.5 0.5 0 0 PANGO_ELLIPSIZE_NONE -1 False 0 label_item 0 True True True 0 0.5 GTK_SHADOW_ETCHED_IN True 0.5 0.5 1 1 0 0 12 0 True False 0 True True Copy full resolution image on the iPod True GTK_RELIEF_NORMAL True False False True 0 False False True 0 0.5 GTK_SHADOW_ETCHED_IN True 0.5 0.5 1 1 0 0 12 0 True False 0 True True Disabled True GTK_RELIEF_NORMAL True False False True 0 False False True True 90° Clockwise True GTK_RELIEF_NORMAL True False False True autorotateradiobutton 0 False False True True 90° Counter-clockwise True GTK_RELIEF_NORMAL True False False True autorotateradiobutton 0 False False True <b>Rotate portrait images automatically</b> False True GTK_JUSTIFY_LEFT False False 0.5 0.5 0 0 PANGO_ELLIPSIZE_NONE -1 False 0 label_item 0 True True True 0 0.5 GTK_SHADOW_ETCHED_IN True 0.5 0.5 1 1 0 0 12 0 True False 0 True True Fit True GTK_RELIEF_NORMAL True False False True 0 False False True True Zoom True GTK_RELIEF_NORMAL True False False True imagebehaviourfitradiobutton 0 False False True True Stretch True GTK_RELIEF_NORMAL True False False True imagebehaviourfitradiobutton 0 False False True <b>Default behaviour for images</b> False True GTK_JUSTIFY_LEFT False False 0.5 0.5 0 0 PANGO_ELLIPSIZE_NONE -1 False 0 label_item 0 False False True 0 0.5 GTK_SHADOW_ETCHED_IN True 0.5 0.5 1 1 0 0 12 0 True False 0 True True Keep stored on the computer True GTK_RELIEF_NORMAL True True False True 0 False False True True Keep stored on the iPod True GTK_RELIEF_NORMAL True False False True thumbscachehomeradiobutton 0 False False True <b>Thumbnails cache</b> False True GTK_JUSTIFY_LEFT False False 0.5 0.5 0 0 PANGO_ELLIPSIZE_NONE -1 False 0 label_item 0 False False True <b>Photos</b> False True GTK_JUSTIFY_LEFT False False 0.5 0.5 0 0 PANGO_ELLIPSIZE_NONE -1 False 0 label_item 0 False False 0 False False True iPod info GTK_WINDOW_TOPLEVEL GTK_WIN_POS_CENTER_ON_PARENT True True False True False False GDK_WINDOW_TYPE_HINT_DIALOG GDK_GRAVITY_CENTER True False True True False 0 True GTK_BUTTONBOX_END True True True gtk-ok True GTK_RELIEF_NORMAL True -5 0 False True GTK_PACK_END True 10 2 False 2 2 True <span size="xx-large"><b>iPod info</b></span> False True GTK_JUSTIFY_LEFT False False 0 0.5 0 0 PANGO_ELLIPSIZE_NONE -1 False 0 1 2 0 1 fill True iPod model: False False GTK_JUSTIFY_LEFT False False 0 0.5 0 0 PANGO_ELLIPSIZE_NONE -1 False 0 0 1 1 2 fill True Total storage: False False GTK_JUSTIFY_LEFT False False 0 0.5 0 0 PANGO_ELLIPSIZE_NONE -1 False 0 0 1 3 4 fill True Used storage: False False GTK_JUSTIFY_LEFT False False 0 0.5 0 0 PANGO_ELLIPSIZE_NONE -1 False 0 0 1 4 5 fill True False False GTK_JUSTIFY_LEFT False False 0 0.5 0 0 PANGO_ELLIPSIZE_NONE -1 False 0 1 2 1 2 fill True False False GTK_JUSTIFY_LEFT False False 0 0.5 0 0 PANGO_ELLIPSIZE_NONE -1 False 0 1 2 3 4 fill True False False GTK_JUSTIFY_LEFT False False 0 0.5 0 0 PANGO_ELLIPSIZE_NONE -1 False 0 1 2 4 5 fill True 0.5 0.5 0 5 0 1 0 1 True False False GTK_JUSTIFY_LEFT False False 0 0.5 0 0 PANGO_ELLIPSIZE_NONE -1 False 0 1 2 9 10 fill True Available storage: False False GTK_JUSTIFY_LEFT False False 0 0.5 0 0 PANGO_ELLIPSIZE_NONE -1 False 0 0 1 6 7 fill True Device: False False GTK_JUSTIFY_LEFT False False 0 0.5 0 0 PANGO_ELLIPSIZE_NONE -1 False 0 0 1 7 8 fill True Mountpoint: False False GTK_JUSTIFY_LEFT False False 0 0.5 0 0 PANGO_ELLIPSIZE_NONE -1 False 0 0 1 8 9 fill True False False GTK_JUSTIFY_LEFT False False 0 0.5 0 0 PANGO_ELLIPSIZE_NONE -1 False 0 1 2 6 7 fill True False False GTK_JUSTIFY_LEFT False False 0 0.5 0 0 PANGO_ELLIPSIZE_NONE -1 False 0 1 2 7 8 fill True False False GTK_JUSTIFY_LEFT False False 0 0.5 0 0 PANGO_ELLIPSIZE_NONE -1 False 0 1 2 8 9 fill True Storage: False False GTK_JUSTIFY_LEFT False False 0 0.5 0 0 PANGO_ELLIPSIZE_NONE -1 False 0 0 1 2 3 fill True GTK_PROGRESS_LEFT_TO_RIGHT 0 0.10000000149 PANGO_ELLIPSIZE_NONE 1 2 2 3 fill True Photo Database: False False GTK_JUSTIFY_LEFT False False 0 0.5 0 0 PANGO_ELLIPSIZE_NONE -1 False 0 0 1 9 10 fill True Used for photos: False False GTK_JUSTIFY_LEFT False False 0 0.5 0 0 PANGO_ELLIPSIZE_NONE -1 False 0 0 1 5 6 fill True False False GTK_JUSTIFY_LEFT False False 0 0.5 0 0 PANGO_ELLIPSIZE_NONE -1 False 0 1 2 5 6 fill 0 True True True Slideshow GTK_WINDOW_TOPLEVEL GTK_WIN_POS_CENTER_ALWAYS True True False False False False GDK_WINDOW_TYPE_HINT_NORMAL GDK_GRAVITY_CENTER True False slideshowwindow True False 0 True GTK_ORIENTATION_HORIZONTAL GTK_TOOLBAR_BOTH_HORIZ True True True gtk-go-back True True True False True True gtk-go-forward True True True False True True False True True True True True gtk-close True True True False True 0 False False True True GTK_POLICY_AUTOMATIC GTK_POLICY_AUTOMATIC GTK_SHADOW_IN GTK_CORNER_TOP_LEFT True GTK_SHADOW_IN True 0.5 0.5 0 0 0 True True gpixpod-0.6.2/GPixPod.png0000644000175000017500000017152710507301076014477 0ustar flagarflagarPNG  IHDR!sBIT|dtEXtSoftwarewww.inkscape.org< IDATxw|W>U޻e˶$) H톄C(z~tT !B Ŏ{/e2Ǚٝ]jW-/}jfNv>)9BJB*` v;5Rs L`!h" ܍"TxQn&0 AD167쇀;Gǵa&@}ơ8x%<.2'0 L 7 (ELRn=O` @ˀWqz%/8uM`y @N`30,VCɳRJ,= L` s݀1.yOBL:m&06!Dn@ee ZbH's@ϡGsܞ L`o@!<<ϻ(//2Mffee8/_?/~xo\rйnGt uc&0 \B y<v ?c5&OFtK++W^C/>+m _[RsT.$fG 56T')e٪t!dRݾvTV;X]|9JTf-nRq=)(4 L m!rK4M]ιl颼9ӧi"r8҇.Ė-۫tq@G pd))BJ#у0(Hr:osg⭮;@ 2z5y[vv6B!" ف)!=(Qݹ L`!*U},^r{҅ŋrP^^b͑ϸRGӒ3G0Ϲe٨g rwJ4G-NeeJ ^eJ&*hYԖ"4dhӍNO fH/AWWWk a)e L ! pؕˬ=ܩS+V\`rCSڋǂ @\t8 {a,,mM޶q#M룹fB>);? _B y=6M/Xνlܺԩhϒ3D'?C҇?  2vƦ9 .I;pf7fRS@DD`2cJ4"LF@E8MDTZꭑZZZ8~8mmm;RqCJG0 K.mKNGQEys5dϛ7BuCQّ> ;wea ;C9F~;TpqFQw1a0L09]/\YYȾvchFinTUU9vG\ml-B{Q*&p.!.⒂ˑx3r͎f/X07c|>/tIU1WBT mɆMxuV>J Gi9rܓ琱M n%H%553BRƴ2$pEt%1Ǥ* :iдgTׄ]hq7*xS_+ӌ@߸ZZZhllرc7܁Rvj x+e&0,>.''kIvv%B0#;;[_7۱lقZ{MTcH g\yOd]ym+7mt[Ӎpq%j\vDZ6ز>R/n|~_0z$A=.wM8zRѣpD$"JV8VQd9nUiF hjjbo[Lݫh{fHxOuswrkh())\r9W< KrfI*KH?XϧWgjUt_u?;xml޶\(FT⩜B& !EFV_|# f='~}ȧ܀q@ ;U:WA !c *=$ gBL'%mȹx2VoҬ^f"ͥ p ;k8I$6MPm\^ۉ/~pv5\ɕ L`!>leeEjv̯*s._ syZs #E҇gǎ=lܼW7lcC  V( ed!,yX"}ncۚ'mFz{{iƦ3BymIDGViG&7b.1/1}-g5|bbv̖E$1w-bBEށV|dpT묜7F75Dms( /BSе/x3Wc a8z% ]Y@'8t#'cťS0TP qx-1]*{1Toޚ^ <&RI Pbm n뤞x!D9P^V/m˗/p/Y\xq]Nݼ*+3Bґ>CH-P{~;w{ -?Q8ŗsguc%! \^¦-2}Z|g_lI}WH]x'B$yG4] L*ƃCr -K1,"yxxH:KYn9H^P7.p bpCPt%}?N> @(M;FY_ ٳLH#\ u/xVXn+oJDze,7\7EJwcl'G__?۶f杼n;{n%)8)Bm ; !>o*B!.mbUVܶ{oY5&{FO=<7?~Cn8|@C:3˓G,a"}VDr`.+'Sd! 2cl#D"K67'yz..܍fqA PfЌ:F[f9kRi0dxT؀٘d:'hR C-0x[@b .^:N d,ȝC ?.y' 9rt+[b݆۸'[ ̞:Jpii" zS]\.Qдp>Hz+1z*+8r)B$EPSңIL +OV”vbZ˷<mKdEq y)_\- !C-yTJOUG@v1IlTYjY^E$ ރCf܌^̙Ep>`lKRJjb]~졽2rSs1s"M oMX0G 4=>i~^b(U:[FÓt.U1zU0CbHhc4%H>i)wIq}k9#m7 ʯcxBsB;ȝm!*G>;fo<3 1XW@h@LȚ7-#JBtAvڦ~uŹ8vp߇̮Yڝp?1$Q qګr3.[(z>oZXhhgEWz>y |bJ* ƟE pQ):r|6Ț 5Uctq/xK`B O@s`?bkzp <,фҲ} ŐTda=qnIc u;)|p\kE56,JC3U%^Y–-"F.YȽ~yڴp #dH81*8nmr*[-]:0fn ?+z4[Ҙ|4} VlšYfl^ Vގ @0,x݋9ef)Pd#6_@xXd/GIC8 {rkW p8OPGpO~r8px3[f]߸.š-gtρF7`ctGeL8 _RsdW.k:uQSCMI5E}l:v[;jqˡL^?%#SH@0ġ~p&"8@htS5\S{&u0#7 l,ڸgC{ ]uZ[[imm%~SJy4|_CZ(|l,Ȯ932F05g*4 thI\Ԅ"P!>?-;ye6oGW ] g`wg;y$&!ғFOF`/`ӏ[;xz?jw7@ \*]9if+oʪimd |Ӹ~'/'T@”Ą=DbCĔ53O֬-e{U&l|]6Hz8r|mXn鱮(PR_I89y**{"O%CGiL|hSB>ChIˍ ٹk?f&`M"1[,ySH:҄,?OҺ_z҈.a]>s )%3f.xxނ8ĘDL|NtPO,;ܲ홐QIQzPm2[@zAQTLAmY|l8YU0^; C72uI"8WKs_U%>* nCoQhy { ;}g@ >OOI DJ088Hoo/躞$؅ZM)o8Oj~ [L$֩sg.]q~/`; '?OpO)xjew "Fg/iB3>7e~:TDmFТ䐎nJiתٺu{Isnݺc}^ :\j{JlMFBEuǀx' x2vbo?uf7HUe"ޣ\9M胰wK2q_H I4gP)vrEжu}|)I $x[IJxƂ?/B pό]l!Upih| fͿ;J(J¸ZXIA>߹uW8w`uןŸ-7qt/DbO$!\=p \6H'\PNKr g_G_oԲ5/IZyפ|i3CQ3mP@i xrkKgЇ25ҫ[z&s?Rj//e<ʑ!{g~y!a9"x<0@`0N !AoC]N’҂2ۮz,d0f{қqf瀄J.W컝l{7j8tfJ"::n;TwxdDƑ"qM'oj9|:$l%%Eh@[ ,72IKk%PxeO"чޱ < !eWy[^\̉Lb'F2cq1 Cvu eW^ {A m( UpFt#R>sh=_G N"{-u4 N E?$-n%ps|K.uY;{֔)Kr)r,fJWCu 6 <6}P EP6c8̅?Ȯگy7p*XYUzSHIcg4D|''SGT̞=)Cq^" xXw=| kQ#/jv4@'yv:[//t~#Auf79 o.o4Ց=WuFנ!1I_RWb̈HC=, gOpPy,NL+6j$2caTH+ꋋVz<drCCMƂ5%ӦUm]BbT4˳9eCsʵgB Ȟ Y3=rB IDATk,mL" s-n|ptҐ&wc%3!M1I#ߢӽN:;&շp8^*pb$& KBr@73 -khxx$|:ݑ #Gz LwTG^=d*D_%,KDm&!".z5gAP8tQYǘ'v)‹Z\B}(RʓDB*M ŗ M,m%+;,]6ntˋ71n `??BȐ؛~#cUvùA˄P!k?{{J57j~Pt^eM8JHt*dW>M~ QBsϕ魔|nKes βh{#[{R֭YjEgp!&g @nQ(c.)X>z?C_#G<|pLFV4;ZF=eZ}phv ӴvLՕHfG4b4,l"iJ3}', dQx;2R7 {-{s_[ktr!7mIB TbҜb>pxLڂsN BZy-|p] *ݽcq$ C J-Cn2K00% -rsTz65/4ȯCؔDW @:Bh.$1SU#y-<ֱr61{= Hޤ8* 9MVhRRޅpV\ŬYI 7Xm짍XyUg(:B^9WAqHᭆv%MrinXa?]lso.LzޣpOw(^EB@";^݅DUl;zUV9/Mwl9t,Û( ; *ث6CQjOru߫ܞs-Of.feyu?UQQv(jε;/]\̑ oug=?GZugKf[,(eF&-O}v'_kx wY0t#ntZB3=C& bX7HC"~~;wJ>QQ"-patXrkљPY ws5YYfu7WN(B>ޯlbVdCǧGa@uϱ-[ʫز]uz\޹؜9ɽypUc|`(=*ެ$"N[0BSyϾ G2_Jb}#+›G3ooz_WqZUN}GAvU׳;cRkol}ٸ50M)#sG W0i[lT<dXGSe$A8,$!_AXcV T3ɅeAE&B=l\Z\|ɜ<:+ z#éUāZ RтMgbp_Pi йO`M#mN}*>bE{NNBƺ!Ba!!ƶQ*] h6Y ={k'8 uQ&cD| umOg\c =M@Sct!;3(Tcr}-C&^93m)mpU8:a?)y7]6L7cOrYHtvQ޽'qnTX͈\.kPQ0ɈΖfz+GPĒ=#53̊zXI05ˉy KN/SPڈ`27vb)@.PvTh:BMօDow@˦O.,D躌xy᧙4*+(nI5QjNE ˖*YRɸ7 7$cy*)aS,XgOP<|}|t'ahT^iDt[\xOH29i’Qb/<5̐MkJȞ3[Nt1^ ¾ suqw[u\رs7 GۖTN!Dx-pv n| #lP|z:C#0ؖ%:v:2!4Ak?A7wmVBW# 6LKLP__#~ˋ {^a7j_ {p\/o\c,psUʨB H!$1RI @J{j6g#nV0zBÜRpʅ>9<^?7m)+~Ͷ!^0[6h FրDHF9HzBUH:tr̖;H{v|Iqc| ‘q͜֋i*HBV:*xb1<7_e<| !oܖzgr/ӯc/y[Zý|o -Fn؇I%},^0x%) p ZhQX{ y]߆ijˮD$~.vd׫p¬nYQաُOM741jihr} _e9!oVbb77(eyUM.`ޗ0y4?8GCWE:l<_}DCqlyh6 WC 6:TS YQ7`EUwo-/̠+8ɧ ļ!]||}UݟTvWx #9C1羨LtoiXj!͖S2i5}3m8e/뵧~UAe9TV%<[[?k9ӦNȦF(E8]3_k+Bi V$[,,ZǯIF睯|%9JLb\OKxu8w!a4Ѿ0pTY`{‰v{5+I- ;pYPO\4:tG\>>?XҹO}Gl9ȟ ^#OMgyfuaLg 1&7H;(oPHMA^=cdp1{PGJOzs1=c?4= p##kac'Z;YH/"U:=E PjJ sQ&pr_G+\kflzwی21[lq>jG ۔)7ŝ0 4ID7/ \." Cme]0G+#9%PM#.ߌh*msݙo,e2A@!$15یeJ"ʔ(՚ TI $Nͼ#kbo3,:F l.pdatSum+M3 y$|n:pM8z]uVO9αC+Rሤkh#07TߊU*uUB\T z'sでisxb\^a2rDNN%mcmoE $0 Ps#  uNv^v=iBr10zdGufTƏֲ38EA&mKڧxA*@ii)"uB`:09zrJ95".t<: cEF v0-WJHPx5|s҇'__:Jļ " ks?kw1wᵪ Z[b=tBČ76U:񥝏Ӿ_z.fʔ 9M;FA(ٱ z# %o#J31IkXĪ! 3\jvvFb])D S /6Ѡ +00>Fty J0ɀ/)&( ;UA᠒Fo){Gy2_w-fb#3 ֍M7֥FHGRp@*ȍw w{׳Cjq<ހrut2u:hO o3ZM#zFIFtӞc9?RbƼ Fg<Ѝ]aTb7P &5P07=:l|P L@[İn%xc{䑔(ORQ)D6%CQ21! a8} [vܴxleK7KJJ.70gd hfgqNH ,"d$!$"eb)$ݗ)FѣQb8%J&2RRŏͼ>]Cw?oǮG[go]^9 {PœdvTD/#7=kj-w >z$ID~9_}w[|zFv񛛰aF. vwsOi> o LS}+?Ԟ-Rnd3Q4C8xHb[G \@m6&$ҍ5d;ѿ#U18BdMQP `1]4CfJA2ثHJm1i{A–m>&?02'u?{Oc27W@6RHC6r!^ .)X@=ǧs^72Bv^2]o䮞/3?+:oS@] ;OquKi]O{ iHrrk輯ztZ8Yhm#AО/̎*dyY@hPPCU%5!t:q8Iwe@bY$9a}⼝b*I-\yGYb1HBW̌_k8 @kj^E]MJؚehn Y 5;΁`圏xz"EZ(pRP0#rtcDz6< [hB/<۝J"GrUZܜD\Xwz{%Oz.s#w8 FM\O]:"+BIY|ahӓ|S~P$AԊYprbP:1ctb(j%ۄ xԒfd:1u/]|A<x<~ lGCb T*cX<(T\> A?8Gٱb,Q1#z%f^#./Pugcww7OG}%wwW3 RE<'gg`@y8 g6۩<~ N]GN x %F"s$JXi濆5ct*':7?ȍ|}-Z.Og,IJ_ł]Jc/IĐ^r9JE&{A>% `u Q$\9'}&ƃ IDAT`e?ã2=3vJ^2RKЃBw+隦T>t},/F5D$ #ZrPFèE!X"L51g+ц*7UXάp:W'` 﫷VC.dCd# Ypv9@iU1Y'́vVTƇ:0Nd'8=Fx;wˋYx: o!Mr w{~%{1g,fse7yfC#Qϲt0\osm[R:^>= ѐ ΐP 8ѴQai.\Q^^z)V@~">@dxcFc|Y[fǰH`8DiazYd(2@B%*^5j#Hd䐈JKIeN 1Y Y }u aaW^$E`ԫ:EO9E(/~"W${Neh^a ke׏hu+UL̲K= UHشkYm^sƨ3G_Lo}w'2aageZdhֵ3?N܃=&1g:A r\nq~o oo  %HֱTҋ+K-w RuslKFވ(|o"o= K 333Jv`bjIl8Ve=}PtHTyP>IS{ylyT}$G2與ͥ t ^ C "槢%ȗRjJprN$([i[d +++AFo3 Jrys^PY=$7jo>d-ה"kD$'ui[<_^#ҟYgFlUG}p婘C9{892]=9lΫValɒ,69s8{p}IG88s0` ٲ`eiJڜffwR^;]]T){DK#aUUsgIxhLCeӜ2;>`&rQ$jncPW"Y`PvkeGZݛl^Cϵ"O bQkWV"<%ÁEw97]Z x*̛p.ίz/sdOG}(m BS?;}k b3ھ"W<1BV!D@9GR>pIf:/ /k)402˜0HZP*E&nI3Qh!O=e "b_8ywtTOV\~H'X@@Sc]M I0*\a@2Lwi i\uq[,!9.G9LD/rMD1K9  ju<7܉Dg1/̋psy`fa0tc IvўvZ_7l@$eH 3395ELPl}@]_&K{~RcnZ[U+au|wt}@krrFaV3% .)桿l9Pa?v̫ k4;rpw-kGRyi:WvS~rv*/MYcMUpq1T QIhŞQ "_?X=: >oqʖ C?W;ފ&q$"Lz2a̠mŠK3̖頓Spr7:UP). Aʦ4=b/d/$p-oqR45j0g@kbJx@"uݱ1N7;}yݬ0B)D 7nM>5ٚF{vI)nhii2(Do!&f5x5@h%hg7{:XhIž2\F>̆5ƺxd~#k$}h6WnR4I>Қ2z0,YyEflPs)p-"fmk"$ivVu}q1_2 =p.R(޵YJMyFC[keX7Wg,;==bH"{86C'?.DF@M3K 7rj'MX_3%=Mij,o qCAM1/2馶/]q_Q1ؕB o/x%KCjE pXQYt)e.Ԝ}H $/Yp%!w \*~J'E/AUIE;yWs ҆q{X_&:wbw -"&}&'H"ul T26w۰!a.Xs_+c5z{iϏsDMMۃ3{gJ߸A hoTC]&UR4t|JM釷0f%V< gb|1(qR {x1$wwJt-> wmN@ﴭdy3c%S wUs('ŠeL2vQ5M@o`O`CȿFdi :& qƐ>of xonڏ;$#:}?*o; *_γo߁A'YlT<B2HZ=xnVaTYS=,g7zru|tK庂ZIH͂3'-݇F_axІpM:=+ zeFN[o찠7%VrQNͶ0>WtD${j;xV aZf8G@ Bɮw!~9\4O!OK6N351ӓӨj_V[6TJBU,o#UޱlR}GR= V|ci¶4bȞWeSW&+QET+ͻbWH}˦ `2>rt 9 wC?GRh!}WeHЅ>Fg$߼I5d+2:L8j&yN qH'XK+y0ҭ v"I^c\+R%0WaL2e'k%}ˮt BS|HpUjT&FV<>˹kn]}F ay^9H 1 f [񂜥/8ǖvM9 brhnmtZezrё & <+H_H^Bň4`ţAhfXۉeiFuR uCvy%̛˾ rF>t8xdkG~spa+M=]];o;y[E0_;-1K-ɬ6ЅQuWQ#O?s:G gZoEyYŧYIy3V }6G%1 lSmEp3,mȗ/a`nUqKɱq=xm p!6ࣣ<maۖ KtkA ݻs}S׈c1˥HSK#ss atd±"ZTR9'T:; 7CHYf"G7hhC\*Z\NvTW>3[&I\E%r෯w>gY06wU &"c]j@=p-ȍ̙*6ICjut9O%g6%T-׊$ڇ%%K|yf1a:]Rfm pbBmk_ݧ]~RTIwh أder |mjmὛj'Q߉qB4XptQCԇb' lo?}?=Q.A c:ZǍBi)};j\޾}a>@j*.ߔ@mqLOu'xUK3]3 4X޴Z,-~] ӈCDAG0hrTfrDwѳbVZxn wX4?Ny̐*3?5g|>[vc& i3^X|C3 j`Eߑ,c?X~e-o3t`4&ؕ1-omEg4h]*7u6)ON[HaXs=m4áC٭ypr<&K+XkE֑-KQ iN( HRa<]gsxeLQ̸ XO7-7[!;}WKdg[Z*Cy}3«juFe6˖-u !zfFډs>^;Șxj:T뉥O^(Pd\A tTE28t?r۫s\zɦ@ϼyW?zR̀u?F{6~-Fy}*TȆ[7^2*lgZ~"A{e4JѢ;Mh0-Q-iw⾴^6w/isjHCs 3ًf>KD˫ا.}`ln3Q!nH={%ˋ$X'x$BQRRc%U\T@RI*ntG3|^Vx%`+kaE}FM"}y`KBUDnԩ՝ygg>8oce"w=?G k}]}< $w=s=gIPil^>O'yo_DvgKzj774T5x $X]?]nuT.h˷o `=P\ͮ ' ƒ^^FƓ\i_Ipk>o&9 Gsa`E͑@jU0L ^f`n=OG[8Ulbmǣ ldghp9sL;8cɥ;h/)`m? ]OLJd]4[ߎ?~Rb/ţ;jm]b]qOr,@2SXO;O_=%3`:E֭5^g'\P˩X g&%=*/$1V"GYqހEP' PLu7'v0zz+S&?NP/5@Y 0>r)\j ^à L6ruJڧu3,rt.a-\8.k$1& T7yڦ5K?iy)'NCKr7/N屗%ՠ w%_9·nq΀~)ܒ C%t; d%7ڮU,@J96n\O8nn$Cx )cAfՋD,ĉX_ԚCo5N6U]|b3I=V|֝ j7>ƯN;.fM=n#hZd܏DCU mC`fy{ϻ|Ϸ'ѷNJ-7Þ uEZ>eP< o~[ƅ ^; jP-'z^j/՟ț֎ 6.[␿ R?M7pT=WOW];&yg 0>rn>iua.ԉkJcDj/ !77_Tw@滇gJ8*𞵂 U﯈, &}U^_魗߾UQ8ʢ1 / tONpҶ6rC0E)egKKKPR-C% !jv'w鴛WX[nD R@X$Xc/G4sz#_(zCPrw}Թ6rJg"_xC0:g{B.7k\-mypMLov?T֒˂L*u%ܼl!!R[U0@2ɥg`x;J5rEa<\[` (pda;բغuo䉲/ I?gp,hKäL&=<9ۜk3 y0}f#/u=W~cINZؠz5H×;yOg:ny:Lߗ`48.ل8jFm 64IT^?=_>Z<n1\3lX)ͲRY*[?XDoɹ!ذiP_ fe$܁˷7}[߽ RZGp>i$}ÕL?jiqɗ@azÆJKKeFr`li[ÆZ!~:4`A=7XCI/:z9G⍙}n?c#+CJKjQ7ڊ5sN5$rM$.Ft6eN"؅|'NFRݡnnfǹz&v߇DQNS2~ҏa1|cVDOC 9N,FAK2BnΊs^XViETe&S*[Nn"UVU 9db'HUjcvxLtyJ"J|Brk>fH^C|^ϐPCvEOFZ9mcDڃx77 $77 s;T;4UXn"#UGa A0XBlm\MW%^}zHC-SQg"L6r|7];X`G==lLASՅ.)KGd5DJ2/.NT;>4gj卧61d]uU2 Bck8 r[t+ b:>N-I'ed m,%r$Sfڈ^HձNb2+ _L /uU$όcJ-vp94zɣ_H^P28Ŏ|ۈ8GmK1Nc0 o ~Upf_Z`-yH5l<tܨEX_zH4N~a U~EE}P(q^u^e{޴7;ۇm{Rxw5 BUi!H+Utm<+ױ{2.O :&;Gz(yYˆ%xTjXX Gq;RY|/]yh6;4Dij*C|R]+e<$R"Dʜ*)I 64 wmA[8XhjSnΧ vCv¶aJh V)>xƲ_VK:8GXlj> Q]%5zF|YH =$@j]%M}אJ;+r -a}jʅ+ RҲ&'5]).Mo|i*J ߕH~vfI͕eTbk|chc+3}vZ)ۧ*&ysj'zQ\W7Px#YxJ .^t *iE!-IJ9WD]s/ku-#E5h*+}_ǽ(SX%{ZJjqT>=0wgB6wW5eʇ,xoZϜ_+Z$M3yXI2\@WaՠFI,'iSO]M|2W@4 Ѵ&If8s7#meO!Dr*O8 R)TP*T$<4k]?zDDZCvxe ]],eBt}?pP 8ۇꃗE~E]~V$ZH94'g&([ryP/3[Tst 'E;P읆zkN3 =ѩt1,ʭHXUV}<ڊJi 3F&pH&| -M;WM:i#m$x00kgANW<56]O7dc"*nH:,cPWW.[~u3 OM)vnל#x!L K!(wKH 9UeԮEN(UDXᚭI;ipnɲ>p&Ue磗xMVWiӹfr1RᷳJhH(n&\7]Ev) L*kv׶QkV o^mž2jebC8JZ孟{qX>a&wDt{qA__Joy9P"NgQGB1p  eg0r\yzUĮ*ƟRl% @%L['gO IDAT e&3Se^JDR}E9G6SI;HҞk{=aOmM&sOf)$k(, #UI"1%13E]5[9UYTq|γ3ҕQ&E!uѺ;'p>Ǘz}vyW:!uuB]r ҥK3҇dx^*1ͦ|u"Y⚤HAj6"'k @W끥$httr;o"I7;ԯr0zsW^U7|t6,Y_#_ #-{W8w7ҥ '"`fm=ppQ7ۯc6d/wC{˅>gn`y~ !WH(\f3FTNp_nCODV~\m %0ƛ_bɩ1+O.}->ji6:ٔC^mkVg 9%H  /ouo)|d[7,$\;B@!'~LOc#LZ_xzQHXg8,2.Ϧ;H(PG[?w=ӍU6y]+'W\r b@n$ 樯rTYc &T/7neARV_ŋ]̨ HK<@Luh,!r6.MuS@R!J&3;~6y,F JG9ye+.x¤R@!qאRrTQ b겚4 :SZΥEK0"ToBSyrwoOacmμR7GK9#%lvwwOu[a:i4CS͕k6nlkO$3]`uՓ$%;8 5=(8Sr={$3?VevQY1oF T2e3Zټi 6e-b] 0Vد&"Yb>tx"c6'f%lpD# ofM@fқcgD1 YAGC^~ꍤ<.B"3 Xq0FP[0ίG\ }O;7i~# _cTL `͚((ܰty<ϟT բpX;u"ROe(<<1"ө Cioy <)"(#~U@wID3m |iPmڍ9~I-VU$>VbhX]KV`[s-EcMLp%Rk>7l]}Ef4 $ hv]tcmTK*п*uv~eZrLcs{PJɔ/>Òh4J(! z% ۫e\@O9H7{ Iރ㯍] s 4{W @*U͎+UtMb;H-\Dc)s<_ R)J!)"rMCg+?0IHRөMwYۃHK3Z.濟3?HMKTɿ'%6?,]#/_fa{lJQ~5m{aSG:/\ZM蕄Z~n7[1r?XVqAkS9Ce,QQ .`>?k:rR!g1@n7"Rҫ^u)~2y^h3̒6l ~.03p^x;٤Sj#g [6=uWqŒKyABdqd2YT(a_ICe3Ig=w‚@k<p䂻AR+iE;syC;]mZ )dpӞAͬʪ 5",QI< >Ƃ3eeY…f7f$fÝeb]WJVF)o,\՞v v (*(vǟ؃]~ALtxPbqczoQrJ$L׬3#hjOkh( M}?'Q 2aZy|ʱԈǝMLs|+>%{f\JA ]YuldM"}hc2޴N",ޓMWBjUyG߲4XQg }<0HNwȃ{ʽhrͻۚ3vEJ9 8ydQ5OI1/eImJmu$PJ@j4ȅU7$JC9FqlMX~ji~zNj1AqH[Y ԯkf1?*nHRRWWZh+? ;z(WFQ҈qdzbsԓA:=Y.)$F!Zrd#MXM鯽A.b|S j)DyO yj1{7ÿϳ~_zCQ `AK-!~4+>R2:"<^OdO$_'Mw}U45z,,mȮCdV+ $~>i^O^WrR>̾K^iTȄa1ǧ:HCx& BvgRzh3.ws h x ~%ZX:zu6LzZjNe£^#˻0SXhQMBsS'933Yb.,WƭN.JIw#RyC׀,v[TQa{ f=!d Ď{u{lCHHDI%YE$˶\e9[$;ͽ8vl'or4;qrrzI"vsb 89gfgꁺ:w14U~sˡ?!B(|.;i:Tqxg i_gm.-pq- xoVK S&ioOt?~ DarOP 7?=LNm}p=?G+")%(GҢ(2z=` N<;.穾} s—^sl=&Xk}?̔E`tO%FJPqC _4v#m#ň u G Ov={ )嗄IR9rիWiZ$81ќ4V6˿j&W"H8Mn%5QlFs̱KUcI ݽv~iY*nF4% }'Mq]sW[Ή ;UȘVz+%!w9hRv.;bo1? Vjc_DlDq7Kz }Zr@QOHچRc?B6]JQZiqǎFlԤ'[1Uj<-L^NJ$5.u`gEdmCӪ'JiWԏHJ?/pOX3^P^oYviΛ๗-Ӕ $( u,P2@Qal[ zwmcoZ b#:^\i#1Gd7*(:H TK/hrsQ=_&{hHVAIPXW#b%MѺB s)I|_ٕ%{ :t [x;,YZTy>=@a!8DD"GQ#X8F/NP:Q;Qԩަa|}e1$Hѹu.41ZUjǯvj a:rQ b.|9[Y*:@b·;E9O3%{bK +E: j[z2\#!*+$~m_}O7/[ˎWipi܃?/Yr1Ǫ,y߱DRE$("((;HWH7q7 MT]Ȳ3#Zƛ|󳷮򩣮M=J#Dʛ<'/<%s8OWY:^>n6w=--yw ȡ*իB2;)e x300P(D}}=~f1㘙 @o׼n>bL1F'3^Pyɢ\XS3k*&*:f]{ۃY\_<ʵV%GyTWۗl "jZkq:- 7`LRE{#ҖPGl&m-!m_FYI5;ӐN_{7~{7'vrv_?)o>;7~%6 d\*]s~7ZZPG r*1$t"u,YZ3ٟ?7NG<,.+ FA FK+yu+[֐j $v( d\\OK;WF+T"q(Bf_+~&Mrk{˹w6s7jKB(P*R}BqW*}\9tt:A~RIQ XB kZi1g  '@Jf  -S5H.Nrvds6+c[vUW|sg4*eܫq'gP1݀IvܶKJ!@:^.u(4yI:`]N%^>LTW 7U餓t]ݬ*Y@Բ`xLW=}]3/oYNS(x^}5#xbK@}?EF^9`g=;[Oo]Ȟ,8e$ZHۃKPPai$vAk]3`ĪU׻£ > =eC0tGJiOO!n]|YTT*i|q=*css|K09JDfu9B B)I8sslL2yvERv*|,}9}wcɵ%2I]J#Zɣx7 d3  ?rA}ip*} AU[I^QO gk 6$0WeJ&-m'l}nͭ%t  3/}n>j.Lp(K$rRo Ͼ73qQ4PP.l٠t[TnQ#׵˻{%H *q,7n΍Ypv1>{[|[G?B|x 8ReYXUxYH) kW ;Faƅ]I !G9#o]IAyd sh: VӷJuZA[+S'X)JB:>8c $r)sq5:^\+a ŧgP?jZ5"ΐIgٝ[iUjT("~>l*`Z*_ZȭRQ$]#8̏ߺ.Gycyו"FD# E@FK[{8!q:h k_BP&cQRȮ cI뗯lx:>Q5Lpm[/yxRZ!J$7gkic{0=(zhW"qy(*R>(aIPPj>j` Cg=QtsqlI!xXXƕ>4pu y}1`_$.!5T lq{[ʆimΣ;0I0A^ .#EW҂GAhh*&;Q[SוS:^T/m"/RX*g<b(?UR(O EHK"svN-x1yV$~ ?Օ8AcڛgӃl]{>_09IvL]MixR>. HLn pIS5-FoGTlIEo9B+Aɗ2ǨR  IDATKsi[T^唨O&k` F+]8fyXẏP+x$b `uPa@%X&7(s6*Of _﷓n)Ds6PĈkp>8PaT"/X)؁0{M@ 4nW1 OK.+I[C+/ڰ7ƇֱbB,&@k$İ,'ObvoہB=u\+ةA B5w50=|UHgRiuK҈[E(Z\QAXJ7D 8 ami\.㪽L^M!s6q83fn{)A)P:xsfiB&S}cY&@pA{( ,OЧ*$ 8|AWFi޿i=={D.7qms0pٕ:rgߢF>R"G$<)'/⪠%Pӂ  sm[u̟?JNFBGRi8޽[F=j鸢(3Ӯ ɹb ]>c(f|M5t"J?'smFf8cԁ%?B(ZG3H_9I%M0Rcޱ]}ut$-J;;@GE.iD 6O|Kju*ˉ h|'8wf!<Gnd';3<6N*{9`Ȋq6O{CkvrbCE^w)C*,˲ -ƿeO~ -'aͺ?q ɣ? =BR"AnB$y=F c4mjD_Hu͞H~n~5l|L(*odyT*SA,'+5};iYLHVwAOL֍5I_nJrjIu:l#\M̋?~'$RI$UKz%Kg+ {nWB|!<Ibء0vtB2)3d&atf^a ߰%K%QLaWW0 #Go~{]xTJ¥sp\AZn/_ۯTP1G#8] {|zSvHڟWIeU/Yʃa=R<ܔ!*}Qf;g|2bdϞ}֭ۢ'O2LTt:8O f!RoR].!ğo Z*Na@ktӧj_4*+F׻ȵjAlk2rlϻ3\.mF>ٴ Ze)N=O\.\ 6LI89Ԡ{/m|%UӄSG߽z&#{Ne>-hSd:V}+Y׵z}7|8EѫouO|nVVnc<3B<"m!f~0Cn@tl'$Nq, i:y1v$kLW gt:%kgl~n  SILttI%vړضm{W7Msq.u?8>)Z{]+f@Rf_BdtZrLEE@SNՉ(շUVR bjM a G7`7"ʭ5,M?bK#Q$7vLӏ5~7"\p2n8zlX.}p:<ͷhYIU оz/Z2G:VJ%rA`9e%jI5?>Iy Oy0EaꧤPBYߎ=rl>>n_il!xkrȝo2jT,/U ^"?r%K,4hţR|7oQ+ wȑsp1^ mq`z]# )RN{0$) ww!+هChAW*i^'uڧD!,qzF_ks5'#S6'~\m VCj⃭q'+4v0sdU‰#YUoCjYT6o48v 륦16|rصUdk69WW( 䃩~۾h.[]Y^?u'hn9_7-g/-V4YX}P)ɏ :<zk1a]oNÙqKAصrۖr0X>t"ު+=X$0}Xwx4<[!Qm"W6ԃ7oM]p%*w,coM!+e݋q8s8q!I/ĺk{ë:nq5~hU4'sH_ ?+c`ֲ^˲_ _Jy0 )rOgpTjuatl͋-C-B@l*slA;_t=6| (ZS0wQS;xCb W寠:2 ؿ˕ Hq*t ~]};C!S9|K'ZΡ[pb̀>"qd׽G#ȰA[C3w_]^Ɇ̊r?ҹ$qv2544eqI:۶eDN$R lH).q#<:$bڐcaD.`D.-ߺ _7V@ʥΣZy a ~SQ ,P +%H"H)9ukoYM 0V5?(Saícsg`oܴ`ؾn_e89y˴*S x4 |Tzo%L R^ 2CJ%[폓8я̺D;i:vYWطOTHGM 0\lOQ@M4߱ )PI ѧb\<!(5nX6nG6p-7RW#M0w2c18p]m{>qbaƐO[8WZ @}OO !Zmf3ڋ _N}j.l!I kWͭP, lL؆cc2,gO]` +2ff>4ЍLpF8y LX(1lc_:tQgmv l,x#ɞ38.iQs3 V2CXa$M:6ܴ7o5אctJRJΜ9GWAc?'344ҹƶVqk/()2 |b=. ?Y|` CˡR]<coӵ/cN,X2{ ܞIŽq.cMYJIϥJ&B>Sa)H8֢öan dcrnim Yy݈%(T$8]'ØLJIV^awmzy3K2Ԋj%ڤq 'hf҇8o:pЈIEQND ]%bH)"Z\2yV$$Ѡ@=.TRC:4E8dيUeĝuC +s2m<ցY} O[ŞPv/WuF,$O Cfj&1&.ܺ%oaOaC-nb^q[z=>xSIxF?uCp:p}{[:}d2EQJ3{ CJi+XPZD6gO!}`t:ê5_44~wF?T?,X@u&4Ӵ8}\6!FWᨼ\8p ȑ˟€A@# 6P݌8d`$'?bgf6=v37߼gЮKqiqMH1=? x T0eT#DhZ''IS?'/ pIVOUR+"74/!!r9 JQ <A4!Yql7rġ]+##N}}irNuX8Rr)8$oٳKD"i)9N,VpH1 !;nfǁ;'{=2c"a݀-,4渏eٜ*GsP? ]{1nl|u|2L_:xN yTQv1XT]; e2Y>̭۞9|H$GR|b|{<)2|b-.i4fx8Gؽgf.n ޾u)>xRRc !>Lgvfdd +O1?SH 5iHL.fpL&˪5 f. HqB;*H/=XđF]Cڵٲe$r2ǜ?=w fmqS|<*S|\+dH)- q%ǁ*ǍhSp;s))I|pgmxw~sI9xt.C7x$˕Bځ 5?Xy/<<{MaYObΎ#cx!N%g)>5 q ON w[1A{VxpKKOv? { 2Yx!:k>%K;>յ,lۦwށU0]qT{Nq4M=+HnY2СcwضugĉF*+rp``d2k ǵO$ہB=s6ܪ}a g` Xk^oEkD"dx0Bϥ˘5ʣuU]ZUUq!~˫ 1tufϞmv%{.n I)w>mY>)幚p#)"/ !֐[MC'*Jg"_z[2 3+02,gO_w2 g~<4m $2eh0<PPc:-*>{czZEȥ@ׁ{v 3\u޾$pSaꏂh1+W~MKOi2#<Q Em^ iljDQTb&ppdbj6;@eѢ:TYT]X۷8xH$I!h4L8R|xOH)mďpc?o| K?YQ 8CaèBsk3mm467̨4L"I#ɒtА+o b8eCm[wN>ofوbtf7pK< dP'n%%'18 ҤCJ o:~d;Ln`duY]7fJ>fR]UMY"ĉ>4_>}X0$0RWNr,1MGf r Bb GYάJ;&# gϞ|H)Op“@AH)+|YҬa6ڜⲞ|l#kRʄR7&8`,ˢ? ìSay!/@Wo!SWM5/\Çg5,&O0 RJKJ)kq81ݚ6L>m"uTΥK5呇نG *BJ'Fn\Ir5iL1Uudx< x)CĕJ|#Sɴf׮]&N\"f<]|#GkkZ0WI fRʌkRU&8&ZIG< ì@J%S֜۠O?'=xxFt) xn \jSGz3g#s ìCJR~JJ 291iũK<㹇9OaNBހz9E f#sN8'z$sRJ]J`\ tH S~% ԩR\G < ^|CJm]uq&j:i-xÜpUBWu[ O(udܱcǎ/q9 OpUBJR~FJXrHu(pL&L~RW*8S},=b&'xx@^+<BBψ0ٳgnqnۃY)|b1.)ctzU:F4hnn& ^1uT'NG2< B)>FKK ͪx?L=J)4K̓ kBFwpU\wkimme޼y477O:޽={&Nfy0><kRyBsxxcxx`0H{{;B+b(.Bp|w⑇OpMCJ"7jmmIj?ak7J)Osxb!N\2@4:;;Yx1---3J ??R~U#@q.hhlldٲe,YdTm}}}|{ö$prZKa&@Qk7$!D;@3@SS a躞u\M؃#Bbv3prlӃ#Bk ᪥lڳg[BH<k(t?#/\5M)4ͯO4i>UUUO~ ~TUE4GUUYP(Аnnnδe/^ZfMdI xvW <p +OUU !|EBETUy|>ߔɧ  Tsssq֋y{³=xx1!FAe42D*N( @& _pСC=g30JK+{1ɡl6KOO} Rl>_<QN?hz+MS%L&˗bE`3{d&!*\L~ @=* E(l@ pqwJCtAѨX]biW@l1 Kضici9a&2iop=ƜJCdLVT!h'#EDFFғ0 HwKMO34OOj RΙ#S\S8ia7,8"YlE!U48IӄaTUï͏ނvu41 ]6dguG7LqlGJ؎Xq؎mq ZHqlrl[J[:eَmmێi:4}2ð#kHӴL&8me:imEIsppnnG 5!z|!P}U+j( |/3N˄TIH%1Ik.!'NZ 9/u"hJ.D0Eiu,0M^qW2`!qi1d @DӈQM#)%ib#$cfD34*H?:v;<0hdd=&NLg2,llpV'lZ&= 8/gѭY|!PPE(hBAA UQ|}~@Ӈ****A?@] H$sY>~?>X0L,%80 fttuÑ.X-?RV؎eKǶ9ҖvLSJ4)m0,4-4ܾ!Mt٬g,Ƕ ˖2Md2f"͡!R kI8'J񅢁F2jA>Er͌QY2BScd4+P8&*,[|1]c [n$3Y,LLn1-C\B\SIDUSTK|deXXJ"%Tbip_*+:w9(? dҐ`YAЉa$l r̂LT2M:!ɎN{4ZMծI_Bg!GaN&Ա聵4B~m 6-IC8Ip$I:e%m4 @6::M:$@VHt@UFO>H.H! PPPQ@ *Mr'5>ҚP\*!-H(\}  PDn@WJns6Inibf2m]e&uLrq@Ε\qmǶcYı-1mSZ[gY,1 1Mñ,1 1MSZz^,۶,]L]׭d2i 8M@*O_܀?2 ~_͕EP1@P |8}?M&r7V;6:Nex:S[ N/EX8d`H$m9SIaiA+,VVa"|> @$-IHI̴6Qv G a ށUt$cRO ;;1ͯr]S#΃srӄD 3T*A䰿˪9SR4TT2Y§[!`uz-%Q\:׃<G33ȳҏ$?Y };yѢn4`iXՠL\tZM\AAbbKHAbXBY#'c9񉁏~eY֕T0N9<ŷ<eͣ`]ͤ8n[K׎6c4:.X z٣]f'>MkW'E6 `k4d&Ytkt ,,,sBxiS70 ,y$=xz.F:CUw rr'w2sZ%([*]6-3pAnZ> M\b|if*%fn$%NeUwDdܲxTZop#Goפ̴qԪ!~   i+rslfiͶIj V۶dmK IIiO歞KyoS O-~>uwMXPoMODXk9]wcnݠ"3"QigDȋNtqQ` tcЄ8S6Lͣ , z1`wZüG9`G(%.Mh|ƉēGqDm޳?`u4M0d |ovRr 'gȂ<7CF99<&<Fh{j '<5J';Lj8c/ݶe\I~ް+Jhe|gK* :wGo^pDEOvi:yd7W^"lKّ..05.ӔUdәLW)v۫mtuc"!U~RԽG?_U;`咘AGOmHB l߻z^kSzAm eCxS \x=6F +xU!1KvvsƇ1Fą3Iy`|znp߽&JQa/R~ⲅ# :ry_l323 l?5Orע84rK鱣]@Hq;H$UHR)IuMkpE4ϞZZ 8 ;+3׎03Z:/@Δ9=+Nɳ>߸{?W^lyq9p90b'9Ҋ8ԊĞ9&cˀYGG:K<48p+Pq\@cN/RAk:{Wu|z'OƕU rsۼu0$_<WyU!sk{M^I\\tI˝y% 6!c]Xv5jHRmLfZ=>^go`GUee (Ue EH&6-IYh &M1Y"F- /1Wbn.}|},unyd}̟>B~7q7ǁwuϋ{~{xY.@3$OǣwjyA u[iOAq-,J8ph0¢;k[o] q}:姶h9{&vLq")q2Pa/rΈi3bFtadXŌ73"`}<ACL38E<<'ڤ_oH~[uO9xt9\4aemWxQfdFM4oooҧ ^nF9bo/{3ֶm{oYs?k.Ʌ{IQ)H*F|z23Q 1ȴ'_|}ڶJǝ~+nb%tN7?s۷JFr]~x'3Jw rËm^j Ʌ-u̐ҤcΨ:EP5J|sC7^sSW_3-y7'&C(r`,눨s:̀X[ڝi kX abZ; /\_.`( @0 |G~*G3b}:c@ үӍ UJZR'*LcT6ګ3ڔdBEfeJEYR5~3>o9x=ogoz(җ9s -cpy,BV8N`0a0<.ӷ4X2h{"Ij pm\>@8u< H$LLBf23AR?auϧ=ԇ=]|i>ʙF{wwLTztT1u?>:6}Z]KIDAT~m[%;=?&G{?_W#ѵlꇒ}}}):)~ҤB~)GI0J,̙RT,UQVEKGOP9nܭw 2U2MKP˼nBsq?ω䞿ƆNJ=@a X J8^g&N o3٫>Y *0Q'ISpl3﹜=ef0݄nnTLdBʧ$`41H ! 2X"Ca*૳ lg\-rgq-rzlskmwlRO}3EtǺֻ(@L9~[jι?umDݻ}?F5߽jq,W3gSm,Nܛ~{{?uǏl2g:hq)KzeD /M>ϳ? ei+JJb|Ks/S8\ deȮPAQP5J-;ًoNrߞςTP;6&uh.85SwPc/E]iM 6+8Z{E^K49KGR< PHt: 2p4nXjS M&R~>uV|5^[{ |Vxv ~e]I|@|̯#|+RH/Bʋ^2EEH?Bya^;yQCy¯3%[S35i Ҙh $\m~b=^'14f7_|{i9SA8,5s\53U'jf0B@m. wCҼmw~Ys7=SV/T&[H'c ~_FH/i-3\ۆv ZyaǗeR)T*!i >BntJvAC C49I),X#_?9x3-ه2%TE Axs^Kf4-|L%MkӫQO󨹕_J:@t/EFթ:UWjTl-KhI hX4!Xy䇧 @% Žf5ja7 5/i3km7I '-FMu(:]Ѷk`jܶqWE7{'jy]> G K(WPQQ^.P!D.Ik|ؾ%SFC̜ &Rbu-et{y,&O "/l,e/%2`1 XH}Q,9ډR=>r_y`[#0"*nn?W3*t)8]>R. 9xw9: O):$ofB#dT?yv 1{va^?BX,$`Pj_8VG^"cLC287_W.-jRQ<''?}Lu:}ڌYF,v1bj %]ith9X:YXz8aFk??E[,5ZY P\fqjw^wh4ݟ8=iŝnQQƔ˘jWLs Jڎ$*H4TSO4gZtBO | O L`HfL-cf210US>3ql+ +*Y>߲rRN(ItJKb0 >5ϱ=}^O>0N{tl HGqy]`1:wW{Q0 ^^E#ݿ3װt&R)R(W"ʥXf".RճxJ ?OD!sVpD&*X:7Ǹa.fZ]p& /]uy8aYC|61__)%zY/36VZdG4JsEKD5aaqlħqi8tO%7L:jD[>5Go#Q"e1fcHDB(R"P"% #_dPRC1Ik-:k.sMpIv3LI)eW~&(̦1?/Yto:? ?>?ynBZiVd0_O]LZh121oifZ!2+$ 1;MvD5j mH2I#A_(7⣍@C' /婛icH&u63$VF9ͨ.oKy./-qՀRh;0mJԎ‰GHC)o6VyR )RMRQ`KȖV#*P폜FzZI4J RIZJ-JPZe+K:eTRT -@ByF}22Ԏt@6zc/p[8')y/ZXL1"C$r4Ɛ5!mBl6^\4G8 ,YupArB}!sudY8,RkmeY;˲ZXkY%Y4M,3@z!{jej)(c2 <+h!H-QJ*B*)RJ)R*)H%M!e ca"I.؋/xq[ @VR9 }@0KH (Źa 畳Φ‘lR\76"R&@'o !"Osβl-E{ҫ*I }Y+UVնmC&J:JO~ 0AI/@K=iFiR+R*7ZH)T&ZaJ )PE`RI!Ji-З e{}az}S/z}+ZTN*_N8ywmvWUzeunll(ց'|e0?)tIENDB`gpixpod-0.6.2/GPixPod_icon.png0000644000175000017500000002561410507301076015502 0ustar flagarflagarPNG  IHDR>asBIT|dtEXtSoftwarewww.inkscape.org< IDATxyWuVwOO*HlI ~`l 0;LI|I $@IBIcxa0ȫEkGttK2̧oݺ]U[?K3}"I/1aU-٫:Ȝ h6HDD ~|k;I~L kjWK:DjWPwT -\0i Z~ЀڥjUi4Hyলc8+/%HفMV9 \RZz2l.(=f%iL,0G`KZ\Es2 Rsi_u)b0i$JM_YM) "'4X"X1 =G6TG>VLUc L!f&YI  "/vRHJk(_Ցz#"{w_(:@D< +9B۲lCUzRhs `rS^Lk}t2E 8a^[1Ě]Wv^ D 5As/."ߒ:KDd= cVa"m'RY,\` bYNzkT&YIg+JE{ϡ3"R~"!߈7L(*0P{ضM>.UAg .E|"˶Tmٌq؃)t%g۵;bDQ8Xlpv{tVh>Tu똷 GMH=oy16a{=)"$Iߪ[vgq ;1Y`ˈ=15 g-8~>cx~@`e( p|8BTV@gDEsX,aqcuW A:VpWU"c^.ctt4W<3LgD$ |ƲO$EcA68\wc- {@rsuL&}\Ug$ةc޵ؽ{knʋ99SK>K\B5:q)+~#|Ok5H'D"e˖9{DrVo 8 i(q~ߏÄtI4=ӌ G5+zb0cx>#H%~y1)i7~㵯}aKA"իWF4%bG0kqJ1{̸!α F\Fp`E[1V<']ϯd%xrT*/:1`Y֣֭;ŊCFcR( U% @vAXnOqB}.8YB=&2"KZwhԿ7rIL:@㗃!Ua)vyyejzSD,|9a4Mȗ1R)~zLu,en`3K.^{h=}Q06u>[6" Fu:eb#>9F1?|3}ً=T>Ʀ2,wugyNrb#ռER I{-xfcWkQH17ϕW^+{dȖ0)ݻWEBfd%X+pEI|oj7 3LkfYܵjժ?L__H˲He'iNLp_ؽ쟄|eHNK~ XW|qOW)cpP4*5"[nܼy3lѩ<{Zڥ~NW;j>gp0WPq9c^c *Q1[.#䏜UYb~][ujr{^uPn>*2 yj`9}K.i'ˡ*N M"IRzCX-H @Ty0!%SD}w UPQ̆)Ks*?Xε[AyJZtww+lUl:G |4H׆7/38vW|E8Au}Tw4󽐜!;9N.۶[o||v|c|?Qr)Iu6PD!_@;vKy1pEM &dQڊ"g׵XE\';TW뵨^ 4@EՑH7nHP w>f# %c6\ Iy%t)- x^j_q_\!1 $1=Tz>1st7*k_>BBTMܸd(ll}cMԄX|&s%@d7hFͥ|;<t8qtw.K.|_%*)Tc|oAnxH$=o:fQFؾb&6l UB=N`QS &2o62%H}o4oH%G!Pu*^/>[') ;<ۗ.U$l5r&v-K2AuR2/lբ333WkfS[iy&B?ė;h͋:zbf:.9w6s x\.G>'S(m۶}f?O0A&%(!mt*پHjB)x1 A3w}ߋ p3/4@p^ E Z! 8EzVw _y|Ht4F:e],o﹋UºpՌ`1+sW7==-}cgHB`e昇o>k}f¹sÇ9!=r?ѡ H٢by޺{  W vGJSՙj"HUt)Rl|.C$9-t[O6A{Piqk~/GuC49v)&l C mzە%GT:R(kP!*;xL 2=QB~g zp|ϦE ȟpr(Qsӭc糌|vNn7ZDy>$-t,J ï|sX,4@ia0Ş9PJNEB*=<!0Cp$@~w!0Nnjc]Mj _(Xt,B&!0nx{CX8!f =yu )B0N}&ȜGaɑAI7})~/:s&Pa r*[WfDm uJ/L>5Q4wiq?*Ue.5Gr&l2ErfLyS|{y_]TNX׋L(PՌ gNbu`CꍧSv>fn@"=Kz_ޮ\DHt$HtK $gf9dS2ɻo=L}ly9Ut,PaVҫ\7榪{ 'Xw&^RC& D]tO2ȷ'k׿5V:WuvuA6#'>hFџi2c(YܫwXBoBүʩ'9db2Vjn%:9M?:p #<և;Nq%rZ!!1K_"66yx/5^=wOH񣀐[ҳ︛H׺zJVZެ[1]DCGngQ?w@1C h;\v%`ej :%"7?3c{ں='Bȹsה:w᜿S=3>_̨֞՝VQضr`w~N+7׼'l7OU~IمlI_+`8R f?؏Y6=Z5W-o~;/2dE UUD/˧I_C~nSWEy-D"Ml^rZUU^K\`UofF:/\~AFl8wY>@$bՋm"{mu18LPsCs)Qk^4/M vއhQ=͸Ҿ^{߉HIk2](܊w}ʷXV(A&!ى]قȵ\/VXzλd.'ÓOΤ0ut&z+V,CFpw0{w >@.gtxgvo<_sCt N&n:m @X;'O:qm3!f>⌌X@;1P45;Ǟ]kltt_N:nl]t:5xzUv?@&v~Hs:jOם矞3uL}~Q=}YDW T[_$J>ټM:;_i k|Pz.xcf/0D],Svpπ:<əEбцbKh+Z+ל ~׿%s?䟽P{y&'ͻdK24x}LN.~x&af:IwO}}{{VP)7U3 U/_ʋ_m"+W"jT߼ fkTrӻ&F ѧD䧪|4+] e>_v|o>%,@(r3sQSM_:o:(_=D2Vr,9ECqGR:5@I ?ym,]D^͔Qw(ݵCG^)Mn̂W23M|qNg[dh kq6*oqQ`jMwfL՗}C?zv9eh颳"9m/[zܪeB7:᭍W'7lk[&QsiXR;+3qrT X t;'KyH+-lLC|`z9o'Hf5`o&;ڹTT<{[hLNӑX'N`c՚sz5SUS@i 1p7_]4f-`>D!;96y9"0==͉zɆϭR-Y. P%RcM WNF־C/XneC#w,ˢy/8S3qT|!Oww'6lt_V:@f:ckZ*(K/|%Orr7|.ϩ)6t>8~AWwgC}!>-?ޝ魌4ebI| W2nQ[N5n:֜=24 \[ kϲ+bٟbI@Z%ymSܘ\,VYYzU]Kt}{+ J잃 +nX8A IDAT…yA6.!Me6M*X9o-B@WҾk.fߞ],%gfyf`kT7hx8Q@lqC/x(xPVy~j8ΥSOq `nsci=ӳTnNU Zֽl[l̑8\QxoeI l7%budb#YSPƦ]M3;g5}y1" 6C7=<AU5L 3LɝՑ6m%{#~ 6M`_Kʒ@mp6^6)b"lhct`sp(NM~  #ĹCt'p6wR 8 (R&вwY!VRw,D<yqQFk|?1r'[)z.U<=eCɉ)NM׿+I$LkW*S3<1䩖LFm>MJ"4< uM43b\ 1 H(}vS0О'#2ڂgi} $3n1vq1*Ri2tח)p6} RFSC\v94ևqQ 䎝䍲+#]-Jf෹^|CM|4ig~V(F賈 T {T%Fj)kGo{R!fpʶ66Z% hOkYc<ӿBTUJe|\.ޭ_l9"(ӣd"r-+}m (JuL &dv& 8rF`k377G6=koH9cO7F1I{%K}|?#j3X`n6333+@`٬Fi_=|uKT=k;y{_>YPYK l6Ԕڶ ࣥ#w[Iuk?d!$GYHD+力+IQ"b>]:.g6Z/ݴ{o=X|Z0999v}x,/z ˦?uJӢM| 22F(o3CwG(/yyfg2/F*ccc>mթ%(9$h<+Y,'v1FⶌO .!3ᆏo㳿)r{3f fx<:'rb.2aD߄s%RS..Xm@{{ma)mi+xni?m}ϋTFa`N\  $S,-\ uy<3a{[j!f^b{R^?CooSCCC a68=QұHH=l\P}`ލL^$uq5 (2|s8ǿ3IwwwUBu<<.n &Rݯdơ^0.n_LPf\5.#aݕC6m\ Qf]R^KkNH$-edgyl6C9T;MKX4G:!Bv Ks{<piq́ ˸A g>z !Dі![z{4RCp'h{Җ'pDKh` yP =(j#v)WdAmu:jG C, vl:oS*޽{ދY4R-h郇gDqة8NjԳX`݋e5(xL" dZuyq 68MTt{ہVW>`&P/ 4g˝#˽R?7|LJڭpNZˮᷰk^عsg!}HU?]߳=3T?%} Jz[{)mR|K+_Lյho޽ݻFرc_uUeZ`!ɟdff@g+okؗ~n Rjh'ͳos=T, lPj.?p۷o? p5lyW<_YYj~iAgU'$o[f+eZ0,7^{k֬Ywoǎڵk pr*SZc-T 5i? ?숻UF@Wjj 7+.vgֽn߁$Vcj\y5rwtttx0<W fxE-xPZ[ٷ"*{7n9w/#6G~$pR6u0Y!Is[L`s֐t].&7^k @H+|z=3 bQU=a2f qn,Yɇ>o@^.ܑV $`u 'RS; |oy@ThZTz`cy-"z$Ij3s0fY[ㅗEyd`Q( J3I! 8O)ֻ`#b,D+G4SmJs^Ǐ*uAuSx7`g(8$,R& lx XN;wR`Q#ಅT![A{{y<cLATPB"HY+Wd ff,(R& 4`R}xUn`EĈp fC5ܪTgU}3-IH p)b 2\,zT">!*gP `2lESB%PaGFƗVA,BP',P X9T=J'.cFY\U1* -4d YOjwolgiFqãWFGnOe,0|PE9 pU˨NN \c` ` &#T鏏*7Njԟq+wrN :I]Mj/S¨n& KIQwXb0`0,ΐ ȓƘeۿcxXc5x}Dm[ht߀ )dHR I:>LWRJa8vavVIi c 1fgn#Ic Y^7:Z&~WKٳK5Oy+\qCG?~nσl5p|rK&;vGr:_n"KS ~ F3gOq}u,<,JE?[G8xOcU`<˓T'8pЋolq >O\ƻ}\?x,m S`{ -_{+H);VϽ! XAc n,T>^hh4 8\R,aÕZAlV$^Z>y,`rf]p8Z-fu A;h@ @ՂRJ! @R44!/y}F;Bݻ{`XiβAX8nnۣ RIn A@m4Zۯ+v+YP F*DBwK}˛!vF?}GoEὌ$d @PJg]Wi? lLjJ)A0績voUX)UJRB,x1xyo^G2^KL*)ج>z#z>N^m0RJ񘗗ybClEw^4R%5z _B_7|:Wէ˪Tڜ+wy!-Zt#{5LQk:˪w<$1zHxj?mo6IENDB`gpixpod-0.6.2/photoalbum.png0000644000175000017500000002415110507301076015325 0ustar flagarflagarPNG  IHDR>abKGD pHYs B(xtIME  ;wW IDATxYdYz-=+%FVό,[`eFE` ,lm e^KG_~}'3'G &*2 Z pVk}?tv$Go=n#{OgzR^J-x4͗ (K)ӛR(# Ү *3P0l!vfLp̨~ŲYm[XiH)s>her/"Q~t:nW&>9&RR*^*RDPq;+LY4K`8c}3aH(ODjvT;JV{ZJJBh@xgX؄F#峔Of ) ("`D ?R*^VziT*haB" (MZ8q8G@6;;ul0 }5A{JIN DBJk+!s-Ialu'^i=o"to1$KA !3Dz0 Ð|Wk_6l,ʿe^]bvffCT\Z*f&[;ln"R!g ޻|kW0LAC+ˬOGfL;ia0KU R ˲^<ό~V"PxsLW0 0ElP Q2 LS05UezƳϞ1lfynsc繭φA|7Z>srt/ :kT*n_7^[Fѹ}{РGC?X7X;2 l$SO!>s!0Qv6r}czv(ƭ>J1*ڧGBD#o=y4}f!3@eiRK !֯YɹȎ4cHct V~:RZkx8h~)e_K_(ByS=9ўx)e('x_|*S@a`˞ _? @+e0 NS܀98{'EۤhG(&+<:@p6w G-7$Qpy8I =R ÐJh4)|5~)qUC%TNߧ'MCRMI6):Eb~+_Wo5"@1'@Ye<{e!`}I@A"9<h4/3h}C֠Tl5QT$NT'${=^>XubYXG0;r,`') L Ʌ0ХŶmS?7y x\.8VaD` Ǝȴti~igrf8Ѐ"Ru挌E4G>%>+< KR*eY@ڹc9Ca41->rIC ]PH"H#S~Z - N(I9G/h2ΞX?Q>"L 2^c9q9vN`h,0{E>Hh<R1Lɵes/DL+YQ4yChIIJbC4#iqG'(s1|qaG." D^ʬGo1 JRhZK)Y;/@p'aȵ{XbUP*L1t`Ҕp XGGu눡^ $;bey+r7`߈Oө@:%h0 J_-OQu44ay}>Cۨ`$+rMRrMD{2LP+%R~*g});єw&Vfx S:S UD\((T$q1L~fZ.}ŲmuݞQ 0RcYD"!L CL'Љ M~ @imʕ#pe% @ 05iluR0|j0@Rfu"ZS(faP،$qammP\>Q?|'s=gh[䕞\.# R2= 38ap}v]Y@ ="vŠԙA0LhlJ#Tƨ@80LCF)пbK3 ;NT(?6`h1t2D9'^r HIHiP_qlVW%8=?*$`=vw{H2>7D"S?H-P }M|ԔS@u4,*<!Jы;/sRߋcӣ;3E1_q (_g^E=XyX)&.bFNZ@)m~FNvY(fF2GrhpCB1k^Gn+rhSr1FdDy1( @% 8`8%9 9M2y!~& $lnZ[;Y?MK89^/M6C%!cbDj@d~pDI8!f+dgGElR(JܢɹN4sۊw@b>l.1Q|:::as jO:"05m%]Z=m8g<8RfݾG)L .! S~mŇC9'U).^X1@'=~b簻ץRaa=1=>R,iHvc ˏ ;;7PxI.90-8TZ,. !pl Sfu}doRo\a5S>){dr_a@!sJחL&~sogog#'39B BH.-G#F?acƹv?Hn 4aAC1(3 7Уp26|ah-Co;*&hm㵶[[M&}<%P, @d2 1,C%iO3jTUL@R\K8H@nHA"h7Y_ +2N$¶-Q XkܾGak/ G~NJHaD+bP eۊl^Dt{Xۄn'U*SKg258G:=<_# &pDi>pEdJRHSMdJ!ۉV! v<%0uC5I(CPn}U>`tSj̼ К'|ڥ2Z "L#A룔@4ZL` 2IZRDaRp l k@G_yieca*z.v';H O&qЉV5^tw!Ѧ9d)D(k8FO` %`bg\qJ/Z4afnr4{}Vm8F1L0H0 VXXC "K+ lopcAADRdqa:#C `2=@P(G&x 8 8278Db&˗RGV~} X-@!`na:wnckq8p*sڅeΘI S}", hޝ68ҌaiX S 1 pbK+ '&מZp;df``2CAhu@B@?@Ch%mLkːp)@O1;wzNMcw5"T/@=< D8ױ 8xG5;at"cR :Rr8tGLj%+z8tdlgSǕWNUTk~<wc_~yj!kkI03 ď-8Qiep*PZtgtbP7;C<qdpXfw`\RǑ3s/>XVzn;7פaQ̱tfr0`4D/yOJ!nO$a$}Э#D7F{F'Fhme0l# 0L#7'pqQ,X=>gh5wmBH^[&^)U:8fK+2j><#áVB:f B\cюsgtIw)hp0eӒ#@Rc1#VY<;:c @q 8 0 mY4iٙv5QL¾JaSt+pdL A_3vdJ.a"z%ld ASrp\8z7UZ}n^bYTVdjq 6l@=dYƱX)3GzXN7YYdܑxEL@ ;t%DB"Nt!bưPr-~[@+40B-v陻cpcKBfqp䔰Sp4}x>B>uvgϏc3]r}:$@/Pc ,4PVS6k[U A Sc^ kc1S3#ÔVwX{ɧGOvT?);0OZ@,ȂW(J9l [>VX~@֍R~l7bjz<9\He"0Z=̲,:`?iK,xȂwH-f`H x.w=9s2" 1 \/8r$KbIBci`Xvڝw(dW>2ՙWR&@ Ʊ-~xb 5v:=\lwE+( ض}0c}AwhS;r \zEM2GQeYJ%JbH@Pct%`CM4; +Q(,ia:zlW Q 0#?2i>*`lsP%1M3wXa)c1@q"&tL $|?@jV/1Q~eR'Ga8O^c&Ν]ĊlqжB*jjݡv{T-C R,.1kS4( BPEC>  ^}؍ݽPi5Lj |8p\ t{} Bt;=A SO]Ô,]>B'0 (;O/˃۹9mQWOU+Tk*N`[Q0Ct'ɔ)0X~skG$+A.@:]/w-mgj&=V{\N.U+@SUJ%'@ Ҷ B@c AgqƱq%I][P;_ʟxG/8W> i4Z==-ܼe[Tb`ԧ"PLMU)xdYC.D1(7>N ɴV^yhBHkn߼34W߹*R7_TP,Ε$;(Z0n߸h;6j SxEǤT0$^C\/E@{t`X$,2΍fmcWpԑRrPw֘3a3=3k3jgFh0=Ѡm;6zQ.9 傕,u 78 s ' /{w) TkǢnOVcqa ۶u{^FD M 7C27]bn: 4v>ۭMn\,نSp+LOGVrɦZHwAX a}S!.h<1+n?nlߺ|nBTԋԋp.LZm7bV&7>4 S5fgkT)jezN"ڥt݀S ˿>11taȵwySSQ~{wnR6(bs1&*EaDTT`@} _uE'ZnL=F0Uqm-7:I}!8,,vX6 gY^]<е<ɽF87(Nmn1NDyZšVq:xc{}߿Ƴ;IDATUf1WZ- ޱm|ïGZ4-V-?2wsN/E-fw%2ɣ T|{7̉)9j٦Z9τvzl.CV@##GA\ $ཏ(WJ,,15SOy(h6ZloCi47n=`vg92ā8Ihxܾ]`ah~ pUh=ALgˍJ5Jt<~^^s;0s?U\_]_|G'Yѷonv:q\QJrX!D46ٶoR9˟^_xEf{*p]uã<ޣF1|̀ { Fh UgvŧsNiesp%2yp3al{VGaN^<|e:A@46C| ^-v#.´K++թN~޴J ^ t2}|q{JbU@S7߿ꫯΏ;]ץnl6iZtaxeHײOXt]XƐς {ۈ^W[0^L(/~q2rv yZ3A3Ah[$0M~?yTe7Mަhu:w񽌲tD2@09~sk.֟B>GMs~ۗ ~njjWϞ=j&llllxwuhTfCV0C@o0W28K_k=.H x N}&rg'h"D&2Ld"D&2Ld"D&2Ld"ȣRN9GIENDB`gpixpod-0.6.2/PKG-INFO0000644000175000017500000000037510513202110013526 0ustar flagarflagarMetadata-Version: 1.0 Name: gpixpod Version: 0.6.2 Summary: GPixPod - organize photos on your iPod, freely! Home-page: http://www.gpixpod.org Author: Flavio Gargiulo Author-email: flagar@gmail.com License: UNKNOWN Description: UNKNOWN Platform: UNKNOWN