aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorGravatar Alex Bennee <alex@bennee.com>2010-07-14 15:57:06 +0100
committerGravatar Alex Bennee <alex@bennee.com>2010-07-14 15:57:06 +0100
commitba15707b292d827bdce732e7713b26fae3f75c74 (patch)
tree8159d5daa4588ade1e71b9cb8541d6438c6b80c6 /src
EasyTag 2.1.1
Diffstat (limited to 'src')
-rwxr-xr-xsrc/Makefile.am84
-rw-r--r--src/Makefile.in710
-rwxr-xr-xsrc/Makefile.mingw178
-rwxr-xr-xsrc/about.c536
-rwxr-xr-xsrc/about.h33
-rwxr-xr-xsrc/ape_tag.c342
-rwxr-xr-xsrc/ape_tag.h40
-rwxr-xr-xsrc/bar.c482
-rwxr-xr-xsrc/bar.h165
-rwxr-xr-xsrc/browser.c4084
-rwxr-xr-xsrc/browser.h178
-rwxr-xr-xsrc/cddb.c4105
-rw-r--r--src/cddb.h130
-rwxr-xr-xsrc/charset.c824
-rwxr-xr-xsrc/charset.h72
-rwxr-xr-xsrc/crc32.c130
-rwxr-xr-xsrc/crc32.h98
-rwxr-xr-xsrc/dlm.c90
-rwxr-xr-xsrc/dlm.h8
-rwxr-xr-xsrc/easytag.c4612
-rwxr-xr-xsrc/easytag.h206
-rwxr-xr-xsrc/et_core.c4835
-rwxr-xr-xsrc/et_core.h469
-rwxr-xr-xsrc/flac_header.c335
-rwxr-xr-xsrc/flac_header.h41
-rwxr-xr-xsrc/flac_tag.c1029
-rwxr-xr-xsrc/flac_tag.h43
-rwxr-xr-xsrc/genres.h195
-rwxr-xr-xsrc/id3_tag.c1362
-rwxr-xr-xsrc/id3_tag.h47
-rwxr-xr-xsrc/id3lib/Makefile.am14
-rw-r--r--src/id3lib/Makefile.in466
-rwxr-xr-xsrc/id3lib/c_wrapper.cpp141
-rwxr-xr-xsrc/id3lib/id3_bugfix.h56
-rwxr-xr-xsrc/id3lib/patch_id3lib_3.8.3_UTF16_writing_bug.diff39
-rwxr-xr-xsrc/id3v24_tag.c1499
-rwxr-xr-xsrc/libapetag/COPYING.LGPL510
-rwxr-xr-xsrc/libapetag/Makefile.am22
-rw-r--r--src/libapetag/Makefile.in471
-rwxr-xr-xsrc/libapetag/README.apetag12
-rwxr-xr-xsrc/libapetag/apetaglib.c1065
-rwxr-xr-xsrc/libapetag/apetaglib.h277
-rwxr-xr-xsrc/libapetag/id3v2_read.c385
-rwxr-xr-xsrc/libapetag/id3v2_read.h42
-rwxr-xr-xsrc/libapetag/info_mac.c151
-rwxr-xr-xsrc/libapetag/info_mac.h96
-rwxr-xr-xsrc/libapetag/info_mpc.c198
-rwxr-xr-xsrc/libapetag/info_mpc.h96
-rwxr-xr-xsrc/libapetag/is_tag.c175
-rwxr-xr-xsrc/libapetag/is_tag.h41
-rwxr-xr-xsrc/libmpg123/Makefile.am24
-rw-r--r--src/libmpg123/Makefile.in476
-rw-r--r--src/libmpg123/README35
-rwxr-xr-xsrc/libmpg123/common.c746
-rwxr-xr-xsrc/libmpg123/dxhead.c165
-rwxr-xr-xsrc/libmpg123/dxhead.h60
-rwxr-xr-xsrc/libmpg123/getbits.c126
-rwxr-xr-xsrc/libmpg123/getbits.h46
-rwxr-xr-xsrc/libmpg123/huffman.h329
-rwxr-xr-xsrc/libmpg123/l2tables.h997
-rwxr-xr-xsrc/libmpg123/layer1.c187
-rwxr-xr-xsrc/libmpg123/layer2.c336
-rwxr-xr-xsrc/libmpg123/layer3.c2100
-rwxr-xr-xsrc/libmpg123/mpg123.c172
-rwxr-xr-xsrc/libmpg123/mpg123.h144
-rwxr-xr-xsrc/log.c307
-rw-r--r--src/log.h42
-rwxr-xr-xsrc/misc.c3392
-rwxr-xr-xsrc/misc.h122
-rwxr-xr-xsrc/monkeyaudio_header.c122
-rwxr-xr-xsrc/monkeyaudio_header.h42
-rwxr-xr-xsrc/mp4_header.c315
-rwxr-xr-xsrc/mp4_header.h42
-rwxr-xr-xsrc/mp4_tag.c429
-rwxr-xr-xsrc/mp4_tag.h41
-rwxr-xr-xsrc/mpeg_header.c401
-rwxr-xr-xsrc/mpeg_header.h41
-rwxr-xr-xsrc/msgbox.c312
-rwxr-xr-xsrc/msgbox.h103
-rwxr-xr-xsrc/musepack_header.c125
-rwxr-xr-xsrc/musepack_header.h42
-rwxr-xr-xsrc/ogg_header.c293
-rwxr-xr-xsrc/ogg_header.h44
-rwxr-xr-xsrc/ogg_tag.c836
-rwxr-xr-xsrc/ogg_tag.h42
-rwxr-xr-xsrc/picture.c1174
-rwxr-xr-xsrc/picture.h125
-rwxr-xr-xsrc/prefs.c1857
-rwxr-xr-xsrc/prefs.h182
-rwxr-xr-xsrc/scan.c3455
-rwxr-xr-xsrc/scan.h89
-rwxr-xr-xsrc/setting.c1616
-rwxr-xr-xsrc/setting.h375
-rwxr-xr-xsrc/ui_manager.h325
-rwxr-xr-xsrc/vcedit.c629
-rwxr-xr-xsrc/vcedit.h75
-rwxr-xr-xsrc/wavpack_header.c114
-rwxr-xr-xsrc/wavpack_header.h41
-rwxr-xr-xsrc/wavpack_tag.c400
-rwxr-xr-xsrc/wavpack_tag.h42
-rwxr-xr-xsrc/win32/easytag.rc4
-rwxr-xr-xsrc/win32/resource.h1
-rwxr-xr-xsrc/win32/win32dep.c493
-rwxr-xr-xsrc/win32/win32dep.h84
-rwxr-xr-xsrc/win32/win_easytag.c557
105 files changed, 55893 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100755
index 0000000..9383d83
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,84 @@
+CFLAGS = @CFLAGS@ @GTK_CFLAGS@
+
+SUBDIRS = libmpg123 libapetag id3lib
+
+bin_PROGRAMS = easytag
+
+easytag_SOURCES = \
+ about.c \
+ about.h \
+ ape_tag.c \
+ ape_tag.h \
+ bar.c \
+ bar.h \
+ browser.c \
+ browser.h \
+ cddb.c \
+ cddb.h \
+ charset.c \
+ charset.h \
+ crc32.c \
+ crc32.h \
+ dlm.c \
+ dlm.h \
+ easytag.c \
+ easytag.h \
+ et_core.c \
+ et_core.h \
+ flac_header.c \
+ flac_header.h \
+ flac_tag.c \
+ flac_tag.h \
+ genres.h \
+ id3_tag.c \
+ id3_tag.h \
+ id3v24_tag.c \
+ log.c \
+ log.h \
+ misc.c \
+ misc.h \
+ monkeyaudio_header.c \
+ monkeyaudio_header.h \
+ mpeg_header.c \
+ mpeg_header.h \
+ mp4_header.c \
+ mp4_header.h \
+ mp4_tag.c \
+ mp4_tag.h \
+ musepack_header.c \
+ musepack_header.h \
+ msgbox.c \
+ msgbox.h \
+ ogg_header.c \
+ ogg_header.h \
+ ogg_tag.c \
+ ogg_tag.h \
+ picture.c \
+ picture.h \
+ prefs.c \
+ prefs.h \
+ scan.c \
+ scan.h \
+ setting.c \
+ setting.h \
+ ui_manager.h \
+ vcedit.c \
+ vcedit.h \
+ wavpack_header.c \
+ wavpack_header.h \
+ wavpack_tag.c \
+ wavpack_tag.h
+
+EXTRA_DIST = \
+ Makefile.mingw \
+ win32/easytag.rc \
+ win32/resource.h \
+ win32/win32dep.c \
+ win32/win32dep.h \
+ win32/win_easytag.c
+
+easytag_LDADD = $(GTK_LIBS) libmpg123/libmpg123.a libapetag/libapetag.a id3lib/libid3bugfix.a
+
+localedir = $(datadir)/locale
+PACKAGE_DATA_DIR = $(datadir)/$(PACKAGE)
+INCLUDES = -DLOCALE=\"$(localedir)\" -DPACKAGE_DATA_DIR=\"$(PACKAGE_DATA_DIR)\"
diff --git a/src/Makefile.in b/src/Makefile.in
new file mode 100644
index 0000000..ec0e58d
--- /dev/null
+++ b/src/Makefile.in
@@ -0,0 +1,710 @@
+# Makefile.in generated by automake 1.9.6 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005 Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+top_builddir = ..
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+INSTALL = @INSTALL@
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+bin_PROGRAMS = easytag$(EXEEXT)
+subdir = src
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/configure.in
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+am__installdirs = "$(DESTDIR)$(bindir)"
+binPROGRAMS_INSTALL = $(INSTALL_PROGRAM)
+PROGRAMS = $(bin_PROGRAMS)
+am_easytag_OBJECTS = about.$(OBJEXT) ape_tag.$(OBJEXT) bar.$(OBJEXT) \
+ browser.$(OBJEXT) cddb.$(OBJEXT) charset.$(OBJEXT) \
+ crc32.$(OBJEXT) dlm.$(OBJEXT) easytag.$(OBJEXT) \
+ et_core.$(OBJEXT) flac_header.$(OBJEXT) flac_tag.$(OBJEXT) \
+ id3_tag.$(OBJEXT) id3v24_tag.$(OBJEXT) log.$(OBJEXT) \
+ misc.$(OBJEXT) monkeyaudio_header.$(OBJEXT) \
+ mpeg_header.$(OBJEXT) mp4_header.$(OBJEXT) mp4_tag.$(OBJEXT) \
+ musepack_header.$(OBJEXT) msgbox.$(OBJEXT) \
+ ogg_header.$(OBJEXT) ogg_tag.$(OBJEXT) picture.$(OBJEXT) \
+ prefs.$(OBJEXT) scan.$(OBJEXT) setting.$(OBJEXT) \
+ vcedit.$(OBJEXT) wavpack_header.$(OBJEXT) \
+ wavpack_tag.$(OBJEXT)
+easytag_OBJECTS = $(am_easytag_OBJECTS)
+am__DEPENDENCIES_1 =
+easytag_DEPENDENCIES = $(am__DEPENDENCIES_1) libmpg123/libmpg123.a \
+ libapetag/libapetag.a id3lib/libid3bugfix.a
+DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(LIBTOOL) --tag=CC --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+SOURCES = $(easytag_SOURCES)
+DIST_SOURCES = $(easytag_SOURCES)
+RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \
+ html-recursive info-recursive install-data-recursive \
+ install-exec-recursive install-info-recursive \
+ install-recursive installcheck-recursive installdirs-recursive \
+ pdf-recursive ps-recursive uninstall-info-recursive \
+ uninstall-recursive
+ETAGS = etags
+CTAGS = ctags
+DIST_SUBDIRS = $(SUBDIRS)
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMDEP_FALSE = @AMDEP_FALSE@
+AMDEP_TRUE = @AMDEP_TRUE@
+AMTAR = @AMTAR@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@ @GTK_CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+ECHO = @ECHO@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+F77 = @F77@
+FFLAGS = @FFLAGS@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GOBJECT_QUERY = @GOBJECT_QUERY@
+GTK_CFLAGS = @GTK_CFLAGS@
+GTK_LIBS = @GTK_LIBS@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAINTAINER_MODE_FALSE = @MAINTAINER_MODE_FALSE@
+MAINTAINER_MODE_TRUE = @MAINTAINER_MODE_TRUE@
+MAKEINFO = @MAKEINFO@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MSGFMT = @MSGFMT@
+OBJEXT = @OBJEXT@
+OGG_CFLAGS = @OGG_CFLAGS@
+OGG_LIBS = @OGG_LIBS@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PKG_CONFIG = @PKG_CONFIG@
+POFILES = @POFILES@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+RANLIB = @RANLIB@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+VORBIS_CFLAGS = @VORBIS_CFLAGS@
+VORBIS_LIBS = @VORBIS_LIBS@
+WAVPACK_CFLAGS = @WAVPACK_CFLAGS@
+WAVPACK_LIBS = @WAVPACK_LIBS@
+XGETTEXT = @XGETTEXT@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_F77 = @ac_ct_F77@
+ac_ct_RANLIB = @ac_ct_RANLIB@
+ac_ct_STRIP = @ac_ct_STRIP@
+ac_pt_PKG_CONFIG = @ac_pt_PKG_CONFIG@
+am__fastdepCC_FALSE = @am__fastdepCC_FALSE@
+am__fastdepCC_TRUE = @am__fastdepCC_TRUE@
+am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@
+am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+datadir = @datadir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+SUBDIRS = libmpg123 libapetag id3lib
+easytag_SOURCES = \
+ about.c \
+ about.h \
+ ape_tag.c \
+ ape_tag.h \
+ bar.c \
+ bar.h \
+ browser.c \
+ browser.h \
+ cddb.c \
+ cddb.h \
+ charset.c \
+ charset.h \
+ crc32.c \
+ crc32.h \
+ dlm.c \
+ dlm.h \
+ easytag.c \
+ easytag.h \
+ et_core.c \
+ et_core.h \
+ flac_header.c \
+ flac_header.h \
+ flac_tag.c \
+ flac_tag.h \
+ genres.h \
+ id3_tag.c \
+ id3_tag.h \
+ id3v24_tag.c \
+ log.c \
+ log.h \
+ misc.c \
+ misc.h \
+ monkeyaudio_header.c \
+ monkeyaudio_header.h \
+ mpeg_header.c \
+ mpeg_header.h \
+ mp4_header.c \
+ mp4_header.h \
+ mp4_tag.c \
+ mp4_tag.h \
+ musepack_header.c \
+ musepack_header.h \
+ msgbox.c \
+ msgbox.h \
+ ogg_header.c \
+ ogg_header.h \
+ ogg_tag.c \
+ ogg_tag.h \
+ picture.c \
+ picture.h \
+ prefs.c \
+ prefs.h \
+ scan.c \
+ scan.h \
+ setting.c \
+ setting.h \
+ ui_manager.h \
+ vcedit.c \
+ vcedit.h \
+ wavpack_header.c \
+ wavpack_header.h \
+ wavpack_tag.c \
+ wavpack_tag.h
+
+EXTRA_DIST = \
+ Makefile.mingw \
+ win32/easytag.rc \
+ win32/resource.h \
+ win32/win32dep.c \
+ win32/win32dep.h \
+ win32/win_easytag.c
+
+easytag_LDADD = $(GTK_LIBS) libmpg123/libmpg123.a libapetag/libapetag.a id3lib/libid3bugfix.a
+localedir = $(datadir)/locale
+PACKAGE_DATA_DIR = $(datadir)/$(PACKAGE)
+INCLUDES = -DLOCALE=\"$(localedir)\" -DPACKAGE_DATA_DIR=\"$(PACKAGE_DATA_DIR)\"
+all: all-recursive
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
+ && exit 0; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/Makefile'; \
+ cd $(top_srcdir) && \
+ $(AUTOMAKE) --foreign src/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+install-binPROGRAMS: $(bin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ test -z "$(bindir)" || $(mkdir_p) "$(DESTDIR)$(bindir)"
+ @list='$(bin_PROGRAMS)'; for p in $$list; do \
+ p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \
+ if test -f $$p \
+ || test -f $$p1 \
+ ; then \
+ f=`echo "$$p1" | sed 's,^.*/,,;$(transform);s/$$/$(EXEEXT)/'`; \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) --mode=install $(binPROGRAMS_INSTALL) '$$p' '$(DESTDIR)$(bindir)/$$f'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) --mode=install $(binPROGRAMS_INSTALL) "$$p" "$(DESTDIR)$(bindir)/$$f" || exit 1; \
+ else :; fi; \
+ done
+
+uninstall-binPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(bin_PROGRAMS)'; for p in $$list; do \
+ f=`echo "$$p" | sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \
+ echo " rm -f '$(DESTDIR)$(bindir)/$$f'"; \
+ rm -f "$(DESTDIR)$(bindir)/$$f"; \
+ done
+
+clean-binPROGRAMS:
+ @list='$(bin_PROGRAMS)'; for p in $$list; do \
+ f=`echo $$p|sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f $$p $$f"; \
+ rm -f $$p $$f ; \
+ done
+easytag$(EXEEXT): $(easytag_OBJECTS) $(easytag_DEPENDENCIES)
+ @rm -f easytag$(EXEEXT)
+ $(LINK) $(easytag_LDFLAGS) $(easytag_OBJECTS) $(easytag_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/about.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ape_tag.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bar.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/browser.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cddb.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/charset.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/crc32.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dlm.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/easytag.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/et_core.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/flac_header.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/flac_tag.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/id3_tag.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/id3v24_tag.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/log.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/misc.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/monkeyaudio_header.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mp4_header.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mp4_tag.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mpeg_header.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/msgbox.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/musepack_header.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ogg_header.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ogg_tag.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/picture.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/prefs.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/scan.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/setting.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vcedit.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wavpack_header.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wavpack_tag.Po@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ if $(LTCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Plo"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+distclean-libtool:
+ -rm -f libtool
+uninstall-info-am:
+
+# This directory's subdirectories are mostly independent; you can cd
+# into them and run `make' without going through this Makefile.
+# To change the values of `make' variables: instead of editing Makefiles,
+# (1) if the variable is set in `config.status', edit `config.status'
+# (which will cause the Makefiles to be regenerated when you run `make');
+# (2) otherwise, pass the desired values on the `make' command line.
+$(RECURSIVE_TARGETS):
+ @failcom='exit 1'; \
+ for f in x $$MAKEFLAGS; do \
+ case $$f in \
+ *=* | --[!k]*);; \
+ *k*) failcom='fail=yes';; \
+ esac; \
+ done; \
+ dot_seen=no; \
+ target=`echo $@ | sed s/-recursive//`; \
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ echo "Making $$target in $$subdir"; \
+ if test "$$subdir" = "."; then \
+ dot_seen=yes; \
+ local_target="$$target-am"; \
+ else \
+ local_target="$$target"; \
+ fi; \
+ (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+ || eval $$failcom; \
+ done; \
+ if test "$$dot_seen" = "no"; then \
+ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
+ fi; test -z "$$fail"
+
+mostlyclean-recursive clean-recursive distclean-recursive \
+maintainer-clean-recursive:
+ @failcom='exit 1'; \
+ for f in x $$MAKEFLAGS; do \
+ case $$f in \
+ *=* | --[!k]*);; \
+ *k*) failcom='fail=yes';; \
+ esac; \
+ done; \
+ dot_seen=no; \
+ case "$@" in \
+ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
+ *) list='$(SUBDIRS)' ;; \
+ esac; \
+ rev=''; for subdir in $$list; do \
+ if test "$$subdir" = "."; then :; else \
+ rev="$$subdir $$rev"; \
+ fi; \
+ done; \
+ rev="$$rev ."; \
+ target=`echo $@ | sed s/-recursive//`; \
+ for subdir in $$rev; do \
+ echo "Making $$target in $$subdir"; \
+ if test "$$subdir" = "."; then \
+ local_target="$$target-am"; \
+ else \
+ local_target="$$target"; \
+ fi; \
+ (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+ || eval $$failcom; \
+ done && test -z "$$fail"
+tags-recursive:
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \
+ done
+ctags-recursive:
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \
+ done
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
+ include_option=--etags-include; \
+ empty_fix=.; \
+ else \
+ include_option=--include; \
+ empty_fix=; \
+ fi; \
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ test ! -f $$subdir/TAGS || \
+ tags="$$tags $$include_option=$$here/$$subdir/TAGS"; \
+ fi; \
+ done; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$tags $$unique; \
+ fi
+ctags: CTAGS
+CTAGS: ctags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ test -z "$(CTAGS_ARGS)$$tags$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$tags $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && cd $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) $$here
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ $(mkdir_p) $(distdir)/win32
+ @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \
+ list='$(DISTFILES)'; for file in $$list; do \
+ case $$file in \
+ $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \
+ $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \
+ esac; \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test "$$dir" != "$$file" && test "$$dir" != "."; then \
+ dir="/$$dir"; \
+ $(mkdir_p) "$(distdir)$$dir"; \
+ else \
+ dir=''; \
+ fi; \
+ if test -d $$d/$$file; then \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
+ fi; \
+ cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
+ else \
+ test -f $(distdir)/$$file \
+ || cp -p $$d/$$file $(distdir)/$$file \
+ || exit 1; \
+ fi; \
+ done
+ list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ test -d "$(distdir)/$$subdir" \
+ || $(mkdir_p) "$(distdir)/$$subdir" \
+ || exit 1; \
+ distdir=`$(am__cd) $(distdir) && pwd`; \
+ top_distdir=`$(am__cd) $(top_distdir) && pwd`; \
+ (cd $$subdir && \
+ $(MAKE) $(AM_MAKEFLAGS) \
+ top_distdir="$$top_distdir" \
+ distdir="$$distdir/$$subdir" \
+ distdir) \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-recursive
+all-am: Makefile $(PROGRAMS)
+installdirs: installdirs-recursive
+installdirs-am:
+ for dir in "$(DESTDIR)$(bindir)"; do \
+ test -z "$$dir" || $(mkdir_p) "$$dir"; \
+ done
+install: install-recursive
+install-exec: install-exec-recursive
+install-data: install-data-recursive
+uninstall: uninstall-recursive
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-recursive
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-recursive
+
+clean-am: clean-binPROGRAMS clean-generic clean-libtool mostlyclean-am
+
+distclean: distclean-recursive
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-libtool distclean-tags
+
+dvi: dvi-recursive
+
+dvi-am:
+
+html: html-recursive
+
+info: info-recursive
+
+info-am:
+
+install-data-am:
+
+install-exec-am: install-binPROGRAMS
+
+install-info: install-info-recursive
+
+install-man:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-recursive
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-recursive
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-recursive
+
+pdf-am:
+
+ps: ps-recursive
+
+ps-am:
+
+uninstall-am: uninstall-binPROGRAMS uninstall-info-am
+
+uninstall-info: uninstall-info-recursive
+
+.PHONY: $(RECURSIVE_TARGETS) CTAGS GTAGS all all-am check check-am \
+ clean clean-binPROGRAMS clean-generic clean-libtool \
+ clean-recursive ctags ctags-recursive distclean \
+ distclean-compile distclean-generic distclean-libtool \
+ distclean-recursive distclean-tags distdir dvi dvi-am html \
+ html-am info info-am install install-am install-binPROGRAMS \
+ install-data install-data-am install-exec install-exec-am \
+ install-info install-info-am install-man install-strip \
+ installcheck installcheck-am installdirs installdirs-am \
+ maintainer-clean maintainer-clean-generic \
+ maintainer-clean-recursive mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool mostlyclean-recursive \
+ pdf pdf-am ps ps-am tags tags-recursive uninstall uninstall-am \
+ uninstall-binPROGRAMS uninstall-info-am
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/src/Makefile.mingw b/src/Makefile.mingw
new file mode 100755
index 0000000..aede25d
--- /dev/null
+++ b/src/Makefile.mingw
@@ -0,0 +1,178 @@
+#
+# Makefile.mingw
+#
+# Description: Makefile for win32 (mingw) version of EasyTAG
+#
+
+#
+# PATHS
+#
+
+INCLUDE_DIR := .
+EASYTAG_TOP := ..
+EASYTAG_SRC := .
+EASYTAG_INSTALL_DIR := $(EASYTAG_TOP)/win32-install-dir
+
+##
+## VARIABLE DEFINITIONS
+##
+
+CPP = g++
+CC = gcc.exe
+
+TARGET = easytag
+
+# Compiler and Linker Options
+
+CFLAGS += -g -O2 -mms-bitfields
+DEFINES += -DHAVE_GTK -DHAVE_CONFIG_H
+LDFLAGS = -mwindows
+
+WINDRES := windres
+
+##
+## INCLUDE PATHS
+##
+
+INCLUDE_PATHS = -I$(INCLUDE_DIR) \
+ -I$(INCLUDE_DIR)/win32 \
+ -I$(EASYTAG_TOP) \
+ `pkg-config --cflags gtk+-2.0` \
+ -I/local/include
+
+
+
+LIB_PATHS = `pkg-config --libs gtk+-2.0` \
+ `pkg-config --libs vorbisfile` \
+ -L$(EASYTAG_SRC) \
+ -L/local/lib \
+ -lflac \
+ -lmp4v2 \
+ -lid3 \
+ -lid3tag \
+ -liconv \
+ -lstdc++ \
+ -lz \
+ -lwavpack \
+ -lws2_32
+
+
+##
+## SOURCES, OBJECTS
+##
+
+RC_SRC = win32/easytag.rc
+
+DLL_C_SRC = about.c \
+ ape_tag.c \
+ bar.c \
+ browser.c \
+ charset.c \
+ cddb.c \
+ crc32.c \
+ dlm.c \
+ easytag.c \
+ et_core.c \
+ flac_header.c \
+ flac_tag.c \
+ id3_tag.c \
+ id3v24_tag.c \
+ id3lib/c_wrapper.c \
+ libapetag/apetaglib.c \
+ libapetag/info_mac.c \
+ libapetag/info_mpc.c \
+ libapetag/is_tag.c \
+ libmpg123/common.c \
+ libmpg123/dxhead.c \
+ libmpg123/getbits.c \
+ libmpg123/layer1.c \
+ libmpg123/layer2.c \
+ libmpg123/layer3.c \
+ libmpg123/mpg123.c \
+ log.c \
+ misc.c \
+ monkeyaudio_header.c \
+ mp4_header.c \
+ mp4_tag.c \
+ mpeg_header.c \
+ msgbox.c \
+ musepack_header.c \
+ ogg_header.c \
+ ogg_tag.c \
+ picture.c \
+ prefs.c \
+ scan.c \
+ setting.c \
+ vcedit.c \
+ wavpack_header.c \
+ wavpack_tag.c \
+ win32/win32dep.c
+
+DLL_OBJECTS = $(DLL_C_SRC:%.c=%.o) $(RC_SRC:%.rc=%.o)
+
+EXE_C_SRC = win32/win_easytag.c
+
+
+
+EXE_OBJECTS = $(EXE_C_SRC:%.c=%.o) $(RC_SRC:%.rc=%.o)
+
+
+
+##
+## LIBRARIES
+##
+
+
+
+##
+## RULES
+##
+
+# How to make a C file
+%.o: %.cpp
+ $(CPP) $(CFLAGS) $(INCLUDE_PATHS) $(DEFINES) -c $< -o $@
+
+
+# How to make a C file
+%.o: %.c
+ $(CC) $(CFLAGS) $(INCLUDE_PATHS) $(DEFINES) -c $< -o $@
+
+# How to make an RC file
+%.o: %.rc
+ $(WINDRES) -i $< -o $@
+
+##
+## TARGET DEFINITIONS
+##
+
+.PHONY: all clean
+
+all: $(TARGET).dll $(TARGET).exe
+
+install: all
+ cp $(EASYTAG_SRC)/easytag.exe $(EASYTAG_SRC)/easytag.dll $(EASYTAG_INSTALL_DIR)
+ strip -s $(EASYTAG_INSTALL_DIR)/easytag.*
+
+#
+# BUILD DLL
+#
+
+$(TARGET).lib $(TARGET).dll: $(DLL_OBJECTS)
+ $(CC) -shared $(DLL_OBJECTS) $(LIB_PATHS) $(DLL_LIBS) $(DLL_LD_FLAGS) -Wl,--out-implib,$(TARGET).lib -o $(TARGET).dll
+
+#
+# BUILD EXE
+#
+
+$(TARGET).exe: $(TARGET.dll) $(EXE_OBJECTS)
+ $(CC) $(LDFLAGS) $(EXE_OBJECTS) $(LIB_PATHS) $(EXE_LIBS) -o $(TARGET).exe
+
+##
+## CLEAN RULES
+##
+
+clean:
+ rm -rf $(DLL_OBJECTS) $(EXE_OBJECTS)
+ rm -rf $(TARGET).dll
+ rm -rf $(TARGET).lib
+ rm -rf $(TARGET).exe
diff --git a/src/about.c b/src/about.c
new file mode 100755
index 0000000..101aacc
--- /dev/null
+++ b/src/about.c
@@ -0,0 +1,536 @@
+/* about.c - 2000/05/05 */
+/*
+ * EasyTAG - Tag editor for MP3 and Ogg Vorbis files
+ * Copyright (C) 2000-2003 Jerome Couderc <easytag@gmail.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+#include <glib/gi18n-lib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h> // for 'system'
+#ifdef ENABLE_MP3
+# include <id3tag.h>
+# ifdef ENABLE_ID3LIB
+# include <id3.h>
+# endif
+#endif
+#include <errno.h>
+
+#include "about.h"
+#include "easytag.h"
+#include "misc.h"
+#include "msgbox.h"
+#include "charset.h"
+
+#ifndef PACKAGE_DATA_DIR
+# include "../pixmaps/EasyTAG_logo.xpm"
+#endif
+
+#ifdef WIN32
+# include "win32/win32dep.h"
+#endif
+
+
+/****************
+ * Declarations *
+ ****************/
+static GtkWidget *AboutWindow = NULL;
+
+
+/**************
+ * Prototypes *
+ **************/
+void About_Window_Key_Press (GtkWidget *window, GdkEvent *event);
+void Quit_About_Window (void);
+void About_Window_Go_To_Home_Page (void);
+
+
+
+/*************
+ * Functions *
+ *************/
+/*
+ * Close the window when the 'escape' key is pressed.
+ */
+void About_Window_Key_Press (GtkWidget *window ATTRIBUTE_UNUSED, GdkEvent *event)
+{
+ GdkEventKey *kevent;
+
+ if (event && event->type == GDK_KEY_PRESS)
+ {
+ kevent = (GdkEventKey *)event;
+ switch(kevent->keyval)
+ {
+ case GDK_Escape:
+ Quit_About_Window();
+ break;
+ }
+ }
+}
+
+
+void Quit_About_Window (void)
+{
+ if (AboutWindow)
+ {
+ gtk_widget_destroy(AboutWindow);
+ AboutWindow = (GtkWidget *)NULL;
+ /*gtk_widget_set_sensitive(MainWindow,TRUE);*/
+ }
+}
+
+void Show_About_Window (void)
+{
+ GtkWidget *AboutNoteBook;
+ GtkWidget *Label;
+ GtkWidget *Frame;
+ GtkWidget *VBox, *hbox;
+ GtkWidget *ScrollWindow;
+ GtkWidget *TextView;
+ GtkTextBuffer *TextBuffer;
+ GtkTextIter iter;
+ GtkWidget *Button;
+ GtkWidget *Logo;
+ GdkPixmap *pixmap;
+ GdkBitmap *mask;
+ gchar temp[MAX_STRING_LEN];
+ gchar *temp_str;
+ gint i;
+#ifdef PACKAGE_DATA_DIR
+ FILE *file;
+ gchar *tmp = NULL;
+#endif
+ gchar *description_text =
+ _(""
+ " EasyTAG is an utility for viewing and editing tags for MP3, MP2, "
+ "FLAC, Ogg Vorbis, MP4/AAC, MusePack and Monkey's Audio files. Its "
+ "simple and nice GTK+ interface makes tagging easier under GNU/Linux."
+ "");
+
+
+ /* Translation contributions */
+ gchar *translations_thanks_text [][2]= {
+ {" - Bastian Kleineidam ", _("(German translation)")},
+ {" - Adrian Bunk ", _("(German translation)")},
+ {" - Colin Marquardt ", _("(German translation)")},
+ {" - Philipp Thomas ", _("(German translation)")},
+ {" - Sergey Zhumatiy ", _("(Russian translation)")},
+ {" - Andrey Astafiev ", _("(Russian translation)")},
+ {" - Vincent van Adrighem ", _("(Dutch translation)")},
+ {" - Björn Olievier ", _("(Dutch translation)")},
+ {" - Patrik Israelsson ", _("(Swedish translation)")},
+ {" - Anders Strömer ", _("(Swedish translation)")},
+ {" - Szel Miklos ", _("(Hungarian translation)")},
+ {" - Nagy Boldizsar ", _("(Hungarian translation)")},
+ {" - Mészáros Csaba ", _("(Hungarian translation)")},
+ {" - Cappelletti Lorenzo ", _("(Italian translation)")},
+ {" - Costantino Ceoldo ", _("(Italian translation)")},
+ {" - Takeshi Aihana ", _("(Japanese translation)")},
+ {" - Olexander Kunytsa ", _("(Ukrainian translation)")},
+ {" - Cawko Xakep ", _("(Ukrainian translation)")},
+ {" - Milan Siebenburger ", _("(Czech translation)")},
+ {" - Zbynek Mrkvicka ", _("(Czech translation)")},
+ {" - Jaime Serrano Cartagena ", _("(Spanish translation)")},
+ {" - Fernando M. Bueno Moreno ", _("(Spanish translation)")},
+ {" - Francisco Javier F. Serrador ", _("(Spanish translation)")},
+ {" - Maciej Kasprzyk ", _("(Polish translation)")},
+ {" - Pauliuc George ", _("(Romanian translation)")},
+ {" - Morten Brix Pedersen ", _("(Danish translation)")},
+ {" - Apollon Oikonomopoulos ", _("(Greek translation)")},
+ {" - doutor zero ", _("(Brazilian Portuguese translation)")},
+ {" - Luchezar P. Petkov ", _("(Bulgarian translation)")},
+ {" - Yang Jinsong ", _("(Chinese translation)")},
+ {" - Yuval Hager ", _("(Hebrew translation)")},
+ {NULL,NULL}
+ };
+
+ /* General contributions */
+ gchar *general_thanks_text =
+ " - Daniel Drake (GTK2 port)\n"
+ " - Mihael Vrbanec (GTK2 port)\n"
+ " - Michael Pujos (Win32 port)\n"
+ " - Andrew Shuvalov\n"
+ " - Sergey Zhumatiy\n"
+ " - Kevin Venkiteswaran\n"
+ " - Sheikholeslami Navid\n"
+ " - Marcus Tegel\n"
+ " - Robert\n"
+ " - Lars Bohn\n"
+ " - Patrik Israelsson\n"
+ " - Adrian Bunk\n"
+ " - Alexander D Harkness\n"
+ " - Vaclav Slavik\n"
+ " - Charles Kerr\n"
+ " - Gian\n"
+ " - Thomas Zajic\n"
+ " - Szel Miklos\n"
+ " - Tore Aursand\n"
+ " - Cappelletti Lorenzo\n"
+ " - Colin Marquardt\n"
+ " - Goetz Waschk\n"
+ " - Holger Schemel\n"
+ " - Artur Polaczynski\n"
+ " - Maciej Kasprzyk\n"
+ " - Daniel Pichler\n"
+ " - Santtu Lakkala\n"
+ " - Philipp Thomas\n"
+ " - Tony Mancill\n"
+ " - Pavel Minayev\n"
+ " - Justus Schwartz\n"
+ " - Fredrik Noring\n"
+ " - Guilherme Destefani\n"
+ " - Michael Ihde\n"
+ " - Stewart Whitman\n"
+ " - Javier Kohen\n"
+ " - Alexey Illarionov\n"
+ " - Der Humph\n"
+ " - Emmanuel Brun\n"
+ " - Maarten Maathuis\n"
+ " - Pierre Dumuid\n"
+ "";
+
+
+ /* Check if already opened */
+ if (AboutWindow)
+ {
+ gdk_window_raise(AboutWindow->window);
+ return;
+ }
+
+ /* Dialog window */
+ AboutWindow = gtk_dialog_new();
+
+ /* Config */
+ gtk_window_set_title(GTK_WINDOW(AboutWindow),_("About..."));
+ gtk_window_set_position(GTK_WINDOW(AboutWindow),GTK_WIN_POS_CENTER);
+
+ /* Signals connection */
+ g_signal_connect(G_OBJECT(AboutWindow),"destroy", G_CALLBACK(Quit_About_Window),NULL);
+ g_signal_connect(G_OBJECT(AboutWindow),"delete_event",G_CALLBACK(Quit_About_Window),NULL);
+ g_signal_connect(G_OBJECT(AboutWindow),"key_press_event", G_CALLBACK(About_Window_Key_Press),NULL);
+
+ /* The NoteBook */
+ AboutNoteBook = gtk_notebook_new();
+ gtk_notebook_popup_enable(GTK_NOTEBOOK(AboutNoteBook));
+ gtk_box_pack_start(GTK_BOX(GTK_DIALOG(AboutWindow)->vbox),AboutNoteBook,TRUE,TRUE,0);
+
+
+ /*
+ * Tab for common informations
+ */
+
+ Label = gtk_label_new(_("About"));
+ Frame = gtk_frame_new(NULL);
+ gtk_notebook_append_page (GTK_NOTEBOOK(AboutNoteBook),Frame,Label);
+ gtk_container_set_border_width(GTK_CONTAINER(Frame), 2);
+
+ VBox = gtk_vbox_new(FALSE,0);
+ gtk_container_add(GTK_CONTAINER(Frame),VBox);
+
+ /* EasyTAG Logo */
+ gtk_widget_realize(AboutWindow);
+
+#ifdef PACKAGE_DATA_DIR
+ pixmap = gdk_pixmap_create_from_xpm(AboutWindow->window,&mask,NULL,PACKAGE_DATA_DIR"/EasyTAG_logo.xpm");
+#else
+ pixmap = gdk_pixmap_create_from_xpm_d(AboutWindow->window,&mask,NULL,EasyTAG_logo_xpm);
+#endif
+
+ if (pixmap)
+ {
+ Logo = gtk_image_new_from_pixmap(pixmap, mask);
+ g_object_unref(pixmap);
+ g_object_unref(mask);
+ gtk_box_pack_start(GTK_BOX(VBox),Logo,FALSE,TRUE,0);
+ gtk_misc_set_padding(GTK_MISC(Logo),2,2);
+ }
+
+ /* Infos */
+ Label = gtk_label_new(NULL);
+ gtk_label_set_markup(GTK_LABEL(Label),"<b>"APPNAME" "VERSION"</b>");
+ gtk_box_pack_start(GTK_BOX(VBox),Label,FALSE,TRUE,0);
+
+ sprintf(temp,_("(compiled: %s)"),__DATE__);
+ Label = gtk_label_new(temp);
+ gtk_box_pack_start(GTK_BOX(VBox),Label,FALSE,TRUE,0);
+
+#ifdef ENABLE_MP3 // FIX ME : should separate gtk and id3lib
+ sprintf(temp,_("(using: GTK+ %d.%d.%d)"),GTK_MAJOR_VERSION,GTK_MINOR_VERSION,
+ GTK_MICRO_VERSION);
+ Label = gtk_label_new(temp);
+ gtk_box_pack_start(GTK_BOX(VBox),Label,FALSE,TRUE,0);
+#endif
+
+#ifdef ENABLE_MP3
+ sprintf(temp, _("(MP3 file support enabled using: libid3tag %s)"), ID3_VERSION);
+ Label = gtk_label_new(temp);
+ gtk_box_pack_start(GTK_BOX(VBox),Label,FALSE,TRUE,0);
+#if (defined ENABLE_ID3LIB)
+ sprintf(temp, _("(ID3v2.3 tags support enabled using: id3lib %d.%d.%d)"),
+ ID3LIB_MAJOR_VERSION, ID3LIB_MINOR_VERSION, ID3LIB_PATCH_VERSION);
+ Label = gtk_label_new(temp);
+ gtk_box_pack_start(GTK_BOX(VBox),Label,FALSE,TRUE,0);
+#else
+ Label = gtk_label_new(_("(ID3v2.3 tags support disabled)"));
+ gtk_box_pack_start(GTK_BOX(VBox),Label,FALSE,TRUE,0);
+#endif
+#else
+ sprintf(temp, _("(MP3 file support disabled)"));
+ Label = gtk_label_new(temp);
+ gtk_box_pack_start(GTK_BOX(VBox),Label,FALSE,TRUE,0);
+#endif
+
+#ifndef ENABLE_OGG
+ Label = gtk_label_new(_("(Ogg Vorbis file support disabled)"));
+ gtk_box_pack_start(GTK_BOX(VBox),Label,FALSE,TRUE,0);
+#endif
+
+#ifndef ENABLE_SPEEX
+ Label = gtk_label_new(_("(Speep file support disabled)"));
+ gtk_box_pack_start(GTK_BOX(VBox),Label,FALSE,TRUE,0);
+#endif
+
+#ifndef ENABLE_FLAC
+ Label = gtk_label_new(_("(FLAC file support disabled)"));
+ gtk_box_pack_start(GTK_BOX(VBox),Label,FALSE,TRUE,0);
+#endif
+
+#ifndef ENABLE_MP4
+ Label = gtk_label_new(_("(MP4/AAC file support disabled)"));
+ gtk_box_pack_start(GTK_BOX(VBox),Label,FALSE,TRUE,0);
+#endif
+
+#ifndef ENABLE_WAVPACK
+ Label = gtk_label_new(_("(WavPack file support disabled)"));
+ gtk_box_pack_start(GTK_BOX(VBox),Label,FALSE,TRUE,0);
+#endif
+
+ /* Insert a blank line */
+ Label = gtk_label_new("");
+ gtk_box_pack_start(GTK_BOX(VBox),Label,FALSE,TRUE,0);
+
+ sprintf(temp,_("Author: %s"),AUTHOR);
+ Label = gtk_label_new(temp);
+ gtk_box_pack_start(GTK_BOX(VBox),Label,FALSE,TRUE,0);
+
+ sprintf(temp,_("E-mail: %s"),EMAIL);
+ Label = gtk_label_new(temp);
+ gtk_box_pack_start(GTK_BOX(VBox),Label,FALSE,TRUE,0);
+
+ hbox = gtk_hbox_new(FALSE,0);
+ gtk_box_pack_start(GTK_BOX(VBox),hbox,FALSE,TRUE,0);
+ Label = gtk_label_new(_("Web Page: "));
+ gtk_misc_set_alignment(GTK_MISC(Label),1,0.5);
+ gtk_box_pack_start(GTK_BOX(hbox),Label,TRUE,TRUE,0);
+ Button = gtk_button_new_with_label(WEBPAGE);
+ gtk_box_pack_start(GTK_BOX(hbox),Button,TRUE,TRUE,0);
+ g_signal_connect(G_OBJECT(Button),"clicked", G_CALLBACK(About_Window_Go_To_Home_Page),NULL);
+ gtk_button_set_relief(GTK_BUTTON(Button),GTK_RELIEF_NONE);
+
+ /* Insert a blank line */
+ Label = gtk_label_new("");
+ gtk_box_pack_start(GTK_BOX(VBox),Label,TRUE,TRUE,0);
+
+ Frame = gtk_frame_new(_("Description:"));
+ gtk_frame_set_shadow_type(GTK_FRAME(Frame),GTK_SHADOW_IN);
+ gtk_box_pack_start(GTK_BOX(VBox),Frame,FALSE,TRUE,0);
+ gtk_container_set_border_width(GTK_CONTAINER(Frame), 2);
+
+ Label = gtk_label_new(_(description_text));
+ gtk_misc_set_padding(GTK_MISC(Label),2,2);
+ gtk_label_set_line_wrap(GTK_LABEL(Label),TRUE);
+ gtk_container_add(GTK_CONTAINER(Frame),Label);
+
+
+ /*
+ * Tab for thanks
+ */
+
+ Label = gtk_label_new(_("Thanks"));
+ Frame = gtk_frame_new(NULL);
+ gtk_notebook_append_page (GTK_NOTEBOOK(AboutNoteBook),Frame,Label);
+
+ ScrollWindow = gtk_scrolled_window_new(NULL,NULL);
+ gtk_container_add(GTK_CONTAINER(Frame),ScrollWindow);
+ gtk_container_set_border_width(GTK_CONTAINER(Frame),2);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ScrollWindow),
+ GTK_POLICY_AUTOMATIC,GTK_POLICY_AUTOMATIC);
+
+ TextBuffer = gtk_text_buffer_new(NULL);
+ gtk_text_buffer_create_tag(TextBuffer, "italic", "style", PANGO_STYLE_ITALIC, NULL);
+ gtk_text_buffer_create_tag(TextBuffer, "bold", "weight", PANGO_WEIGHT_BOLD, NULL);
+ gtk_text_buffer_create_tag(TextBuffer, "underline", "underline", PANGO_UNDERLINE_SINGLE, NULL);
+ gtk_text_buffer_create_tag(TextBuffer, "large", "scale", PANGO_SCALE_LARGE, NULL);
+ //gtk_text_buffer_create_tag(TextBuffer, "x-large", "scale", PANGO_SCALE_X_LARGE, NULL);
+ //gtk_text_buffer_create_tag(TextBuffer, "monospace", "family", "monospace", NULL);
+
+ gtk_text_buffer_get_iter_at_offset(TextBuffer, &iter, 0);
+
+ temp_str = _("Translations:\n");
+ gtk_text_buffer_insert_with_tags_by_name(TextBuffer, &iter,
+ temp_str, -1,
+ "bold", "underline", "large", NULL);
+
+ for (i=0; translations_thanks_text[i][0]!=NULL; i++)
+ {
+ // Translator name
+ if (!g_utf8_validate(translations_thanks_text[i][0], -1, NULL))
+ temp_str = convert_string(translations_thanks_text[i][0], "iso-8859-1", "utf-8",TRUE);
+ else
+ temp_str = g_strdup(translations_thanks_text[i][0]);
+ gtk_text_buffer_insert(TextBuffer, &iter, temp_str, -1);
+ g_free(temp_str);
+
+ // Translation language
+ if (!g_utf8_validate(translations_thanks_text[i][1], -1, NULL))
+ temp_str = convert_string(translations_thanks_text[i][1], "iso-8859-1", "utf-8",TRUE);
+ else
+ temp_str = g_strdup(translations_thanks_text[i][1]);
+ gtk_text_buffer_insert_with_tags_by_name(TextBuffer, &iter, temp_str, -1,
+ "italic", NULL);
+ g_free(temp_str);
+ gtk_text_buffer_insert(TextBuffer, &iter, "\n", -1);
+ }
+
+ gtk_text_buffer_insert(TextBuffer, &iter, "\n", -1);
+
+ temp_str = _("General:\n");
+ gtk_text_buffer_insert_with_tags_by_name(TextBuffer, &iter,
+ temp_str, -1,
+ "bold", "underline", "large", NULL);
+ gtk_text_buffer_insert(TextBuffer, &iter, general_thanks_text, -1);
+
+ TextView = gtk_text_view_new_with_buffer(TextBuffer);
+ gtk_container_add(GTK_CONTAINER(ScrollWindow),TextView);
+ gtk_text_view_set_editable(GTK_TEXT_VIEW(TextView), FALSE);
+ gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(TextView), GTK_WRAP_WORD);
+
+
+ /*
+ * Tab for ChangeLog
+ */
+
+#ifdef PACKAGE_DATA_DIR
+ Label = gtk_label_new(_("Changes"));
+ Frame = gtk_frame_new(NULL);
+ gtk_notebook_append_page (GTK_NOTEBOOK(AboutNoteBook),Frame,Label);
+
+ ScrollWindow = gtk_scrolled_window_new(NULL,NULL);
+ gtk_container_add(GTK_CONTAINER(Frame),ScrollWindow);
+ gtk_container_set_border_width(GTK_CONTAINER(Frame),2);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ScrollWindow),
+ GTK_POLICY_AUTOMATIC,GTK_POLICY_AUTOMATIC);
+
+ TextBuffer = gtk_text_buffer_new(NULL);
+ gtk_text_buffer_get_iter_at_offset(TextBuffer, &iter, 0);
+ gtk_text_buffer_create_tag(TextBuffer, "monospace", "family", "monospace", NULL);
+ gtk_text_buffer_create_tag(TextBuffer, "red_foreground", "foreground", "red", NULL);
+ gtk_text_buffer_create_tag(TextBuffer, "blue_foreground", "foreground", "blue", NULL);
+ gtk_text_buffer_create_tag(TextBuffer, "bold", "weight", PANGO_WEIGHT_BOLD, NULL);
+
+ gtk_text_buffer_get_iter_at_offset(TextBuffer, &iter, 0);
+
+ // The file 'ChangeLog' to read
+ if ( (file=fopen(PACKAGE_DATA_DIR"/ChangeLog","r"))==0 )
+ {
+ gchar *msg = g_strdup_printf(_("Can't open file '%s' (%s)\n"),PACKAGE_DATA_DIR"/ChangeLog",g_strerror(errno));
+ gtk_text_buffer_insert_with_tags_by_name(TextBuffer, &iter,
+ msg, -1,
+ "monospace", "red_foreground", NULL);
+ g_free(msg);
+ } else
+ {
+ gint first_version = 0;
+
+ while (fgets(temp,sizeof(temp),file))
+ {
+ if (temp[strlen(temp)-1]=='\n')
+ temp[strlen(temp)-1]='\0';
+ if (temp[strlen(temp)-1]=='\r')
+ temp[strlen(temp)-1]='\0';
+
+ // Convert line to UTF-8
+ if (!g_utf8_validate(temp, -1, NULL))
+ tmp = convert_string(temp, "iso-8859-1", "utf-8",TRUE);
+ else
+ tmp = g_strdup(temp);
+
+ if (tmp && tmp[0]!=' ') // If first character is a space => 1rst line after title
+ {
+ first_version++;
+ // To set to bold the title of the version and to red the first version
+ if (first_version > 2) // As title takes 2 lines
+ gtk_text_buffer_insert_with_tags_by_name(TextBuffer, &iter,
+ tmp, -1,
+ "monospace", "bold", NULL);
+ else
+ gtk_text_buffer_insert_with_tags_by_name(TextBuffer, &iter,
+ tmp, -1,
+ "monospace", "bold", "blue_foreground", NULL);
+ }else
+ {
+ if (first_version > 2) // As title takes 2 lines
+ gtk_text_buffer_insert_with_tags_by_name(TextBuffer, &iter,
+ tmp, -1,
+ "monospace", NULL);
+ else
+ gtk_text_buffer_insert_with_tags_by_name(TextBuffer, &iter,
+ tmp, -1,
+ "monospace", "blue_foreground", NULL);
+ }
+
+ gtk_text_buffer_insert(TextBuffer, &iter, "\n", -1);
+ g_free(tmp);
+ }
+ fclose(file);
+ }
+ TextView = gtk_text_view_new_with_buffer(TextBuffer);
+ gtk_container_add(GTK_CONTAINER(ScrollWindow),TextView);
+ gtk_text_view_set_editable(GTK_TEXT_VIEW(TextView), FALSE);
+ gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(TextView), GTK_WRAP_WORD);
+#endif
+
+
+ /*
+ * Close Button
+ */
+ Button = Create_Button_With_Pixmap(BUTTON_CLOSE);
+ gtk_box_pack_start(GTK_BOX(GTK_DIALOG(AboutWindow)->action_area),Button,FALSE,FALSE,0);
+ g_signal_connect(G_OBJECT(Button),"clicked", G_CALLBACK(Quit_About_Window),NULL);
+ GTK_WIDGET_SET_FLAGS(Button,GTK_CAN_DEFAULT);
+ gtk_widget_grab_default(Button);
+
+
+ /* Disable main window */
+ /*gtk_widget_set_sensitive(MainWindow,FALSE);*/
+
+ gtk_widget_show_all(AboutWindow);
+}
+
+
+void About_Window_Go_To_Home_Page (void)
+{
+#ifdef WIN32
+ ET_Win32_Notify_Uri(WEBPAGE);
+#else
+ system("gnome-moz-remote "WEBPAGE);
+#endif
+}
diff --git a/src/about.h b/src/about.h
new file mode 100755
index 0000000..1fb4c25
--- /dev/null
+++ b/src/about.h
@@ -0,0 +1,33 @@
+/* about.h - 2000/05/05 */
+/*
+ * EasyTAG - Tag editor for MP3 and Ogg Vorbis files
+ * Copyright (C) 2000-2003 Jerome Couderc <easytag@gmail.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef __ABOUT_H__
+#define __ABOUT_H__
+
+
+/**************
+ * Prototypes *
+ **************/
+
+void Show_About_Window (void);
+
+
+#endif /* __ABOUT_H__ */
diff --git a/src/ape_tag.c b/src/ape_tag.c
new file mode 100755
index 0000000..c88f326
--- /dev/null
+++ b/src/ape_tag.c
@@ -0,0 +1,342 @@
+/* ape_tag.c */
+/*
+ * EasyTAG - Tag editor for MP3, Ogg Vorbis and MPC files
+ * Copyright (C) 2001-2003 Jerome Couderc <easytag@gmail.com>
+ * Copyright (C) 2002-2003 Artur Polaczyñski <artii@o2.pl>
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+
+#include <gtk/gtk.h>
+#include <glib/gi18n-lib.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "easytag.h"
+#include "ape_tag.h"
+#include "et_core.h"
+#include "log.h"
+#include "log.h"
+#include "misc.h"
+#include "setting.h"
+#include "charset.h"
+#include "libapetag/apetaglib.h"
+
+/***************
+ * Declaration *
+ ***************/
+
+
+
+/**************
+ * Prototypes *
+ **************/
+gboolean Ape_Tag_Write_File (FILE *file_in, gchar *filename_in);
+
+
+/*************
+ * Functions *
+ *************/
+
+/*
+ * Note:
+ * - if field is found but contains no info (strlen(str)==0), we don't read it
+ */
+gboolean Ape_Tag_Read_File_Tag (gchar *filename, File_Tag *FileTag)
+{
+ FILE *file;
+ gchar *string = NULL;
+ gchar *string1 = NULL;
+ apetag *ape_cnt;
+
+ if (!filename || !FileTag)
+ return FALSE;
+
+ if ((file = fopen(filename, "rb")) == NULL)
+ {
+ gchar *filename_utf8 = filename_to_display(filename);
+ Log_Print(_("ERROR while opening file: '%s' (%s)."),filename_utf8, g_strerror(errno));
+ g_free(filename_utf8);
+ return FALSE;
+ }
+
+ ape_cnt = apetag_init();
+ apetag_read_fp(ape_cnt, file, filename, 0); /* read all tags ape,id3v[12]*/
+
+ /*********
+ * Title *
+ *********/
+ string = apefrm_getstr(ape_cnt, APE_TAG_FIELD_TITLE);
+ if (FileTag->title == NULL)
+ FileTag->title = Try_To_Validate_Utf8_String(string);
+
+
+ /**********
+ * Artist *
+ **********/
+ string = apefrm_getstr(ape_cnt, APE_TAG_FIELD_ARTIST);
+ if (FileTag->artist == NULL)
+ FileTag->artist = Try_To_Validate_Utf8_String(string);
+
+ /*********
+ * Album *
+ *********/
+ string = apefrm_getstr(ape_cnt, APE_TAG_FIELD_ALBUM);
+ if (FileTag->album == NULL)
+ FileTag->album = Try_To_Validate_Utf8_String(string);
+
+ /***************
+ * Disc Number *
+ ***************/
+ string = apefrm_getstr(ape_cnt, "Part");
+ if (FileTag->disc_number == NULL)
+ FileTag->disc_number = Try_To_Validate_Utf8_String(string);
+
+ /********
+ * Year *
+ ********/
+ string = apefrm_getstr(ape_cnt, APE_TAG_FIELD_YEAR);
+ FileTag->year = Try_To_Validate_Utf8_String(string);
+
+ /*************************
+ * Track and Total Track *
+ *************************/
+ string = apefrm_getstr(ape_cnt, APE_TAG_FIELD_TRACK);
+ if (string)
+ {
+ string = Try_To_Validate_Utf8_String(string);
+
+ string1 = g_utf8_strchr(string, -1, '/'); // strchr don't like NULL string
+ if (NUMBER_TRACK_FORMATED)
+ {
+ if (string1)
+ {
+ FileTag->track_total = g_strdup_printf("%.*d",NUMBER_TRACK_FORMATED_SPIN_BUTTON, atoi(string1 + 1));
+ *string1 = '\0';
+ }
+ FileTag->track = g_strdup_printf("%.*d",NUMBER_TRACK_FORMATED_SPIN_BUTTON, atoi(string));
+ } else
+ {
+ if (string1)
+ {
+ FileTag->track_total = g_strdup(string1 + 1);
+ *string1 = '\0';
+ }
+ FileTag->track = g_strdup(string);
+ }
+ g_free(string);
+ } else
+ {
+ FileTag->track = FileTag->track_total = NULL;
+ }
+
+ /*********
+ * Genre *
+ *********/
+ string = apefrm_getstr(ape_cnt, APE_TAG_FIELD_GENRE);
+ if (FileTag->genre == NULL)
+ FileTag->genre = Try_To_Validate_Utf8_String(string);
+
+ /***********
+ * Comment *
+ ***********/
+ string = apefrm_getstr(ape_cnt, APE_TAG_FIELD_COMMENT);
+ if (FileTag->comment == NULL)
+ FileTag->comment = Try_To_Validate_Utf8_String(string);
+
+ /************
+ * Composer *
+ ************/
+ string = apefrm_getstr(ape_cnt, APE_TAG_FIELD_COMPOSER);
+ if (FileTag->composer == NULL)
+ FileTag->composer = Try_To_Validate_Utf8_String(string);
+
+ /*******************
+ * Original artist *
+ *******************/
+ string = apefrm_getstr(ape_cnt, "Original Artist");
+ if (FileTag->orig_artist == NULL)
+ FileTag->orig_artist = Try_To_Validate_Utf8_String(string);
+
+ /*************
+ * Copyright *
+ *************/
+ string = apefrm_getstr(ape_cnt, APE_TAG_FIELD_COPYRIGHT);
+ if (FileTag->copyright == NULL)
+ FileTag->copyright = Try_To_Validate_Utf8_String(string);
+
+ /*******
+ * URL *
+ *******/
+ string = apefrm_getstr(ape_cnt, APE_TAG_FIELD_RELATED_URL);
+ if (FileTag->url == NULL)
+ FileTag->url = Try_To_Validate_Utf8_String(string);
+
+ /**************
+ * Encoded by *
+ **************/
+ string = apefrm_getstr(ape_cnt, "Encoded By");
+ if (FileTag->encoded_by == NULL)
+ FileTag->encoded_by = Try_To_Validate_Utf8_String(string);
+
+ apetag_free(ape_cnt);
+ fclose(file);
+
+ return TRUE;
+}
+
+
+
+gboolean Ape_Tag_Write_File_Tag (ET_File *ETFile)
+{
+
+ File_Tag *FileTag;
+ gchar *filename_in;
+ //FILE *file_in;
+ gchar *string;
+ //GList *list;
+ apetag *ape_mem;
+
+ if (!ETFile || !ETFile->FileTag)
+ return FALSE;
+
+ FileTag = (File_Tag *)ETFile->FileTag->data;
+ filename_in = ((File_Name *)ETFile->FileNameCur->data)->value;
+
+ ape_mem = apetag_init();
+
+ /*********
+ * Title *
+ *********/
+ if ( FileTag->title && g_utf8_strlen(FileTag->title, -1) > 0)
+ apefrm_add(ape_mem, 0, APE_TAG_FIELD_TITLE, FileTag->title);
+ else
+ apefrm_remove(ape_mem,APE_TAG_FIELD_TITLE);
+
+
+ /**********
+ * Artist *
+ **********/
+ if ( FileTag->artist && g_utf8_strlen(FileTag->artist, -1) > 0)
+ apefrm_add(ape_mem, 0, APE_TAG_FIELD_ARTIST, FileTag->artist);
+ else
+ apefrm_remove(ape_mem,APE_TAG_FIELD_ARTIST);
+
+ /*********
+ * Album *
+ *********/
+ if ( FileTag->album && g_utf8_strlen(FileTag->album, -1) > 0)
+ apefrm_add(ape_mem, 0, APE_TAG_FIELD_ALBUM, FileTag->album);
+ else
+ apefrm_remove(ape_mem,APE_TAG_FIELD_ALBUM);
+
+ /***************
+ * Disc Number *
+ ***************/
+ if ( FileTag->disc_number && g_utf8_strlen(FileTag->disc_number, -1) > 0)
+ apefrm_add(ape_mem, 0, "Part", FileTag->disc_number);
+ else
+ apefrm_remove(ape_mem,"Part");
+
+ /********
+ * Year *
+ ********/
+ if ( FileTag->year && g_utf8_strlen(FileTag->year, -1) > 0)
+ apefrm_add(ape_mem, 0, APE_TAG_FIELD_YEAR, FileTag->year);
+ else
+ apefrm_remove(ape_mem,APE_TAG_FIELD_YEAR);
+
+ /*************************
+ * Track and Total Track *
+ *************************/
+ if ( FileTag->track && g_utf8_strlen(FileTag->track, -1) > 0)
+ {
+ if ( FileTag->track_total && g_utf8_strlen(FileTag->track_total, -1) > 0 )
+ string = g_strconcat(FileTag->track,"/",FileTag->track_total,NULL);
+ else
+ string = g_strconcat(FileTag->track,NULL);
+ apefrm_add(ape_mem, 0, APE_TAG_FIELD_TRACK, string);
+ g_free(string);
+ }else
+ apefrm_remove(ape_mem,APE_TAG_FIELD_TRACK);
+
+ /*********
+ * Genre *
+ *********/
+ if ( FileTag->genre && g_utf8_strlen(FileTag->genre, -1) > 0)
+ apefrm_add(ape_mem, 0, APE_TAG_FIELD_GENRE, FileTag->genre);
+ else
+ apefrm_remove(ape_mem,APE_TAG_FIELD_GENRE);
+
+ /***********
+ * Comment *
+ ***********/
+ if ( FileTag->comment && g_utf8_strlen(FileTag->comment, -1) > 0)
+ apefrm_add (ape_mem, 0, APE_TAG_FIELD_COMMENT, FileTag->comment);
+ else
+ apefrm_remove(ape_mem,APE_TAG_FIELD_COMMENT);
+
+ /************
+ * Composer *
+ ************/
+ if ( FileTag->composer && g_utf8_strlen(FileTag->composer, -1) > 0)
+ apefrm_add(ape_mem, 0, APE_TAG_FIELD_COMPOSER, FileTag->composer);
+ else
+ apefrm_remove(ape_mem,APE_TAG_FIELD_COMPOSER);
+
+ /*******************
+ * Original artist *
+ *******************/
+ if ( FileTag->orig_artist && g_utf8_strlen(FileTag->orig_artist, -1) > 0)
+ apefrm_add(ape_mem, 0, "Original Artist", FileTag->orig_artist);
+ else
+ apefrm_remove(ape_mem,"Original Artist");
+
+ /*************
+ * Copyright *
+ *************/
+ if ( FileTag->copyright && g_utf8_strlen(FileTag->copyright, -1) > 0)
+ apefrm_add(ape_mem, 0, APE_TAG_FIELD_COPYRIGHT, FileTag->copyright);
+ else
+ apefrm_remove(ape_mem,APE_TAG_FIELD_COPYRIGHT);
+
+ /*******
+ * URL *
+ *******/
+ if ( FileTag->url && g_utf8_strlen(FileTag->url, -1) > 0)
+ apefrm_add(ape_mem, 0, APE_TAG_FIELD_RELATED_URL, FileTag->url);
+ else
+ apefrm_remove(ape_mem,APE_TAG_FIELD_RELATED_URL);
+
+ /**************
+ * Encoded by *
+ **************/
+ if ( FileTag->encoded_by && g_utf8_strlen(FileTag->encoded_by, -1) > 0)
+ apefrm_add(ape_mem, 0, "Encoded By", FileTag->encoded_by);
+ else
+ apefrm_remove(ape_mem,"Encoded By");
+
+
+ /* reread all tag-type again excl. changed frames by apefrm_remove() */
+ apetag_save(filename_in,ape_mem,APE_TAG_V2+SAVE_NEW_OLD_APE_TAG);
+ apetag_free(ape_mem);
+
+ return TRUE;
+}
diff --git a/src/ape_tag.h b/src/ape_tag.h
new file mode 100755
index 0000000..1d21acb
--- /dev/null
+++ b/src/ape_tag.h
@@ -0,0 +1,40 @@
+/* ape_tag.h - 2001/11/08 */
+/*
+ * EasyTAG - Tag editor for MP3, Ogg Vorbis and MPC files
+ * Copyright (C) 2001-2003 Jerome Couderc <easytag@gmail.com>
+ * Copyright (C) 2002-2003 Artur Polaczyñski <artii@o2.pl>
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef __APE_TAG_H__
+#define __APE_TAG_H__
+
+
+#include "et_core.h"
+
+/***************
+ * Declaration *
+ ***************/
+
+
+/**************
+ * Prototypes *
+ **************/
+gboolean Ape_Tag_Read_File_Tag (gchar *filename, File_Tag *FileTag);
+gboolean Ape_Tag_Write_File_Tag (ET_File *ETFile);
+
+#endif /* __APE_TAG_H__ */
diff --git a/src/bar.c b/src/bar.c
new file mode 100755
index 0000000..fc44855
--- /dev/null
+++ b/src/bar.c
@@ -0,0 +1,482 @@
+/* bar.c - 2000/05/05 */
+/*
+ * EasyTAG - Tag editor for MP3 and Ogg Vorbis files
+ * Copyright (C) 2000-2003 Jerome Couderc <easytag@gmail.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+
+#include <gtk/gtk.h>
+#include <glib/gi18n-lib.h>
+
+#include "bar.h"
+#include "easytag.h"
+#include "about.h"
+#include "prefs.h"
+#include "setting.h"
+#include "browser.h"
+#include "scan.h"
+#include "cddb.h"
+#include "log.h"
+#include "misc.h"
+#include "charset.h"
+#include "ui_manager.h"
+
+/***************
+ * Declaration *
+ ***************/
+GtkWidget *StatusBar = NULL;
+static guint StatusbarTimerId = 0;
+GList *ActionPairsList = NULL;
+
+/**************
+ * Prototypes *
+ **************/
+void Init_Menu_Bar (void);
+void Menu_Sort_Action (GtkAction *action, gpointer data);
+
+void Statusbar_Start_Timer (void);
+gboolean Statusbar_Stop_Timer (void);
+void Statusbar_Remove_Timer (void);
+
+
+/*************
+ * Functions *
+ *************/
+
+/*
+ * Dynamic reimplementation of switch macros
+ */
+#define QCASE(string,callback) if (quark == g_quark_from_string((string))) { (callback)(); }
+#define QCASE_DATA(string,callback,data) if (quark == g_quark_from_string((string))) { (callback)((data)); }
+
+/*
+ * Menu bar
+ */
+void Menu_Sort_Action (GtkAction *item, gpointer data)
+{
+ GtkWidget *TBViewMode;
+ const gchar *action = gtk_action_get_name(item);
+ GQuark quark = g_quark_from_string(action);
+
+ QCASE_DATA(AM_SORT_ASCENDING_FILENAME, ET_Sort_Displayed_File_List_And_Update_UI, SORTING_BY_ASCENDING_FILENAME);
+ QCASE_DATA(AM_SORT_DESCENDING_FILENAME, ET_Sort_Displayed_File_List_And_Update_UI, SORTING_BY_DESCENDING_FILENAME);
+ QCASE_DATA(AM_SORT_ASCENDING_CREATION_DATE, ET_Sort_Displayed_File_List_And_Update_UI, SORTING_BY_ASCENDING_CREATION_DATE);
+ QCASE_DATA(AM_SORT_DESCENDING_CREATION_DATE, ET_Sort_Displayed_File_List_And_Update_UI, SORTING_BY_DESCENDING_CREATION_DATE);
+ QCASE_DATA(AM_SORT_ASCENDING_TRACK_NUMBER, ET_Sort_Displayed_File_List_And_Update_UI, SORTING_BY_ASCENDING_TRACK_NUMBER);
+ QCASE_DATA(AM_SORT_DESCENDING_TRACK_NUMBER, ET_Sort_Displayed_File_List_And_Update_UI, SORTING_BY_DESCENDING_TRACK_NUMBER);
+ QCASE_DATA(AM_SORT_ASCENDING_TITLE, ET_Sort_Displayed_File_List_And_Update_UI, SORTING_BY_ASCENDING_TITLE);
+ QCASE_DATA(AM_SORT_DESCENDING_TITLE, ET_Sort_Displayed_File_List_And_Update_UI, SORTING_BY_DESCENDING_TITLE);
+ QCASE_DATA(AM_SORT_ASCENDING_ARTIST, ET_Sort_Displayed_File_List_And_Update_UI, SORTING_BY_ASCENDING_ARTIST);
+ QCASE_DATA(AM_SORT_DESCENDING_ARTIST, ET_Sort_Displayed_File_List_And_Update_UI, SORTING_BY_DESCENDING_ARTIST);
+ QCASE_DATA(AM_SORT_ASCENDING_ALBUM, ET_Sort_Displayed_File_List_And_Update_UI, SORTING_BY_ASCENDING_ALBUM);
+ QCASE_DATA(AM_SORT_DESCENDING_ALBUM, ET_Sort_Displayed_File_List_And_Update_UI, SORTING_BY_DESCENDING_ALBUM);
+ QCASE_DATA(AM_SORT_ASCENDING_YEAR, ET_Sort_Displayed_File_List_And_Update_UI, SORTING_BY_ASCENDING_YEAR);
+ QCASE_DATA(AM_SORT_DESCENDING_YEAR, ET_Sort_Displayed_File_List_And_Update_UI, SORTING_BY_DESCENDING_YEAR);
+ QCASE_DATA(AM_SORT_ASCENDING_GENRE, ET_Sort_Displayed_File_List_And_Update_UI, SORTING_BY_ASCENDING_GENRE);
+ QCASE_DATA(AM_SORT_DESCENDING_GENRE, ET_Sort_Displayed_File_List_And_Update_UI, SORTING_BY_DESCENDING_GENRE);
+ QCASE_DATA(AM_SORT_ASCENDING_COMMENT, ET_Sort_Displayed_File_List_And_Update_UI, SORTING_BY_ASCENDING_COMMENT);
+ QCASE_DATA(AM_SORT_DESCENDING_COMMENT, ET_Sort_Displayed_File_List_And_Update_UI, SORTING_BY_DESCENDING_COMMENT);
+ QCASE_DATA(AM_SORT_ASCENDING_COMPOSER, ET_Sort_Displayed_File_List_And_Update_UI, SORTING_BY_ASCENDING_COMPOSER);
+ QCASE_DATA(AM_SORT_DESCENDING_COMPOSER, ET_Sort_Displayed_File_List_And_Update_UI, SORTING_BY_DESCENDING_COMPOSER);
+ QCASE_DATA(AM_SORT_ASCENDING_ORIG_ARTIST, ET_Sort_Displayed_File_List_And_Update_UI, SORTING_BY_ASCENDING_ORIG_ARTIST);
+ QCASE_DATA(AM_SORT_DESCENDING_ORIG_ARTIST, ET_Sort_Displayed_File_List_And_Update_UI, SORTING_BY_DESCENDING_ORIG_ARTIST);
+ QCASE_DATA(AM_SORT_ASCENDING_COPYRIGHT, ET_Sort_Displayed_File_List_And_Update_UI, SORTING_BY_ASCENDING_COPYRIGHT);
+ QCASE_DATA(AM_SORT_DESCENDING_COPYRIGHT, ET_Sort_Displayed_File_List_And_Update_UI, SORTING_BY_DESCENDING_COPYRIGHT);
+ QCASE_DATA(AM_SORT_ASCENDING_URL, ET_Sort_Displayed_File_List_And_Update_UI, SORTING_BY_ASCENDING_URL);
+ QCASE_DATA(AM_SORT_DESCENDING_URL, ET_Sort_Displayed_File_List_And_Update_UI, SORTING_BY_DESCENDING_URL);
+ QCASE_DATA(AM_SORT_ASCENDING_ENCODED_BY, ET_Sort_Displayed_File_List_And_Update_UI, SORTING_BY_ASCENDING_ENCODED_BY);
+ QCASE_DATA(AM_SORT_DESCENDING_ENCODED_BY, ET_Sort_Displayed_File_List_And_Update_UI, SORTING_BY_DESCENDING_ENCODED_BY);
+ QCASE_DATA(AM_SORT_ASCENDING_FILE_TYPE, ET_Sort_Displayed_File_List_And_Update_UI, SORTING_BY_ASCENDING_FILE_TYPE);
+ QCASE_DATA(AM_SORT_DESCENDING_FILE_TYPE, ET_Sort_Displayed_File_List_And_Update_UI, SORTING_BY_DESCENDING_FILE_TYPE);
+ QCASE_DATA(AM_SORT_ASCENDING_FILE_SIZE, ET_Sort_Displayed_File_List_And_Update_UI, SORTING_BY_ASCENDING_FILE_SIZE);
+ QCASE_DATA(AM_SORT_DESCENDING_FILE_SIZE, ET_Sort_Displayed_File_List_And_Update_UI, SORTING_BY_DESCENDING_FILE_SIZE);
+ QCASE_DATA(AM_SORT_ASCENDING_FILE_DURATION, ET_Sort_Displayed_File_List_And_Update_UI, SORTING_BY_ASCENDING_FILE_DURATION);
+ QCASE_DATA(AM_SORT_DESCENDING_FILE_DURATION, ET_Sort_Displayed_File_List_And_Update_UI, SORTING_BY_DESCENDING_FILE_DURATION);
+ QCASE_DATA(AM_SORT_ASCENDING_FILE_BITRATE, ET_Sort_Displayed_File_List_And_Update_UI, SORTING_BY_ASCENDING_FILE_BITRATE);
+ QCASE_DATA(AM_SORT_DESCENDING_FILE_BITRATE, ET_Sort_Displayed_File_List_And_Update_UI, SORTING_BY_DESCENDING_FILE_BITRATE);
+ QCASE_DATA(AM_SORT_ASCENDING_FILE_SAMPLERATE, ET_Sort_Displayed_File_List_And_Update_UI, SORTING_BY_ASCENDING_FILE_SAMPLERATE);
+ QCASE_DATA(AM_SORT_DESCENDING_FILE_SAMPLERATE, ET_Sort_Displayed_File_List_And_Update_UI, SORTING_BY_DESCENDING_FILE_SAMPLERATE);
+ QCASE_DATA(AM_INITIALIZE_TREE, Browser_Tree_Rebuild, NULL);
+
+ if (quark == g_quark_from_string(AM_TREE_OR_ARTISTALBUM_VIEW))
+ {
+ // Toggle button to switch between Browser view and Artist / Album view
+ TBViewMode = gtk_ui_manager_get_widget(UIManager, "/ToolBar/ViewModeToggle");
+ gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(TBViewMode),
+ !gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(TBViewMode)));
+ }
+}
+
+void Create_UI (GtkWidget **ppmenubar, GtkWidget **pptoolbar)
+{
+ GtkWidget *menubar;
+ GtkWidget *toolbar;
+ GtkWidget *toolbarwidget;
+
+ /*
+ * Structure :
+ * - name
+ * - stock_id
+ * - label
+ * - accelerator
+ * - tooltip
+ * - callback
+ */
+ GtkActionEntry ActionEntries[] =
+ {
+
+ /*
+ * Main Menu Actions
+ */
+ { MENU_FILE, NULL, _("_File"), NULL, NULL, NULL},
+ { MENU_FILE_SORT_TAG, GTK_STOCK_SORT_ASCENDING, _("Sort list by tag"), NULL, NULL, NULL },
+ { MENU_FILE_SORT_PROP, GTK_STOCK_SORT_ASCENDING, _("Sort list by property"), NULL, NULL, NULL },
+ { AM_SORT_ASCENDING_FILENAME, GTK_STOCK_SORT_ASCENDING, _("Ascending by filename"), NULL, _("Ascending by filename"), G_CALLBACK(Menu_Sort_Action) },
+ { AM_SORT_DESCENDING_FILENAME, GTK_STOCK_SORT_DESCENDING, _("Descending by filename"), NULL, _("Descending by filename"), G_CALLBACK(Menu_Sort_Action) },
+ { AM_SORT_ASCENDING_CREATION_DATE, GTK_STOCK_SORT_ASCENDING, _("Ascending by creation date"), NULL, _("Ascending by creation date"), G_CALLBACK(Menu_Sort_Action) },
+ { AM_SORT_DESCENDING_CREATION_DATE, GTK_STOCK_SORT_DESCENDING, _("Descending by creation date"), NULL, _("Descending by creation date"), G_CALLBACK(Menu_Sort_Action) },
+ { AM_SORT_ASCENDING_TRACK_NUMBER, GTK_STOCK_SORT_ASCENDING, _("Ascending by track number"), NULL, _("Ascending by track number"), G_CALLBACK(Menu_Sort_Action) },
+ { AM_SORT_DESCENDING_TRACK_NUMBER, GTK_STOCK_SORT_DESCENDING, _("Descending by track number"), NULL, _("Descending by track number"), G_CALLBACK(Menu_Sort_Action) },
+ { AM_SORT_ASCENDING_TITLE, GTK_STOCK_SORT_ASCENDING, _("Ascending by title"), NULL, _("Ascending by title"), G_CALLBACK(Menu_Sort_Action) },
+ { AM_SORT_DESCENDING_TITLE, GTK_STOCK_SORT_DESCENDING, _("Descending by title"), NULL, _("Descending by title"), G_CALLBACK(Menu_Sort_Action) },
+ { AM_SORT_ASCENDING_ARTIST, GTK_STOCK_SORT_ASCENDING, _("Ascending by artist"), NULL, _("Ascending by artist"), G_CALLBACK(Menu_Sort_Action) },
+ { AM_SORT_DESCENDING_ARTIST, GTK_STOCK_SORT_DESCENDING, _("Descending by artist"), NULL, _("Descending by artist"), G_CALLBACK(Menu_Sort_Action) },
+ { AM_SORT_ASCENDING_ALBUM, GTK_STOCK_SORT_ASCENDING, _("Ascending by album"), NULL, _("Ascending by album"), G_CALLBACK(Menu_Sort_Action) },
+ { AM_SORT_DESCENDING_ALBUM, GTK_STOCK_SORT_DESCENDING, _("Descending by album"), NULL, _("Descending by album"), G_CALLBACK(Menu_Sort_Action) },
+ { AM_SORT_ASCENDING_YEAR, GTK_STOCK_SORT_ASCENDING, _("Ascending by year"), NULL, _("Ascending by year"), G_CALLBACK(Menu_Sort_Action) },
+ { AM_SORT_DESCENDING_YEAR, GTK_STOCK_SORT_DESCENDING, _("Descending by year"), NULL, _("Descending by year"), G_CALLBACK(Menu_Sort_Action) },
+ { AM_SORT_ASCENDING_GENRE, GTK_STOCK_SORT_ASCENDING, _("Ascending by genre"), NULL, _("Ascending by genre"), G_CALLBACK(Menu_Sort_Action) },
+ { AM_SORT_DESCENDING_GENRE, GTK_STOCK_SORT_DESCENDING, _("Descending by genre"), NULL, _("Descending by genre"), G_CALLBACK(Menu_Sort_Action) },
+ { AM_SORT_ASCENDING_COMMENT, GTK_STOCK_SORT_ASCENDING, _("Ascending by comment"), NULL, _("Ascending by comment"), G_CALLBACK(Menu_Sort_Action) },
+ { AM_SORT_DESCENDING_COMMENT, GTK_STOCK_SORT_DESCENDING, _("Descending by comment"), NULL, _("Descending by comment"), G_CALLBACK(Menu_Sort_Action) },
+ { AM_SORT_ASCENDING_COMPOSER, GTK_STOCK_SORT_ASCENDING, _("Ascending by composer"), NULL, _("Ascending by composer"), G_CALLBACK(Menu_Sort_Action) },
+ { AM_SORT_DESCENDING_COMPOSER, GTK_STOCK_SORT_DESCENDING, _("Descending by composer"), NULL, _("Descending by composer"), G_CALLBACK(Menu_Sort_Action) },
+ { AM_SORT_ASCENDING_ORIG_ARTIST, GTK_STOCK_SORT_ASCENDING, _("Ascending by original artist"), NULL, _("Ascending by original artist"), G_CALLBACK(Menu_Sort_Action) },
+ { AM_SORT_DESCENDING_ORIG_ARTIST, GTK_STOCK_SORT_DESCENDING, _("Descending by original artist"), NULL, _("Descending by original artist"), G_CALLBACK(Menu_Sort_Action) },
+ { AM_SORT_ASCENDING_COPYRIGHT, GTK_STOCK_SORT_ASCENDING, _("Ascending by copyright"), NULL, _("Ascending by copyright"), G_CALLBACK(Menu_Sort_Action) },
+ { AM_SORT_DESCENDING_COPYRIGHT, GTK_STOCK_SORT_DESCENDING, _("Descending by copyright"), NULL, _("Descending by copyright"), G_CALLBACK(Menu_Sort_Action) },
+ { AM_SORT_ASCENDING_URL, GTK_STOCK_SORT_ASCENDING, _("Ascending by URL"), NULL, _("Ascending by URL"), G_CALLBACK(Menu_Sort_Action) },
+ { AM_SORT_DESCENDING_URL, GTK_STOCK_SORT_DESCENDING, _("Descending by URL"), NULL, _("Descending by URL"), G_CALLBACK(Menu_Sort_Action) },
+ { AM_SORT_ASCENDING_ENCODED_BY, GTK_STOCK_SORT_ASCENDING, _("Ascending by encoder name"), NULL, _("Ascending by encoder name"), G_CALLBACK(Menu_Sort_Action) },
+ { AM_SORT_DESCENDING_ENCODED_BY, GTK_STOCK_SORT_DESCENDING, _("Descending by encoder name"), NULL, _("Descending by encoder name"), G_CALLBACK(Menu_Sort_Action) },
+ { AM_SORT_ASCENDING_FILE_TYPE, GTK_STOCK_SORT_ASCENDING, _("Ascending by file type"), NULL, _("Ascending by file type"), G_CALLBACK(Menu_Sort_Action) },
+ { AM_SORT_DESCENDING_FILE_TYPE, GTK_STOCK_SORT_DESCENDING, _("Descending by file type"), NULL, _("Descending by file type"), G_CALLBACK(Menu_Sort_Action) },
+ { AM_SORT_ASCENDING_FILE_SIZE, GTK_STOCK_SORT_ASCENDING, _("Ascending by file size"), NULL, _("Ascending by file size"), G_CALLBACK(Menu_Sort_Action) },
+ { AM_SORT_DESCENDING_FILE_SIZE, GTK_STOCK_SORT_DESCENDING, _("Descending by file size"), NULL, _("Descending by file size"), G_CALLBACK(Menu_Sort_Action) },
+ { AM_SORT_ASCENDING_FILE_DURATION, GTK_STOCK_SORT_ASCENDING, _("Ascending by duration"), NULL, _("Ascending by duration"), G_CALLBACK(Menu_Sort_Action) },
+ { AM_SORT_DESCENDING_FILE_DURATION, GTK_STOCK_SORT_DESCENDING, _("Descending by duration"), NULL, _("Descending by duration"), G_CALLBACK(Menu_Sort_Action) },
+ { AM_SORT_ASCENDING_FILE_BITRATE, GTK_STOCK_SORT_ASCENDING, _("Ascending by bitrate"), NULL, _("Ascending by bitrate"), G_CALLBACK(Menu_Sort_Action) },
+ { AM_SORT_DESCENDING_FILE_BITRATE, GTK_STOCK_SORT_DESCENDING, _("Descending by bitrate"), NULL, _("Descending by bitrate"), G_CALLBACK(Menu_Sort_Action) },
+ { AM_SORT_ASCENDING_FILE_SAMPLERATE, GTK_STOCK_SORT_ASCENDING, _("Ascending by samplerate"), NULL, _("Ascending by samplerate"), G_CALLBACK(Menu_Sort_Action) },
+ { AM_SORT_DESCENDING_FILE_SAMPLERATE, GTK_STOCK_SORT_DESCENDING, _("Descending by samplerate"), NULL, _("Descending by samplerate"), G_CALLBACK(Menu_Sort_Action) },
+
+ { AM_OPEN_FILE_WITH, GTK_STOCK_OPEN, _("Open File(s) with ..."), NULL, _("Open File(s) with ..."), G_CALLBACK(Browser_Open_Run_Program_List_Window) },
+ { AM_SELECT_ALL_FILES, "easytag-select-all", _("Select All Files"), "<Control>A", _("Select All Files"), G_CALLBACK(Action_Select_All_Files) },
+ { AM_UNSELECT_ALL_FILES, "easytag-unselect-all", _("Unselect All Files"), "<Shift><Control>A", _("Unselect All Files"), G_CALLBACK(Action_Unselect_All_Files) },
+ { AM_INVERT_SELECTION, "easytag-invert-selection", _("Invert Files Selection"), "<Control>I", _("Invert Files Selection"), G_CALLBACK(Action_Invert_Files_Selection) },
+ { AM_DELETE_FILE, GTK_STOCK_DELETE, _("Delete File(s)"), NULL, _("Delete File(s)"), G_CALLBACK(Action_Delete_Selected_Files) },
+ { AM_FIRST, GTK_STOCK_GOTO_FIRST, _("_First File"), "<Control>Home", _("First File"), G_CALLBACK(Action_Select_First_File) },
+ { AM_PREV, GTK_STOCK_GO_BACK, _("_Previous File"), "Page_Up", _("Previous File"), G_CALLBACK(Action_Select_Prev_File) },
+ { AM_NEXT, GTK_STOCK_GO_FORWARD, _("_Next File"), "Page_Down", _("Next File"), G_CALLBACK(Action_Select_Next_File) },
+ { AM_LAST, GTK_STOCK_GOTO_LAST, _("_Last File"), "<Control>End", _("Last File"), G_CALLBACK(Action_Select_Last_File) },
+ // XXX GTK1 version uses Ctrl+C for scanner, this doesnt work in GTK1 as its copy! in gtk2, behaviour is different
+ // and binding Ctrl+C effectively stops the user copying text..
+ { AM_SCAN, "easytag-scan", _("S_can File(s)"), NULL, _("Scan File(s)"), G_CALLBACK(Action_Scan_Selected_Files) },
+ { AM_REMOVE, GTK_STOCK_CLEAR, _("_Remove Tag(s)"), "<Control>R", _("Remove Tag(s)"), G_CALLBACK(Action_Remove_Selected_Tags) },
+ { AM_UNDO, GTK_STOCK_UNDO, _("_Undo Last File(s) Changes"), "<Control>Z", _("Undo Last File(s) Changes"), G_CALLBACK(Action_Undo_Selected_Files) },
+ { AM_REDO, GTK_STOCK_REDO, _("R_edo Last File(s) Changes"), "<Shift><Control>Z", _("Redo Last File(s) Changes"), G_CALLBACK(Action_Redo_Selected_File) },
+ { AM_SAVE, GTK_STOCK_SAVE, _("_Save File(s)"), "<Control>S", _("Save File(s)"), G_CALLBACK(Action_Save_Selected_Files) },
+ { AM_SAVE_FORCED, GTK_STOCK_SAVE, _("_Force Saving File(s)"), "<Shift><Control>S", _("Force Saving File(s)"), G_CALLBACK(Action_Force_Saving_Selected_Files) },
+ { AM_UNDO_HISTORY, GTK_STOCK_UNDO, _("Undo Last Changes"), NULL, _("Undo Last Changes"), G_CALLBACK(Action_Undo_From_History_List) },
+ { AM_REDO_HISTORY, GTK_STOCK_REDO, _("Redo Last Changes"), NULL, _("Redo Last Changes"), G_CALLBACK(Action_Redo_From_History_List) },
+ { AM_QUIT, GTK_STOCK_QUIT, _("_Quit"), "<Control>Q", _("Quit"), G_CALLBACK(Quit_MainWindow) },
+
+ { MENU_BROWSER, NULL, _("_Browser"), NULL, NULL, NULL },
+ { AM_LOAD_HOME_DIR, GTK_STOCK_HOME, _("Go to _Home Directory"), "<Alt>Home", _("Go to Home Directory"), G_CALLBACK(Browser_Load_Home_Directory) },
+ { AM_LOAD_DEFAULT_DIR, GTK_STOCK_HARDDISK, _("Go to _Default Directory"), "<Control>D", _("Go to Default Directory"), G_CALLBACK(Browser_Load_Default_Directory) },
+ { AM_SET_PATH_AS_DEFAULT, GTK_STOCK_JUMP_TO, _("Set _Current Path as Default"), NULL, _("Set Current Path as Default"), G_CALLBACK(Set_Current_Path_As_Default) },
+ { AM_TREE_OR_ARTISTALBUM_VIEW, "easytag-artist-album", _("Tree View | Artist-Album View"), NULL, _("Tree View | Artist-Album View"), G_CALLBACK(Menu_Sort_Action) },
+ { AM_RENAME_DIR, GTK_STOCK_INDEX, _("Rename Directory ..."), "F2", _("Rename Directory ..."), G_CALLBACK(Browser_Open_Rename_Directory_Window) },
+ { AM_RELOAD_DIRECTORY, GTK_STOCK_REFRESH, _("Reload Directory"), "F5", _("Reload Directory"), G_CALLBACK(Browser_Reload_Directory) },
+ { AM_BROWSE_DIRECTORY_WITH, GTK_STOCK_EXECUTE, _("Browse Directory with ..."), NULL, _("Browse Directory with ..."), G_CALLBACK(Browser_Open_Run_Program_Tree_Window) },
+ { AM_COLLAPSE_TREE, NULL, _("_Collapse Tree"), "<Control><Shift>C", _("_Collapse Tree"), G_CALLBACK(Browser_Tree_Collapse) },
+ { AM_INITIALIZE_TREE, GTK_STOCK_REFRESH, _("_Refresh Tree"), "<Control><Shift>R", _("_Refresh Tree"), G_CALLBACK(Browser_Tree_Rebuild) },
+
+ { MENU_SCANNER, NULL, _("S_canner"), NULL, NULL, NULL },
+ { AM_SCANNER_FILL_TAG, "easytag-scan", _("_Fill Tag(s) ..."), NULL, _("Fill Tag(s) ..."), G_CALLBACK(Scan_Use_Fill_Tag_Scanner) },
+ { AM_SCANNER_RENAME_FILE, "easytag-scan", _("_Rename File(s) and Directory ..."), NULL, _("Rename File(s) and Directory ..."),G_CALLBACK(Scan_Use_Rename_File_Scanner) },
+ { AM_SCANNER_PROCESS_FIELDS, "easytag-scan", _("_Process Field(s) ..."), NULL, _("Process Fields(s) ..."), G_CALLBACK(Scan_Use_Process_Fields_Scanner) },
+
+ { MENU_MISC, NULL, _("_Misc"), NULL, NULL, NULL },
+ { AM_SEARCH_FILE, GTK_STOCK_FIND, _("Search _File(s) ..."), "<Control>F", _("Search File(s)..."), G_CALLBACK(Open_Search_File_Window) },
+ { AM_CDDB_SEARCH, GTK_STOCK_CDROM, _("CD Data _Base Search ..."), "<Control>B", _("CDDB Search ..."), G_CALLBACK(Open_Cddb_Window) },
+ { AM_FILENAME_FROM_TXT, GTK_STOCK_OPEN, _("Load Filenames from TXT ..."), "<Alt>T", _("Load Filenames from TXT ..."), G_CALLBACK(Open_Load_Filename_Window) },
+ { AM_WRITE_PLAYLIST, GTK_STOCK_SAVE_AS, _("Write Playlist ..."), "<Alt>W", _("Write Playlist ..."), G_CALLBACK(Open_Write_Playlist_Window) },
+ { AM_RUN_AUDIO_PLAYER, "easytag-sound", _("Run Audio Player"), "<Alt>X", _("Run Audio Player"), G_CALLBACK(Run_Audio_Player_Using_Selection) },
+
+ { MENU_SETTINGS, NULL, _("_Settings"), NULL, NULL, NULL },
+ { AM_OPEN_OPTIONS_WINDOW, GTK_STOCK_PREFERENCES, _("_Preferences ..."), "<Alt>P", _("Preferences ..."), G_CALLBACK(Open_OptionsWindow) },
+
+ { MENU_HELP, NULL, _("_Help"), NULL, NULL, NULL },
+ { AM_OPEN_ABOUT_WINDOW, GTK_STOCK_HELP, _("_About"), NULL, _("About"), G_CALLBACK(Show_About_Window) },
+
+
+ /*
+ * Following items are on toolbar but not on menu
+ */
+ { AM_STOP, GTK_STOCK_STOP, _("Stop the current action"), NULL, _("Stop the current action"), G_CALLBACK(Action_Main_Stop_Button_Pressed) },
+
+
+ /*
+ * Popup menu's Actions
+ */
+ { POPUP_FILE, NULL, _("_File Operations"), NULL, NULL, NULL },
+ { POPUP_SUBMENU_SCANNER, "easytag-scan", _("S_canner"), NULL, NULL, NULL },
+ { POPUP_DIR_RUN_AUDIO, "easytag-sound", _("Run Audio Player"), NULL, _("Run Audio Player"), G_CALLBACK(Run_Audio_Player_Using_Directory) },
+ { AM_ARTIST_RUN_AUDIO_PLAYER, "easytag-sound", _("Run Audio Player"), NULL, _("Run Audio Player"), G_CALLBACK(Run_Audio_Player_Using_Browser_Artist_List) },
+ { AM_ALBUM_RUN_AUDIO_PLAYER, "easytag-sound", _("Run Audio Player"), NULL, _("Run Audio Player"), G_CALLBACK(Run_Audio_Player_Using_Browser_Album_List) },
+ { AM_CDDB_SEARCH_FILE, GTK_STOCK_CDROM, _("CDDB Search File(s)..."), NULL, _("CDDB Search File(s)..."), G_CALLBACK(Cddb_Popup_Menu_Search_Selected_File) },
+ //{ AM_ARTIST_OPEN_FILE_WITH, GTK_STOCK_OPEN, _("Open File(s) with ..."), NULL, _("Open File(s) with ..."), G_CALLBACK(Browser_Open_Run_Program_List_Window??? Browser_Open_Run_Program_Tree_Window???) },
+ //{ AM_ALBUM_OPEN_FILE_WITH, GTK_STOCK_OPEN, _("Open File(s) with ..."), NULL, _("Open File(s) with ..."), G_CALLBACK(Browser_Open_Run_Program_List_Window??? Browser_Open_Run_Program_Tree_Window???) },
+
+ { AM_LOG_CLEAN, GTK_STOCK_CLEAR, _("Clean Log"), NULL, _("Clean Log"), G_CALLBACK(Log_Clean_Log_List) }
+
+ };
+
+ GtkToggleActionEntry ToggleActionEntries[] =
+ {
+ //{ AM_BROWSE_SUBDIR, GTK_STOCK_INDEX, _("Browse _Sub-directories"), NULL, _("Browse _Sub-directories"), NULL, FALSE },
+ { AM_BROWSE_SUBDIR, NULL, _("Browse _Sub-directories"), NULL, _("Browse _Sub-directories"), NULL, BROWSE_SUBDIR },
+#ifndef WIN32 // No sense here for Win32, "hidden" means : starts with a '.'
+ //{ AM_BROWSER_HIDDEN_DIR, NULL, _("Show Hidden Directories"), NULL, _("Show Hidden Directories"), G_CALLBACK(Browser_Tree_Rebuild), FALSE },
+ { AM_BROWSER_HIDDEN_DIR, NULL, _("Show Hidden Directories"), NULL, _("Show Hidden Directories"), G_CALLBACK(Browser_Tree_Rebuild), BROWSE_HIDDEN_DIR },
+#endif
+ { AM_VIEWMODE_TOGGLE, "easytag-artist-album", _("Show tree browser / Display by Artist and Album"), NULL, _("Show tree browser / Display by Artist and Album"), G_CALLBACK(Action_Select_Browser_Style), FALSE },
+ };
+
+ GError *error = NULL;
+ guint num_menu_entries;
+ guint num_toggle_entries;
+ guint i;
+
+ /* Calculate number of items into the menu */
+ num_menu_entries = G_N_ELEMENTS(ActionEntries);
+ num_toggle_entries = G_N_ELEMENTS(ToggleActionEntries);
+
+ /* Populate quarks list with the entries */
+ for(i = 0; i < num_menu_entries; i++)
+ {
+ Action_Pair* ActionPair = g_malloc0(sizeof(Action_Pair));
+ ActionPair->action = ActionEntries[i].name;
+ ActionPair->quark = g_quark_from_string(ActionPair->action);
+ ActionPairsList = g_list_append(ActionPairsList, ActionPair);
+ }
+
+ for(i = 0; i < num_toggle_entries; i++)
+ {
+ Action_Pair* ActionPair = g_malloc0(sizeof(Action_Pair));
+ ActionPair->action = ToggleActionEntries[i].name;
+ ActionPair->quark = g_quark_from_string(ActionPair->action);
+ ActionPairsList = g_list_append(ActionPairsList, ActionPair);
+ }
+
+ /* UI Management */
+ ActionGroup = gtk_action_group_new("actions");
+ gtk_action_group_add_actions(ActionGroup, ActionEntries, num_menu_entries, NULL);
+ gtk_action_group_add_toggle_actions(ActionGroup, ToggleActionEntries, num_toggle_entries, NULL);
+
+ UIManager = gtk_ui_manager_new();
+ if (!gtk_ui_manager_add_ui_from_string(UIManager, ui_xml, -1, &error))
+ {
+ g_error(_("Could not merge UI, error was: %s\n"), error->message);
+ g_error_free(error);
+ }
+ gtk_ui_manager_insert_action_group(UIManager, ActionGroup, 0);
+ gtk_window_add_accel_group(GTK_WINDOW(MainWindow), gtk_ui_manager_get_accel_group(UIManager));
+
+ /* Menu (not placed in a handlebar) */
+ menubar = gtk_ui_manager_get_widget(UIManager, "/MenuBar");
+ Init_Menu_Bar();
+ gtk_widget_show_all(menubar);
+
+ /* Toolbar is placed in a handlebar */
+ toolbar = gtk_handle_box_new();
+ toolbarwidget = gtk_ui_manager_get_widget(UIManager, "/ToolBar");
+ gtk_container_add(GTK_CONTAINER(toolbar), toolbarwidget);
+ gtk_toolbar_set_style(GTK_TOOLBAR(toolbarwidget), GTK_TOOLBAR_ICONS);
+ gtk_widget_show_all(toolbar);
+
+ *pptoolbar = toolbar;
+ *ppmenubar = menubar;
+}
+
+
+/*
+ * Initialize some items of the main menu
+ */
+void Init_Menu_Bar (void)
+{
+
+ CheckMenuItemBrowseSubdirMainMenu = gtk_ui_manager_get_widget(UIManager, "/MenuBar/BrowserMenu/BrowseSubdir");
+ if (CheckMenuItemBrowseSubdirMainMenu)
+ {
+ // Link to update BROWSE_SUBDIR when changed
+ g_signal_connect(G_OBJECT(CheckMenuItemBrowseSubdirMainMenu),"toggled",
+ G_CALLBACK(Check_Menu_Item_Toggled_Browse_Subdir),NULL);
+ }
+
+ CheckMenuItemBrowseHiddenDirMainMenu = gtk_ui_manager_get_widget(UIManager, "/MenuBar/BrowserMenu/BrowseHiddenDir");
+ if (CheckMenuItemBrowseHiddenDirMainMenu)
+ {
+ // Link to update BROWSE_HIDDEN_DIR when changed
+ g_signal_connect(G_OBJECT(CheckMenuItemBrowseHiddenDirMainMenu),"toggled",
+ G_CALLBACK(Check_Menu_Item_Toggled_Browse_Hidden_Dir),NULL);
+ }
+
+ /* If entry not implemented */
+ //{GtkWidget *widget = gtk_item_factory_get_widget_by_action(ItemFactory,FILENAME_FROM_TXT);
+ //if (widget) gtk_widget_set_sensitive(widget,FALSE);}
+}
+
+/*
+ * Callback to update state of check button to browse subdir into menu
+ */
+void Check_Menu_Item_Toggled_Browse_Subdir (GtkWidget *checkmenuitem)
+{
+ BROWSE_SUBDIR = GTK_CHECK_MENU_ITEM(checkmenuitem)->active;
+ Check_Menu_Item_Update_Browse_Subdir();
+}
+void Check_Menu_Item_Update_Browse_Subdir (void)
+{
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(CheckMenuItemBrowseSubdirMainMenu),BROWSE_SUBDIR);
+}
+
+/*
+ * Callback to update state of check button to show hiddendirectories into menu
+ */
+void Check_Menu_Item_Toggled_Browse_Hidden_Dir (GtkWidget *checkmenuitem)
+{
+ BROWSE_HIDDEN_DIR = GTK_CHECK_MENU_ITEM(checkmenuitem)->active;
+ Check_Menu_Item_Update_Browse_Hidden_Dir();
+
+ // Reload directory, in case we have changed BROWSE_HIDDEN_DIR
+ //Browser_Tree_Rebuild(NULL); // Commented, as already done in GtkToggleActionEntry for AM_BROWSER_HIDDEN_DIR
+}
+void Check_Menu_Item_Update_Browse_Hidden_Dir (void)
+{
+#ifndef WIN32
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(CheckMenuItemBrowseHiddenDirMainMenu),BROWSE_HIDDEN_DIR);
+#endif
+}
+
+
+
+
+
+
+
+
+
+/*
+ * Status bar functions
+ */
+GtkWidget *Create_Status_Bar (void)
+{
+ StatusBar = gtk_statusbar_new();
+ /* Specify a size to avoid statubar resizing if the message is too long */
+ gtk_widget_set_size_request(StatusBar, 200, -1);
+ /* Create serie */
+ StatusBarContext = gtk_statusbar_get_context_id(GTK_STATUSBAR(StatusBar),"Messages");
+
+ Statusbar_Message(_("Ready to start..."),TRUE);
+
+ gtk_widget_show(StatusBar);
+ return StatusBar;
+}
+
+gboolean Statusbar_Stop_Timer (void)
+{
+ gtk_statusbar_pop(GTK_STATUSBAR(StatusBar),StatusBarContext);
+ return FALSE; /* Stop the timer */
+}
+
+void Statusbar_Start_Timer (void)
+{
+ Statusbar_Remove_Timer();
+ StatusbarTimerId = g_timeout_add(4000,(GtkFunction)Statusbar_Stop_Timer,NULL);
+}
+
+void Statusbar_Remove_Timer (void)
+{
+ if (StatusbarTimerId)
+ {
+ g_source_remove(StatusbarTimerId);
+ StatusbarTimerId = 0;
+ }
+}
+
+/*
+ * Send a message to the status bar
+ * - with_timer: if TRUE, the message will be displayed during 4s
+ * if FALSE, the message will be displayed up to the next posted message
+ */
+void Statusbar_Message (gchar *message, gint with_timer)
+{
+ gchar *msg_temp;
+ if (!StatusBar) return;
+
+ /* Validate UTF8 */
+ if (!g_utf8_validate(message, -1, NULL))
+ msg_temp = convert_to_utf8(message);
+ else
+ msg_temp = g_strdup(message);
+
+ /* Remove a running timer */
+ Statusbar_Remove_Timer();
+
+ /* Pop last message */
+ gtk_statusbar_pop(GTK_STATUSBAR(StatusBar),StatusBarContext);
+
+ /* Push the given message */
+ gtk_statusbar_push(GTK_STATUSBAR(StatusBar),StatusBarContext,msg_temp);
+
+ g_free(msg_temp);
+
+ while (gtk_events_pending())
+ gtk_main_iteration();
+
+ if (with_timer)
+ Statusbar_Start_Timer();
+}
+
+
+
+
+
+
+
+/*
+ * Progress bar
+ */
+GtkWidget *Create_Progress_Bar (void)
+{
+ ProgressBar = gtk_progress_bar_new();
+
+ gtk_widget_show(ProgressBar);
+ return ProgressBar;
+}
diff --git a/src/bar.h b/src/bar.h
new file mode 100755
index 0000000..846c2f0
--- /dev/null
+++ b/src/bar.h
@@ -0,0 +1,165 @@
+/* bar.h - 2000/05/05 */
+/*
+ * EasyTAG - Tag editor for MP3 and Ogg Vorbis files
+ * Copyright (C) 2000-2003 Jerome Couderc <easytag@gmail.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef __BAR_H__
+#define __BAR_H__
+
+/***************
+ * Declaration *
+ ***************/
+GtkWidget *MenuBar;
+GtkWidget *ProgressBar;
+GtkUIManager *UIManager;
+GtkActionGroup *ActionGroup;
+guint StatusBarContext;
+
+GtkWidget *CheckMenuItemBrowseSubdirMainMenu;
+GtkWidget *CheckMenuItemBrowseHiddenDirMainMenu;
+
+#define MENU_FILE "FileMenu"
+#define MENU_BROWSER "BrowserMenu"
+#define MENU_SCANNER "ScannerMenu"
+#define MENU_MISC "MiscMenu"
+#define MENU_SETTINGS "SettingsMenu"
+#define MENU_HELP "HelpMenu"
+
+#define MENU_FILE_SORT_TAG "SortTagMenu"
+#define MENU_FILE_SORT_PROP "SortPropMenu"
+#define MENU_SORT_TAG_PATH "FileMenu/SortTagMenu"
+#define MENU_SORT_PROP_PATH "FileMenu/SortPropMenu"
+
+#define POPUP_FILE "FilePopup"
+#define POPUP_DIR "DirPopup"
+#define POPUP_SUBMENU_SCANNER "ScannerSubpopup"
+#define POPUP_DIR_RUN_AUDIO "DirPopupRunAudio"
+#define POPUP_LOG "LogPopup"
+
+#define AM_PREV "PreviousFile"
+#define AM_NEXT "NextFile"
+#define AM_FIRST "FirstFile"
+#define AM_LAST "LastFile"
+#define AM_SCAN "ScanFile"
+#define AM_REMOVE "RemoveTag"
+#define AM_UNDO "UndoFile"
+#define AM_REDO "RedoFile"
+#define AM_UNDO_HISTORY "Undo"
+#define AM_REDO_HISTORY "Redo"
+#define AM_SAVE "SaveFile"
+#define AM_SAVE_FORCED "SaveFileForced"
+#define AM_SELECT_ALL_FILES "SelAll"
+#define AM_UNSELECT_ALL_FILES "UnselAll"
+#define AM_INVERT_SELECTION "SelInv"
+#define AM_DELETE_FILE "DeleteFile"
+#define AM_LOAD_HOME_DIR "GoToHome"
+#define AM_LOAD_DEFAULT_DIR "GoToDefaultPath"
+#define AM_SET_PATH_AS_DEFAULT "SetDefaultPath"
+#define AM_RENAME_DIR "RenameDir"
+#define AM_BROWSE_SUBDIR "BrowseSubdir"
+#define AM_BROWSER_HIDDEN_DIR "BrowseHiddenDir"
+#define AM_COLLAPSE_TREE "CollapseTree"
+#define AM_INITIALIZE_TREE "RefreshTree"
+#define AM_RELOAD_DIRECTORY "ReloadDir"
+#define AM_TREE_OR_ARTISTALBUM_VIEW "ViewMode"
+#define AM_BROWSE_DIRECTORY_WITH "BrowseDir"
+#define AM_OPEN_FILE_WITH "OpenFile"
+#define AM_OPEN_OPTIONS_WINDOW "Preferences"
+#define AM_SCANNER_FILL_TAG "FillTag"
+#define AM_SCANNER_RENAME_FILE "RenameFile"
+#define AM_SCANNER_PROCESS_FIELDS "ProcessFields"
+#define AM_SEARCH_FILE "SearchFile"
+#define AM_CDDB_SEARCH_FILE "CDDBSearchFile"
+#define AM_CDDB_SEARCH "CDDBSearch"
+#define AM_FILENAME_FROM_TXT "LoadFilenames"
+#define AM_WRITE_PLAYLIST "WritePlaylist"
+#define AM_RUN_AUDIO_PLAYER "RunAudio"
+#define AM_OPEN_ABOUT_WINDOW "About"
+#define AM_QUIT "Quit"
+
+#define AM_ARTIST_RUN_AUDIO_PLAYER "ArtistRunAudio"
+#define AM_ARTIST_OPEN_FILE_WITH "ArtistOpenFile"
+#define AM_ALBUM_RUN_AUDIO_PLAYER "AlbumRunAudio"
+#define AM_ALBUM_OPEN_FILE_WITH "AlbumOpenFile"
+
+#define AM_LOG_CLEAN "CleanLog"
+
+#define AM_STOP "Stop"
+#define AM_VIEWMODE_TOGGLE "ViewModeToggle"
+
+#define AM_SORT_ASCENDING_FILENAME "SortFilenameAsc"
+#define AM_SORT_DESCENDING_FILENAME "SortFilenameDesc"
+#define AM_SORT_ASCENDING_CREATION_DATE "SortDateAsc"
+#define AM_SORT_DESCENDING_CREATION_DATE "SortDateDesc"
+#define AM_SORT_ASCENDING_TRACK_NUMBER "SortTrackNumAsc"
+#define AM_SORT_DESCENDING_TRACK_NUMBER "SortTrackNumDesc"
+#define AM_SORT_ASCENDING_TITLE "SortTitleAsc"
+#define AM_SORT_DESCENDING_TITLE "SortTitleDesc"
+#define AM_SORT_ASCENDING_ARTIST "SortArtistAsc"
+#define AM_SORT_DESCENDING_ARTIST "SortArtistDesc"
+#define AM_SORT_ASCENDING_ALBUM "SortAlbumAsc"
+#define AM_SORT_DESCENDING_ALBUM "SortAlbumDesc"
+#define AM_SORT_ASCENDING_YEAR "SortYearAsc"
+#define AM_SORT_DESCENDING_YEAR "SortYearDesc"
+#define AM_SORT_ASCENDING_GENRE "SortGenreAsc"
+#define AM_SORT_DESCENDING_GENRE "SortGenreDesc"
+#define AM_SORT_ASCENDING_COMMENT "SortCommentAsc"
+#define AM_SORT_DESCENDING_COMMENT "SortCommentDesc"
+#define AM_SORT_ASCENDING_COMPOSER "SortComposerAsc"
+#define AM_SORT_DESCENDING_COMPOSER "SortComposerDesc"
+#define AM_SORT_ASCENDING_ORIG_ARTIST "SortOrigArtistAsc"
+#define AM_SORT_DESCENDING_ORIG_ARTIST "SortOrigArtistDesc"
+#define AM_SORT_ASCENDING_COPYRIGHT "SortCopyrightAsc"
+#define AM_SORT_DESCENDING_COPYRIGHT "SortCopyrightDesc"
+#define AM_SORT_ASCENDING_URL "SortUrlAsc"
+#define AM_SORT_DESCENDING_URL "SortUrlDesc"
+#define AM_SORT_ASCENDING_ENCODED_BY "SortEncodedByAsc"
+#define AM_SORT_DESCENDING_ENCODED_BY "SortEncodedByDesc"
+#define AM_SORT_ASCENDING_FILE_TYPE "SortTypeAsc"
+#define AM_SORT_DESCENDING_FILE_TYPE "SortTypeDesc"
+#define AM_SORT_ASCENDING_FILE_SIZE "SortSizeAsc"
+#define AM_SORT_DESCENDING_FILE_SIZE "SortSizeDesc"
+#define AM_SORT_ASCENDING_FILE_DURATION "SortDurationAsc"
+#define AM_SORT_DESCENDING_FILE_DURATION "SortDurationDesc"
+#define AM_SORT_ASCENDING_FILE_BITRATE "SortBitrateAsc"
+#define AM_SORT_DESCENDING_FILE_BITRATE "SortBitrateDesc"
+#define AM_SORT_ASCENDING_FILE_SAMPLERATE "SortSamplerateAsc"
+#define AM_SORT_DESCENDING_FILE_SAMPLERATE "SortSamplerateDesc"
+
+typedef struct _Action_Pair Action_Pair;
+struct _Action_Pair {
+ const gchar *action;
+ GQuark quark;
+};
+
+/**************
+ * Prototypes *
+ **************/
+
+void Create_UI (GtkWidget **menubar, GtkWidget **toolbar);
+GtkWidget *Create_Status_Bar (void);
+void Statusbar_Message (gchar *message, gint with_timer);
+GtkWidget *Create_Progress_Bar (void);
+
+void Check_Menu_Item_Toggled_Browse_Subdir (GtkWidget *checkmenuitem);
+void Check_Menu_Item_Update_Browse_Subdir (void);
+void Check_Menu_Item_Toggled_Browse_Hidden_Dir (GtkWidget *checkmenuitem);
+void Check_Menu_Item_Update_Browse_Hidden_Dir (void);
+
+#endif /* __BAR_H__ */
diff --git a/src/browser.c b/src/browser.c
new file mode 100755
index 0000000..e807e7a
--- /dev/null
+++ b/src/browser.c
@@ -0,0 +1,4084 @@
+/* browser.c - 2000/04/28 */
+/*
+ * EasyTAG - Tag editor for MP3 and Ogg Vorbis files
+ * Copyright (C) 2000-2003 Jerome Couderc <easytag@gmail.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+/* Some parts of this browser are taken from:
+ * XMMS - Cross-platform multimedia player
+ * Copyright (C) 1998-1999 Peter Alm, Mikael Alm, Olle Hallnas,
+ * Thomas Nilsson and 4Front Technologies
+ */
+
+#include <config.h>
+
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+#include <gdk/gdk.h>
+#include <glib/gi18n-lib.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include "easytag.h"
+#include "browser.h"
+#include "et_core.h"
+#include "scan.h"
+#include "msgbox.h"
+#include "bar.h"
+#include "log.h"
+#include "misc.h"
+#include "setting.h"
+#include "charset.h"
+#include "dlm.h"
+
+#include <assert.h>
+
+#ifdef WIN32
+# include <windows.h>
+# include "win32/win32dep.h"
+#endif
+
+/* Pixmaps */
+#include "../pixmaps/opened_folder.xpm"
+#include "../pixmaps/closed_folder.xpm"
+#include "../pixmaps/closed_folder_locked.xpm"
+#ifdef WIN32
+#include "../pixmaps/ram_disk.xpm"
+#endif
+
+
+
+/****************
+ * Declarations *
+ ****************/
+
+// Pixmaps
+static GdkPixbuf *opened_folder_pixmap = NULL, *closed_folder_pixmap, *closed_folder_locked_pixmap;
+#ifdef WIN32
+static GdkPixbuf *harddrive_pixmap, *removable_pixmap, *cdrom_pixmap, *network_pixmap, *ramdisk_pixmap;
+#endif
+
+GtkWidget *BrowserTree; // Tree of directories
+GtkTreeStore *directoryTreeModel;
+GtkWidget *BrowserList; // List of files
+GtkListStore *fileListModel;
+GtkWidget *BrowserLabel;
+GtkWidget *BrowserButton;
+GtkWidget *BrowserNoteBook;
+GtkWidget *BrowserArtistList;
+GtkListStore *artistListModel;
+GtkWidget *BrowserAlbumList;
+GtkListStore *albumListModel;
+gchar *BrowserCurrentPath = NULL; // Path selected in the browser area (BrowserEntry or BrowserTree)
+
+GtkListStore *RunProgramModel;
+
+GtkWidget *RunProgramTreeWindow = NULL;
+GtkWidget *RunProgramListWindow = NULL;
+
+// The Rename Directory window
+GtkWidget *RenameDirectoryWindow = NULL;
+GtkWidget *RenameDirectoryCombo;
+GtkWidget *RenameDirectoryWithMask;
+GtkWidget *RenameDirectoryMaskCombo;
+GtkListStore *RenameDirectoryMaskModel = NULL;
+GtkWidget *RenameDirectoryMaskStatusIconBox;
+GtkWidget *RenameDirectoryPreviewLabel = NULL;
+
+guint blrs_idle_handler_id = 0;
+guint blru_idle_handler_id = 0;
+guint bl_row_selected;
+
+ET_File *LastBrowserListETFileSelected; // The last ETFile selected in the BrowserList
+
+
+gchar *Rename_Directory_Masks [] =
+{
+ "%a - %b",
+ "%a_-_%b",
+ "%a - %b (%y) - %g",
+ "%a_-_%b_(%y)_-_%g",
+ "VA - %b (%y)",
+ "VA_-_%b_(%y)",
+ NULL
+};
+
+
+/**************
+ * Prototypes *
+ **************/
+
+gboolean Browser_Tree_Key_Press (GtkWidget *tree, GdkEvent *event, gpointer data);
+void Browser_Tree_Set_Node_Visible (GtkWidget *directoryView, GtkTreePath * path);
+void Browser_List_Set_Row_Visible (GtkTreeModel *treeModel, GtkTreeIter *rowIter);
+void Browser_Tree_Disable (void);
+void Browser_Tree_Enable (void);
+void Browser_Tree_Initialize (void);
+gboolean Browser_Tree_Node_Selected (GtkTreeSelection *selection, gpointer user_data);
+void Browser_Tree_Rename_Directory (gchar *last_path, gchar *new_path);
+void Browser_Tree_Handle_Rename (GtkTreeIter *parentnode, gchar *old_path, gchar *new_path);
+
+static gint Browser_List_Key_Press (GtkWidget *list, GdkEvent *event, gpointer data);
+gboolean Browser_List_Button_Press (GtkTreeView *treeView, GdkEventButton *event);
+void Browser_List_Disable (void);
+void Browser_List_Enable (void);
+void Browser_List_Row_Selected (GtkTreeSelection * selection, gpointer data);
+gint Browser_List_Sort_Func (GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer data);
+void Browser_List_Select_All_Files (void);
+void Browser_List_Unselect_All_Files (void);
+void Browser_List_Invert_File_Selection (void);
+
+void Browser_Entry_Activated (void);
+void Browser_Entry_Disable (void);
+void Browser_Entry_Enable (void);
+
+void Browser_Button_Clicked (void);
+
+void Browser_Artist_List_Load_Files (ET_File *etfile_to_select);
+void Browser_Artist_List_Row_Selected (GtkTreeSelection *selection, gpointer data);
+void Browser_Artist_List_Set_Row_Appearance(GtkTreeIter *row);
+
+void Browser_Album_List_Load_Files (GList *albumlist, ET_File *etfile_to_select);
+void Browser_Album_List_Row_Selected (GtkTreeSelection *selection, gpointer data);
+void Browser_Album_List_Set_Row_Appearance(GtkTreeIter *row);
+
+gchar *Browser_Get_Current_Path (void);
+void Browser_Update_Current_Path (gchar *path);
+void Browser_Load_Home_Directory (void);
+void Browser_Load_Default_Directory (void);
+void Browser_Reload_Directory (void);
+
+gint Browser_Win32_Get_Drive_Root (gchar *drive, GtkTreeIter *rootNode, GtkTreePath **rootPath);
+
+static gboolean check_for_subdir (gchar *path);
+
+GtkTreePath *Find_Child_Node(GtkTreeIter *parent, gchar *searchtext);
+
+gboolean Check_For_Access_Permission (gchar *path);
+
+static void expand_cb (GtkWidget *tree, GtkTreeIter *iter, GtkTreePath *path, gpointer data);
+static void collapse_cb (GtkWidget *tree, GtkTreeIter *iter, GtkTreePath *treePath, gpointer data);
+
+/* Pop up menus */
+gboolean Browser_Popup_Menu_Handler (GtkMenu *menu, GdkEventButton *event);
+
+/* For window to rename a directory */
+void Browser_Open_Rename_Directory_Window (void);
+void Destroy_Rename_Directory_Window (void);
+void Rename_Directory (void);
+gboolean Rename_Directory_Window_Key_Press (GtkWidget *window, GdkEvent *event);
+void Rename_Directory_With_Mask_Toggled (void);
+
+/* For window to run a program with the directory */
+void Browser_Open_Run_Program_Tree_Window (void);
+void Destroy_Run_Program_Tree_Window (void);
+gboolean Run_Program_Tree_Window_Key_Press (GtkWidget *window, GdkEvent *event);
+void Run_Program_With_Directory (GtkObject *combobox);
+
+/* For window to run a program with the file */
+void Browser_Open_Run_Program_List_Window (void);
+void Destroy_Run_Program_List_Window (void);
+gboolean Run_Program_List_Window_Key_Press (GtkWidget *window, GdkEvent *event);
+void Run_Program_With_Selected_Files (GtkObject *combobox);
+
+gboolean Run_Program (gchar *program_name, GList *args_list);
+
+
+
+/*************
+ * Functions *
+ *************/
+/*
+ * Load home directory
+ */
+void Browser_Load_Home_Directory (void)
+{
+ Browser_Tree_Select_Dir(HOME_VARIABLE);
+}
+
+
+/*
+ * Load default directory
+ */
+void Browser_Load_Default_Directory (void)
+{
+ gchar *temp;
+ temp = g_strdup(DEFAULT_PATH_TO_MP3);
+
+ if (temp && g_utf8_strlen(temp, -1)>0)
+ {
+ Browser_Tree_Select_Dir(temp);
+ } else
+ {
+ g_free(temp);
+ temp = g_strdup(HOME_VARIABLE);
+ Browser_Tree_Select_Dir(HOME_VARIABLE);
+ }
+ g_free(temp);
+}
+
+
+/*
+ * Get the path from the selected node (row) in the browser
+ * Warning: return NULL if no row selected int the tree.
+ * Remember to free the value returned from this function!
+ */
+gchar *Browser_Tree_Get_Path_Of_Selected_Node (void)
+{
+ GtkTreeSelection *selection;
+ GtkTreeIter selectedIter;
+ gchar *path;
+
+ if (!BrowserTree) return NULL;
+
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(BrowserTree));
+ if (selection
+ && gtk_tree_selection_get_selected(selection, NULL, &selectedIter))
+ {
+ gtk_tree_model_get(GTK_TREE_MODEL(directoryTreeModel), &selectedIter,
+ TREE_COLUMN_FULL_PATH, &path, -1);
+ return path;
+ }else
+ {
+ return NULL;
+ }
+}
+
+
+/*
+ * Set the 'path' within the variable BrowserCurrentPath.
+ */
+void Browser_Update_Current_Path (gchar *path)
+{
+ /* Be sure that we aren't passing 'BrowserCurrentPath' as parameter of the function :
+ * to avoid some memory problems */
+ if (path == NULL || path == BrowserCurrentPath) return;
+
+ if (BrowserCurrentPath != NULL)
+ g_free(BrowserCurrentPath);
+ BrowserCurrentPath = g_strdup(path);
+
+#ifdef WIN32
+ /* On win32 : "c:\path\to\dir" succeed with stat() for example, while "c:\path\to\dir\" fails */
+ ET_Win32_Path_Remove_Trailing_Backslash(BrowserCurrentPath);
+#endif
+
+ if (strcmp(G_DIR_SEPARATOR_S,BrowserCurrentPath) == 0)
+ gtk_widget_set_sensitive(BrowserButton,FALSE);
+ else
+ gtk_widget_set_sensitive(BrowserButton,TRUE);
+}
+
+
+/*
+ * Return the current path
+ */
+gchar *Browser_Get_Current_Path (void)
+{
+ return BrowserCurrentPath;
+}
+
+/*
+ * Reload the current directory.
+ */
+void Browser_Reload_Directory (void)
+{
+ if (BrowserTree && BrowserCurrentPath != NULL)
+ {
+ // Unselect files, to automatically reload the file of the directory
+ GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(BrowserTree));
+ if (selection)
+ {
+ gtk_tree_selection_unselect_all(selection);
+ }
+ Browser_Tree_Select_Dir(BrowserCurrentPath);
+ }
+}
+
+/*
+ * Set the current path (selected node) in browser as default path (within config variable).
+ */
+void Set_Current_Path_As_Default (void)
+{
+ if (DEFAULT_PATH_TO_MP3 != NULL)
+ g_free(DEFAULT_PATH_TO_MP3);
+ DEFAULT_PATH_TO_MP3 = g_strdup(BrowserCurrentPath);
+ Statusbar_Message(_("New default path for files selected"),TRUE);
+}
+
+/*
+ * When you press the key 'enter' in the BrowserEntry to validate the text (browse the directory)
+ */
+void Browser_Entry_Activated (void)
+{
+ const gchar *path_utf8;
+ gchar *path;
+
+ path_utf8 = gtk_entry_get_text(GTK_ENTRY(GTK_BIN(BrowserEntryCombo)->child));
+ Add_String_To_Combo_List(GTK_LIST_STORE(BrowserEntryModel), (gchar *)path_utf8);
+
+ path = filename_from_display(path_utf8);
+
+ Browser_Tree_Select_Dir(path);
+ g_free(path);
+}
+
+/*
+ * Set a text into the BrowserEntry (and don't activate it)
+ */
+void Browser_Entry_Set_Text (gchar *text)
+{
+ if (!text || !BrowserEntryCombo)
+ return;
+
+ gtk_entry_set_text(GTK_ENTRY(GTK_BIN(BrowserEntryCombo)->child),text);
+}
+
+/*
+ * Button to go to parent directory
+ */
+void Browser_Button_Clicked (void)
+{
+ gchar *parent_dir, *path;
+
+ parent_dir = Browser_Get_Current_Path();
+ if (strlen(parent_dir)>1)
+ {
+ if ( parent_dir[strlen(parent_dir)-1]==G_DIR_SEPARATOR )
+ parent_dir[strlen(parent_dir)-1] = '\0';
+ path = g_path_get_dirname(parent_dir);
+
+ Browser_Tree_Select_Dir(path);
+ g_free(path);
+ }
+}
+
+/*
+ * Set a text into the BrowserLabel
+ */
+void Browser_Label_Set_Text (gchar *text)
+{
+ if (BrowserLabel && text)
+ gtk_label_set_text(GTK_LABEL(BrowserLabel),text?text:"");
+}
+
+/*
+ * Key Press events into browser tree
+ */
+gboolean Browser_Tree_Key_Press (GtkWidget *tree, GdkEvent *event, gpointer data)
+{
+ GdkEventKey *kevent;
+ GtkTreeIter SelectedNode;
+ GtkTreeModel *treeModel;
+ GtkTreeSelection *treeSelection;
+ GtkTreePath *treePath;
+
+ if (!tree) return FALSE;
+
+ treeSelection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree));
+
+ if (event && event->type==GDK_KEY_PRESS)
+ {
+ if (!gtk_tree_selection_get_selected(treeSelection, &treeModel, &SelectedNode))
+ return FALSE;
+
+ kevent = (GdkEventKey *)event;
+ treePath = gtk_tree_model_get_path(GTK_TREE_MODEL(treeModel), &SelectedNode);
+
+ switch(kevent->keyval)
+ {
+ case GDK_KP_Enter: /* Enter key in Num Pad */
+ case GDK_Return: /* 'Normal' Enter key */
+ case GDK_t: /* Expand/Collapse node */
+ case GDK_T: /* Expand/Collapse node */
+ if(gtk_tree_view_row_expanded(GTK_TREE_VIEW(tree), treePath))
+ gtk_tree_view_collapse_row(GTK_TREE_VIEW(tree), treePath);
+ else
+ gtk_tree_view_expand_row(GTK_TREE_VIEW(tree), treePath, FALSE);
+
+ gtk_tree_path_free(treePath);
+ return TRUE;
+ break;
+
+ case GDK_e: /* Expand node */
+ case GDK_E: /* Expand node */
+ gtk_tree_view_expand_row(GTK_TREE_VIEW(tree), treePath, FALSE);
+ gtk_tree_path_free(treePath);
+ return TRUE;
+ break;
+
+ case GDK_c: /* Collapse node */
+ case GDK_C: /* Collapse node */
+ gtk_tree_view_collapse_row(GTK_TREE_VIEW(tree), treePath);
+ gtk_tree_path_free(treePath);
+ return TRUE;
+ break;
+ }
+ gtk_tree_path_free(treePath);
+ }
+ return FALSE;
+}
+
+/*
+ * Key Press events into browser list
+ */
+gboolean Browser_List_Stop_Timer (guint *flag)
+{
+ *flag = FALSE;
+ return FALSE;
+}
+
+/*
+ * Key press into browser list
+ * - Delete = delete file
+ * Also tries to capture text input and relate it to files
+ */
+gboolean Browser_List_Key_Press (GtkWidget *list, GdkEvent *event, gpointer data)
+{
+ gchar *string, *current_filename = NULL, *current_filename_copy = NULL, *temp;
+ static gchar *key_string = NULL;
+ gint key_string_length;
+ static guint BrowserListTimerId = 0;
+ static gboolean timer_is_running = FALSE;
+ gint row;
+ gboolean valid;
+ GdkEventKey *kevent;
+
+ GtkTreePath *currentPath = NULL;
+ GtkTreeIter currentIter;
+ ET_File *currentETFile;
+
+ GtkTreeModel *fileListModel;
+ GtkTreeSelection *fileSelection;
+
+
+ if (!list) return FALSE;
+
+ fileListModel = gtk_tree_view_get_model(GTK_TREE_VIEW(list));
+ fileSelection = gtk_tree_view_get_selection(GTK_TREE_VIEW(list));
+
+ kevent = (GdkEventKey *)event;
+ if (event && event->type==GDK_KEY_PRESS)
+ {
+ if (gtk_tree_selection_count_selected_rows(fileSelection))
+ {
+ switch(kevent->keyval)
+ {
+ case GDK_Delete:
+ Action_Delete_Selected_Files();
+ return TRUE;
+ }
+ }
+ }
+
+ /*
+ * Tries to select file corresponding to the character sequence entered
+ */
+ if ( strlen(kevent->string)>0 ) // Alphanumeric key?
+ {
+ // If the timer is running: concatenate the character of the pressed key, else starts a new string
+ string = key_string;
+ if (timer_is_running)
+ key_string = g_strconcat(key_string,kevent->string,NULL);
+ else
+ key_string = g_strdup(kevent->string);
+ g_free(string);
+
+ // Remove the current timer
+ if (BrowserListTimerId)
+ {
+ g_source_remove(BrowserListTimerId);
+ BrowserListTimerId = 0;
+ }
+ // Start a new timer of 500ms
+ BrowserListTimerId = g_timeout_add(500,(GtkFunction)Browser_List_Stop_Timer,&timer_is_running);
+ timer_is_running = TRUE;
+
+ // Browse the list keeping the current classification
+ for (row=0; row < gtk_tree_model_iter_n_children(fileListModel, NULL); row++)
+ {
+ if (row == 0)
+ currentPath = gtk_tree_path_new_first();
+ else
+ gtk_tree_path_next(currentPath);
+
+ valid = gtk_tree_model_get_iter(GTK_TREE_MODEL(fileListModel), &currentIter, currentPath);
+ if (valid)
+ {
+ gtk_tree_model_get(GTK_TREE_MODEL(fileListModel), &currentIter,
+ LIST_FILE_POINTER, &currentETFile,
+ LIST_FILE_NAME, &current_filename,
+ -1);
+
+ /* UTF-8 comparison */
+ if (g_utf8_validate(key_string, -1, NULL) == FALSE)
+ {
+ temp = convert_to_utf8(key_string);
+ g_free(key_string);
+ key_string = temp;
+ }
+
+ key_string_length = g_utf8_strlen(key_string, -1);
+ current_filename_copy = g_malloc(strlen(current_filename) + 1);
+ g_utf8_strncpy(current_filename_copy, current_filename, key_string_length);
+
+ temp = g_utf8_casefold(current_filename_copy, -1);
+ g_free(current_filename_copy);
+ current_filename_copy = temp;
+
+ temp = g_utf8_casefold(key_string, -1);
+ g_free(key_string);
+ key_string = temp;
+
+ if (g_utf8_collate(current_filename_copy,key_string)==0 )
+ {
+ if (!gtk_tree_selection_iter_is_selected(fileSelection,&currentIter))
+ gtk_tree_selection_select_iter(fileSelection,&currentIter);
+
+ g_free(current_filename);
+ break;
+ }
+ g_free(current_filename);
+ }
+ }
+ g_free(current_filename_copy);
+ gtk_tree_path_free(currentPath);
+ }
+ return FALSE;
+}
+
+/*
+ * Action for double/triple click
+ */
+gboolean Browser_List_Button_Press (GtkTreeView *treeView, GdkEventButton *event)
+{
+ if (!event)
+ return FALSE;
+
+ if (event->type==GDK_2BUTTON_PRESS && event->button==1)
+ {
+ /* Double left mouse click */
+ // Select files of the same directory (usefull when browsing sub-directories)
+ GList *etfilelist = NULL;
+ gchar *path_ref = NULL;
+ gchar *patch_check = NULL;
+ GtkTreePath *currentPath = NULL;
+
+ if (!ETCore->ETFileDisplayed)
+ return FALSE;
+
+ // File taken as reference...
+ path_ref = g_path_get_dirname( ((File_Name *)ETCore->ETFileDisplayed->FileNameCur->data)->value );
+
+ // Search and select files of the same directory
+ etfilelist = g_list_first(ETCore->ETFileDisplayedList);
+ while (etfilelist)
+ {
+ // Path of the file to check if it is in the same directory
+ patch_check = g_path_get_dirname( ((File_Name *)((ET_File *)etfilelist->data)->FileNameCur->data)->value );
+
+ if ( path_ref && patch_check && strcmp(path_ref,patch_check)==0 )
+ {
+ // Use of 'currentPath' to try to increase speed. Indeed, in many
+ // cases, the next file to select, is the next in the list
+ currentPath = Browser_List_Select_File_By_Etfile2((ET_File *)etfilelist->data,TRUE,currentPath);
+ }
+ etfilelist = g_list_next(etfilelist);
+ g_free(patch_check);
+ }
+ g_free(path_ref);
+ if (currentPath)
+ gtk_tree_path_free(currentPath);
+ }else if (event->type==GDK_3BUTTON_PRESS && event->button==1)
+ {
+ /* Triple left mouse click */
+ // Select all files of the list
+ Action_Select_All_Files();
+ }
+ return FALSE;
+}
+
+/*
+ * Collapse (close) tree recursively up to the root node.
+ */
+void Browser_Tree_Collapse (void)
+{
+ GtkTreePath *rootPath;
+
+ if (!BrowserTree) return;
+
+ gtk_tree_view_collapse_all(GTK_TREE_VIEW(BrowserTree));
+
+#ifndef WIN32
+ /* But keep the main directory opened */
+ rootPath = gtk_tree_path_new_first();
+ gtk_tree_view_expand_to_path(GTK_TREE_VIEW(BrowserTree), rootPath);
+ gtk_tree_path_free(rootPath);
+#endif
+}
+
+
+/*
+ * Set a row (or node) visible in the TreeView (by scrolling the tree)
+ */
+void Browser_Tree_Set_Node_Visible (GtkWidget *directoryView, GtkTreePath * path)
+{
+ if (!directoryView) return;
+
+ gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(directoryView), path, NULL, TRUE, 0.5, 0.0);
+}
+
+
+/*
+ * Set a row visible in the file list (by scrolling the list)
+ */
+void Browser_List_Set_Row_Visible (GtkTreeModel *treeModel, GtkTreeIter *rowIter)
+{
+ /*
+ * TODO: Make this only scroll to the row if it is not visible
+ * (like in easytag GTK1)
+ * See function gtk_tree_view_get_visible_rect() ??
+ */
+ GtkTreePath *rowPath;
+
+ if (!treeModel) return;
+
+ rowPath = gtk_tree_model_get_path(treeModel, rowIter);
+ gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(BrowserList), rowPath, NULL, FALSE, 0, 0);
+ gtk_tree_path_free(rowPath);
+}
+
+/*
+ * Triggers when a new node in the browser tree is selected
+ * Do file-save confirmation, and then prompt the new dir to be loaded
+ */
+gboolean Browser_Tree_Node_Selected (GtkTreeSelection *selection, gpointer user_data)
+{
+ gchar *pathName, *pathName_utf8;
+ static int counter = 0;
+ GtkTreeIter selectedIter;
+ GtkTreePath *selectedPath;
+
+ if (!gtk_tree_selection_get_selected(selection, NULL, &selectedIter))
+ return TRUE;
+ selectedPath = gtk_tree_model_get_path(GTK_TREE_MODEL(directoryTreeModel), &selectedIter);
+
+ /* Open the node */
+ if (OPEN_SELECTED_BROWSER_NODE)
+ {
+ gtk_tree_view_expand_row(GTK_TREE_VIEW(BrowserTree), selectedPath, FALSE);
+ }
+
+ /* Don't start a new reading, if another one is running... */
+ if (ReadingDirectory == TRUE)
+ return TRUE;
+
+ //Browser_Tree_Set_Node_Visible(BrowserTree, selectedPath);
+ gtk_tree_path_free(selectedPath);
+ gtk_tree_model_get(GTK_TREE_MODEL(directoryTreeModel), &selectedIter,
+ TREE_COLUMN_FULL_PATH, &pathName, -1);
+ if (!pathName)
+ return FALSE;
+
+ /* Save the current displayed data */
+ ET_Save_File_Data_From_UI(ETCore->ETFileDisplayed);
+ Update_Command_Buttons_Sensivity(); // Not clean to put this here...
+
+ /* Check if all files have been saved before changing the directory */
+ if (ET_Check_If_All_Files_Are_Saved() != TRUE)
+ {
+ GtkWidget *msgbox = NULL;
+ gint button;
+
+ msgbox = msg_box_new(_("Confirm..."),_("Some files have been modified but not "
+ "saved...\nDo you want to save them before changing the directory?"),
+ GTK_STOCK_DIALOG_QUESTION,BUTTON_CANCEL,BUTTON_NO,BUTTON_YES,0);
+ msg_box_hide_check_button(MSG_BOX(msgbox));
+ button = msg_box_run(MSG_BOX(msgbox));
+ gtk_widget_destroy(msgbox);
+ switch (button)
+ {
+ case BUTTON_YES:
+ if (Save_All_Files_With_Answer(FALSE)==-1)
+ return TRUE;
+ break;
+ case BUTTON_NO:
+ break;
+ case BUTTON_CANCEL:
+ case -1:
+ return TRUE;
+ }
+ }
+
+ /* Memorize the current path */
+ Browser_Update_Current_Path(pathName);
+
+ /* Display the selected path into the BrowserEntry */
+ pathName_utf8 = filename_to_display(pathName);
+ gtk_entry_set_text(GTK_ENTRY(GTK_BIN(BrowserEntryCombo)->child), pathName_utf8);
+
+ /* Start to read the directory */
+ /* The first time 'counter' is equal to zero and if we don't want to load
+ * directory on startup, we skip the 'reading', but we must read it */
+ if (LOAD_ON_STARTUP || counter)
+ Read_Directory(pathName);
+ else
+ /* As we don't use the function 'Read_Directory' we must add this function here */
+ Update_Command_Buttons_Sensivity();
+ counter++;
+
+ g_free(pathName);
+ g_free(pathName_utf8);
+ return FALSE;
+}
+
+
+gint Browser_Win32_Get_Drive_Root (gchar *drive, GtkTreeIter *rootNode, GtkTreePath **rootPath)
+{
+ gint root_index;
+ gboolean found = FALSE;
+ GtkTreeIter parentNode;
+ gchar *nodeName;
+
+ gtk_tree_model_get_iter_first(GTK_TREE_MODEL(directoryTreeModel), &parentNode);
+
+ // Find root of path, ie: the drive letter
+ root_index = 0;
+
+ do
+ {
+ gtk_tree_model_get(GTK_TREE_MODEL(directoryTreeModel), &parentNode,
+ TREE_COLUMN_FULL_PATH, &nodeName, -1);
+ if (strncasecmp(drive,nodeName, strlen(drive)) == 0)
+ {
+ g_free(nodeName);
+ found = TRUE;
+ break;
+ }
+ root_index++;
+ } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(directoryTreeModel), &parentNode));
+
+ if (!found) return FALSE;
+
+ *rootNode = parentNode;
+ *rootPath = gtk_tree_path_new_from_indices(root_index, -1);
+
+ return TRUE;
+}
+
+
+/*
+ * Browser_Tree_Select_Dir: Select the directory corresponding to the 'path' in
+ * the tree browser, but it doesn't read it!
+ * Check if path is correct before selecting it. And returns 1 on success, else 0.
+ */
+gint Browser_Tree_Select_Dir (gchar *current_path)
+{
+ GtkTreePath *rootPath = NULL;
+ GtkTreeIter parentNode, currentNode;
+ struct stat stbuf;
+ gint index = 1; // Skip the first token as it is NULL due to leading /
+ gchar **parts;
+ gchar *nodeName;
+ gchar *temp;
+
+ if (!BrowserTree) return FALSE;
+
+ /* Load current_path */
+ if(!current_path || !*current_path)
+ {
+ return TRUE;
+ }
+
+#ifdef WIN32
+ /* On win32 : stat("c:\path\to\dir") succeed, while stat("c:\path\to\dir\") fails */
+ ET_Win32_Path_Remove_Trailing_Backslash(current_path);
+#endif
+
+ /* If path is invalid: inform the user, but load the first directories
+ * of the full path while parent directories are valid */
+ if (stat(current_path,&stbuf)==-1)
+ {
+ GtkWidget *msgbox;
+ gchar *msg;
+ gchar *current_path_utf8;
+
+ current_path_utf8 = filename_to_display(current_path);
+ msg = g_strdup_printf(_("The entered path is invalid!:\n%s\n(%s)"),
+ current_path_utf8,g_strerror(errno));
+ msgbox = msg_box_new(_("Error..."),msg,GTK_STOCK_DIALOG_ERROR,BUTTON_OK,0);
+ g_free(msg);
+ msg_box_hide_check_button(MSG_BOX(msgbox));
+ msg_box_run(MSG_BOX(msgbox));
+ gtk_widget_destroy(msgbox);
+ g_free(current_path_utf8);
+ return FALSE;
+ }
+
+ Browser_Update_Current_Path(current_path);
+
+ parts = g_strsplit((const gchar*)current_path, G_DIR_SEPARATOR_S, 0);
+
+ // Expand root node (fill parentNode and rootPath)
+#ifdef WIN32
+ if (!Browser_Win32_Get_Drive_Root(parts[0], &parentNode, &rootPath))
+ return FALSE;
+#else
+ gtk_tree_model_get_iter_first(GTK_TREE_MODEL(directoryTreeModel), &parentNode);
+ rootPath = gtk_tree_path_new_first();
+#endif
+ if (rootPath)
+ {
+ gtk_tree_view_expand_to_path(GTK_TREE_VIEW(BrowserTree), rootPath);
+ gtk_tree_path_free(rootPath);
+ }
+
+ while (parts[index]) // it is NULL-terminated
+ {
+ if (strlen(parts[index]) == 0)
+ {
+ index++;
+ continue;
+ }
+
+ if (!gtk_tree_model_iter_children(GTK_TREE_MODEL(directoryTreeModel), &currentNode, &parentNode))
+ {
+ break;
+ }
+ do
+ {
+ gtk_tree_model_get(GTK_TREE_MODEL(directoryTreeModel), &currentNode,
+ TREE_COLUMN_FULL_PATH, &temp, -1);
+ nodeName = g_path_get_basename(temp);
+ g_free(temp);
+#ifdef WIN32
+ if (strcasecmp(parts[index],nodeName) == 0)
+#else
+ if (strcmp(parts[index],nodeName) == 0)
+#endif
+ {
+ g_free(nodeName);
+ break;
+ }
+ g_free(nodeName);
+ } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(directoryTreeModel), &currentNode));
+
+ parentNode = currentNode;
+ rootPath = gtk_tree_model_get_path(GTK_TREE_MODEL(directoryTreeModel), &parentNode);
+ if (rootPath)
+ {
+ gtk_tree_view_expand_to_path(GTK_TREE_VIEW(BrowserTree), rootPath);
+ gtk_tree_path_free(rootPath);
+ }
+ index++;
+ }
+
+ rootPath = gtk_tree_model_get_path(GTK_TREE_MODEL(directoryTreeModel), &parentNode);
+ if (rootPath)
+ {
+ gtk_tree_view_expand_to_path(GTK_TREE_VIEW(BrowserTree), rootPath);
+ gtk_tree_selection_select_path(gtk_tree_view_get_selection(GTK_TREE_VIEW(BrowserTree)), rootPath);
+ Browser_Tree_Set_Node_Visible(BrowserTree, rootPath);
+ gtk_tree_path_free(rootPath);
+ }
+
+ g_strfreev(parts);
+ return TRUE;
+}
+
+/*
+ * Callback to select-row event
+ * Displays the file info of the lowest selected file in the right-hand pane
+ */
+void Browser_List_Row_Selected (GtkTreeSelection *selection, gpointer data)
+{
+ GList *selectedRows;
+ GtkTreePath *lastSelected;
+ GtkTreeIter lastFile;
+ ET_File *fileETFile;
+
+ selectedRows = gtk_tree_selection_get_selected_rows(selection, NULL);
+
+ /*
+ * After a file is deleted, this function is called :
+ * So we must handle the situation if no rows are selected
+ */
+ if (g_list_length(selectedRows) == 0)
+ {
+ g_list_foreach(selectedRows, (GFunc) gtk_tree_path_free, NULL);
+ g_list_free(selectedRows);
+ return;
+ }
+
+ if (!LastBrowserListETFileSelected)
+ {
+ // Returns the last line selected (in ascending line order) to display the item
+ lastSelected = (GtkTreePath *)g_list_last(selectedRows)->data;
+ if (gtk_tree_model_get_iter(GTK_TREE_MODEL(fileListModel), &lastFile, lastSelected))
+ gtk_tree_model_get(GTK_TREE_MODEL(fileListModel), &lastFile, LIST_FILE_POINTER, &fileETFile, -1);
+
+ Action_Select_Nth_File_By_Etfile(fileETFile);
+ }else
+ {
+ // The real last selected line
+ Action_Select_Nth_File_By_Etfile(LastBrowserListETFileSelected);
+ }
+
+ g_list_foreach(selectedRows, (GFunc) gtk_tree_path_free, NULL);
+ g_list_free(selectedRows);
+}
+
+/*
+ * Loads the specified etfilelist into the browser list
+ * Also supports optionally selecting a specific etfile
+ * but be careful, this does not call Browser_List_Row_Selected !
+ */
+void Browser_List_Load_File_List (GList *etfilelist, ET_File *etfile_to_select)
+{
+ gboolean activate_bg_color = 0;
+ GtkTreeIter row;
+
+ if (!BrowserList) return;
+
+ gtk_list_store_clear(fileListModel);
+ etfilelist = g_list_first(etfilelist);
+ while (etfilelist)
+ {
+ guint fileKey = ((ET_File *)etfilelist->data)->ETFileKey;
+ gchar *current_filename_utf8 = ((File_Name *)((ET_File *)etfilelist->data)->FileNameCur->data)->value_utf8;
+ gchar *basename_utf8 = g_path_get_basename(current_filename_utf8);
+
+ // Change background color when changing directory (the first row must not be changed)
+ if (gtk_tree_model_iter_n_children(GTK_TREE_MODEL(fileListModel), NULL) > 0)
+ {
+ gchar *dir1_utf8;
+ gchar *dir2_utf8;
+ gchar *previous_filename_utf8 = ((File_Name *)((ET_File *)etfilelist->prev->data)->FileNameCur->data)->value_utf8;
+
+ dir1_utf8 = g_path_get_dirname(previous_filename_utf8);
+ dir2_utf8 = g_path_get_dirname(current_filename_utf8);
+
+ if (g_utf8_collate(dir1_utf8, dir2_utf8) != 0)
+ activate_bg_color = !activate_bg_color;
+
+ g_free(dir1_utf8);
+ g_free(dir2_utf8);
+ }
+
+ // File list displays the current filename (name on HD)
+ gtk_list_store_append(fileListModel, &row);
+ gtk_list_store_set(fileListModel, &row,
+ LIST_FILE_NAME, basename_utf8,
+ LIST_FILE_POINTER, etfilelist->data,
+ LIST_FILE_KEY, fileKey,
+ LIST_FILE_OTHERDIR, activate_bg_color,
+ -1);
+ g_free(basename_utf8);
+
+ if (etfile_to_select == etfilelist->data)
+ {
+ Browser_List_Select_File_By_Iter(&row, TRUE);
+ //ET_Display_File_Data_To_UI(etfilelist->data);
+ }
+
+ // Set appearance of the row
+ Browser_List_Set_Row_Appearance(&row);
+
+ etfilelist = g_list_next(etfilelist);
+ }
+}
+
+
+/*
+ * Update state of files in the list after changes (without clearing the list model!)
+ * - Refresh filename is file saved,
+ * - Change color is something change on the file
+ */
+void Browser_List_Refresh_Whole_List (void)
+{
+ ET_File *ETFile;
+ //GtkTreeIter iter;
+ GtkTreePath *currentPath = NULL;
+ GtkTreeIter iter;
+ gint row;
+ gchar *current_basename_utf8;
+ gboolean valid;
+ GtkWidget *TBViewMode;
+
+ if (!ETCore->ETFileDisplayedList || !BrowserList ||
+ gtk_tree_model_iter_n_children(GTK_TREE_MODEL(fileListModel), NULL) == 0)
+ {
+ return;
+ }
+
+ TBViewMode = gtk_ui_manager_get_widget(UIManager, "/ToolBar/ViewModeToggle");
+
+ // Browse the full list for changes
+ //gtk_tree_model_get_iter_first(GTK_TREE_MODEL(fileListModel), &iter);
+ // g_print("above worked %d rows\n", gtk_tree_model_iter_n_children(GTK_TREE_MODEL(fileListModel), NULL));
+
+ currentPath = gtk_tree_path_new_first();
+
+ valid = gtk_tree_model_get_iter(GTK_TREE_MODEL(fileListModel), &iter, currentPath);
+ while (valid)
+ {
+ // Refresh filename
+ gtk_tree_model_get(GTK_TREE_MODEL(fileListModel), &iter,
+ LIST_FILE_POINTER, &ETFile, -1);
+ current_basename_utf8 = g_path_get_basename( ((File_Name *)ETFile->FileNameCur->data)->value_utf8 );
+ gtk_list_store_set(fileListModel, &iter, LIST_FILE_NAME, current_basename_utf8, -1);
+ g_free(current_basename_utf8);
+
+ Browser_List_Set_Row_Appearance(&iter);
+
+ valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(fileListModel), &iter);
+ }
+ gtk_tree_path_free(currentPath);
+
+ // When displaying Artist + Album lists => refresh also rows color
+ if ( gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(TBViewMode)) )
+ {
+
+ for (row=0; row < gtk_tree_model_iter_n_children(GTK_TREE_MODEL(artistListModel), NULL); row++)
+ {
+ if (row == 0)
+ currentPath = gtk_tree_path_new_first();
+ else
+ gtk_tree_path_next(currentPath);
+
+ gtk_tree_model_get_iter(GTK_TREE_MODEL(artistListModel), &iter, currentPath);
+ Browser_Artist_List_Set_Row_Appearance(&iter);
+ }
+ gtk_tree_path_free(currentPath);
+
+
+ for (row=0; row < gtk_tree_model_iter_n_children(GTK_TREE_MODEL(albumListModel), NULL); row++)
+ {
+ if (row == 0)
+ currentPath = gtk_tree_path_new_first();
+ else
+ gtk_tree_path_next(currentPath);
+
+ gtk_tree_model_get_iter(GTK_TREE_MODEL(albumListModel), &iter, currentPath);
+ Browser_Album_List_Set_Row_Appearance(&iter);
+ }
+ gtk_tree_path_free(currentPath);
+ }
+}
+
+
+/*
+ * Update state of one file in the list after changes (without clearing the clist!)
+ * - Refresh filename is file saved,
+ * - Change color is something change on the file
+ */
+void Browser_List_Refresh_File_In_List (ET_File *ETFile)
+{
+ GList *selectedRow = NULL;
+ GtkWidget *TBViewMode;
+ GtkTreeSelection *selection;
+ GtkTreeIter selectedIter;
+ GtkTreePath *currentPath = NULL;
+ ET_File *file;
+ gboolean row_found = FALSE;
+ gchar *current_basename_utf8;
+ gboolean valid;
+ gint row;
+ gchar *artist, *album;
+
+ if (!ETCore->ETFileDisplayedList || !BrowserList || !ETFile ||
+ gtk_tree_model_iter_n_children(GTK_TREE_MODEL(fileListModel), NULL) == 0)
+ {
+ return;
+ }
+
+ TBViewMode = gtk_ui_manager_get_widget(UIManager, "/ToolBar/ViewModeToggle");
+
+ // Search the row of the modified file to update it (when found: row_found=TRUE)
+ // 1/3. Get position of ETFile in ETFileList
+ if (row_found == FALSE)
+ {
+ valid = gtk_tree_model_iter_nth_child (GTK_TREE_MODEL(fileListModel), &selectedIter, NULL, ETFile->IndexKey-1);
+ if (valid)
+ {
+ gtk_tree_model_get(GTK_TREE_MODEL(fileListModel), &selectedIter,
+ LIST_FILE_POINTER, &file, -1);
+ if (ETFile->ETFileKey == file->ETFileKey)
+ {
+ row_found = TRUE;
+ }
+ }
+ }
+
+ // 2/3. Try with the selected file in list (works only if we select the same file)
+ if (row_found == FALSE)
+ {
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(BrowserList));
+ selectedRow = gtk_tree_selection_get_selected_rows(selection, NULL);
+ if (selectedRow && selectedRow->data != NULL)
+ {
+ valid = gtk_tree_model_get_iter(GTK_TREE_MODEL(fileListModel), &selectedIter,
+ (GtkTreePath*) selectedRow->data);
+ if (valid)
+ {
+ gtk_tree_model_get(GTK_TREE_MODEL(fileListModel), &selectedIter,
+ LIST_FILE_POINTER, &file, -1);
+ if (ETFile->ETFileKey == file->ETFileKey)
+ {
+ row_found = TRUE;
+ }
+ }
+ }
+ g_list_foreach(selectedRow, (GFunc) gtk_tree_path_free, NULL);
+ g_list_free(selectedRow);
+ }
+
+ // 3/3. Fails, now we browse the full list to find it
+ if (row_found == FALSE)
+ {
+ valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(fileListModel), &selectedIter);
+ while (valid && !row_found)
+ {
+ gtk_tree_model_get(GTK_TREE_MODEL(fileListModel), &selectedIter,
+ LIST_FILE_POINTER, &file, -1);
+ if (ETFile->ETFileKey == file->ETFileKey)
+ {
+ row_found = TRUE;
+ } else
+ {
+ valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(fileListModel), &selectedIter);
+ }
+ }
+ }
+
+ // Error somewhere...
+ if (row_found == FALSE)
+ return;
+
+ // Displayed the filename
+ current_basename_utf8 = g_path_get_basename( ((File_Name *)file->FileNameCur->data)->value_utf8 );
+ gtk_list_store_set(fileListModel, &selectedIter, LIST_FILE_NAME, current_basename_utf8, -1);
+ g_free(current_basename_utf8);
+
+ // Change appearance (line to red) if filename changed
+ Browser_List_Set_Row_Appearance(&selectedIter);
+
+ // When displaying Artist + Album lists => refresh also rows color
+ if ( gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(TBViewMode)) )
+ {
+ gchar *current_artist = ((File_Tag *)ETFile->FileTag->data)->artist;
+ gchar *current_album = ((File_Tag *)ETFile->FileTag->data)->album;
+
+ for (row=0; row < gtk_tree_model_iter_n_children(GTK_TREE_MODEL(artistListModel), NULL); row++)
+ {
+ if (row == 0)
+ currentPath = gtk_tree_path_new_first();
+ else
+ gtk_tree_path_next(currentPath);
+
+ valid = gtk_tree_model_get_iter(GTK_TREE_MODEL(artistListModel), &selectedIter, currentPath);
+ if (valid)
+ {
+ gtk_tree_model_get(GTK_TREE_MODEL(artistListModel), &selectedIter, ARTIST_NAME, &artist, -1);
+
+ if ( (!current_artist && !artist)
+ || (current_artist && artist && g_utf8_collate(current_artist,artist)==0) )
+ {
+ // Set color of the row
+ Browser_Artist_List_Set_Row_Appearance(&selectedIter);
+ g_free(artist);
+ break;
+ }
+ g_free(artist);
+ }
+ }
+ gtk_tree_path_free(currentPath); currentPath = NULL;
+
+ //
+ // FIX ME : see also if we must add a new line / or change list of the ETFile
+ //
+ for (row=0; row < gtk_tree_model_iter_n_children(GTK_TREE_MODEL(albumListModel), NULL); row++)
+ {
+ if (row == 0)
+ currentPath = gtk_tree_path_new_first();
+ else
+ gtk_tree_path_next(currentPath);
+
+ valid = gtk_tree_model_get_iter(GTK_TREE_MODEL(albumListModel), &selectedIter, currentPath);
+ if (valid)
+ {
+ gtk_tree_model_get(GTK_TREE_MODEL(albumListModel), &selectedIter, ALBUM_NAME, &album, -1);
+
+ if ( (!current_album && !album)
+ || (current_album && album && g_utf8_collate(current_album,album)==0) )
+ {
+ // Set color of the row
+ Browser_Album_List_Set_Row_Appearance(&selectedIter);
+ g_free(album);
+ break;
+ }
+ g_free(album);
+ }
+ }
+ gtk_tree_path_free(currentPath); currentPath = NULL;
+
+ //
+ // FIX ME : see also if we must add a new line / or change list of the ETFile
+ //
+ }
+}
+
+
+/*
+ * Set the appearance of the row
+ */
+void Browser_List_Set_Row_Appearance (GtkTreeIter *iter)
+{
+ ET_File *rowETFile;
+ gboolean otherdir = FALSE;
+ GdkColor *backgroundcolor;
+ gchar *temp;
+
+ if (iter == NULL)
+ return;
+
+ // Get the ETFile reference
+ gtk_tree_model_get(GTK_TREE_MODEL(fileListModel), iter,
+ LIST_FILE_POINTER, &rowETFile,
+ LIST_FILE_OTHERDIR, &otherdir,
+ LIST_FILE_NAME, &temp, -1);
+
+ if (otherdir)
+ backgroundcolor = &LIGHT_BLUE;
+ else
+ backgroundcolor = NULL;
+
+ // Set text to bold/red if filename or tag changed
+ if ( ET_Check_If_File_Is_Saved(rowETFile) == FALSE )
+ {
+ if (CHANGED_FILES_DISPLAYED_TO_BOLD)
+ {
+ gtk_list_store_set(fileListModel, iter, LIST_FONT_WEIGHT, PANGO_WEIGHT_BOLD, LIST_ROW_BACKGROUND, backgroundcolor, LIST_ROW_FOREGROUND, NULL, -1);
+ } else
+ {
+ gtk_list_store_set(fileListModel, iter, LIST_FONT_WEIGHT, PANGO_WEIGHT_NORMAL, LIST_ROW_BACKGROUND, backgroundcolor, LIST_ROW_FOREGROUND, &RED, -1);
+ }
+ } else
+ {
+ gtk_list_store_set(fileListModel, iter, LIST_FONT_WEIGHT, PANGO_WEIGHT_NORMAL, LIST_ROW_BACKGROUND, backgroundcolor, LIST_ROW_FOREGROUND, NULL ,-1);
+ }
+ // Frees allocated item from gtk_tree_model_get...
+ g_free(temp);
+}
+
+
+/*
+ * Remove a file from the list, by ETFile
+ */
+void Browser_List_Remove_File (ET_File *searchETFile)
+{
+ gint row;
+ GtkTreePath *currentPath = NULL;
+ GtkTreeIter currentIter;
+ ET_File *currentETFile;
+ gboolean valid;
+
+ if (searchETFile == NULL)
+ return;
+
+ // Go through the file list until it is found
+ for (row=0; row < gtk_tree_model_iter_n_children(GTK_TREE_MODEL(fileListModel), NULL); row++)
+ {
+ if (row == 0)
+ currentPath = gtk_tree_path_new_first();
+ else
+ gtk_tree_path_next(currentPath);
+
+ valid = gtk_tree_model_get_iter(GTK_TREE_MODEL(fileListModel), &currentIter, currentPath);
+ if (valid)
+ {
+ gtk_tree_model_get(GTK_TREE_MODEL(fileListModel), &currentIter, LIST_FILE_POINTER, &currentETFile, -1);
+
+ if (currentETFile == searchETFile)
+ {
+ // Reinit this value to avoid a crash after deleting files...
+ if (LastBrowserListETFileSelected == searchETFile)
+ LastBrowserListETFileSelected = NULL;
+
+ gtk_list_store_remove(fileListModel, &currentIter);
+ break;
+ }
+ }
+ }
+}
+
+/*
+ * Get ETFile pointer of a file from a Tree Iter
+ */
+ET_File *Browser_List_Get_ETFile_From_Path (GtkTreePath *path)
+{
+ GtkTreeIter iter;
+
+ if (!gtk_tree_model_get_iter(GTK_TREE_MODEL(fileListModel), &iter, path))
+ return NULL;
+
+ return Browser_List_Get_ETFile_From_Iter(&iter);
+}
+
+/*
+ * Get ETFile pointer of a file from a Tree Iter
+ */
+ET_File *Browser_List_Get_ETFile_From_Iter (GtkTreeIter *iter)
+{
+ ET_File *etfile;
+
+ gtk_tree_model_get(GTK_TREE_MODEL(fileListModel), iter, LIST_FILE_POINTER, &etfile, -1);
+ return etfile;
+}
+
+
+/*
+ * Select the specified file in the list, by its ETFile
+ */
+void Browser_List_Select_File_By_Etfile (ET_File *searchETFile, gboolean select_it)
+{
+ GtkTreePath *currentPath = NULL;
+
+ currentPath = Browser_List_Select_File_By_Etfile2(searchETFile, select_it, NULL);
+ if (currentPath)
+ gtk_tree_path_free(currentPath);
+}
+/*
+ * Select the specified file in the list, by its ETFile
+ * - startPath : if set : starting path to try increase speed
+ * - returns allocated "currentPath" to free
+ */
+GtkTreePath *Browser_List_Select_File_By_Etfile2 (ET_File *searchETFile, gboolean select_it, GtkTreePath *startPath)
+{
+ gint row;
+ GtkTreePath *currentPath = NULL;
+ GtkTreeIter currentIter;
+ ET_File *currentETFile;
+ gboolean valid;
+
+ if (searchETFile == NULL)
+ return NULL;
+
+ // If the path is used, we try the next item (to increase speed), as it is correct in many cases...
+ if (startPath)
+ {
+ // Try the next path
+ gtk_tree_path_next(startPath);
+ valid = gtk_tree_model_get_iter(GTK_TREE_MODEL(fileListModel), &currentIter, startPath);
+ if (valid)
+ {
+ gtk_tree_model_get(GTK_TREE_MODEL(fileListModel), &currentIter, LIST_FILE_POINTER, &currentETFile, -1);
+ // It is the good file?
+ if (currentETFile == searchETFile)
+ {
+ Browser_List_Select_File_By_Iter(&currentIter, select_it);
+ return startPath;
+ }
+ }
+ }
+
+ // Else, we try the whole list...
+ // Go through the file list until it is found
+ currentPath = gtk_tree_path_new_first();
+ for (row=0; row < gtk_tree_model_iter_n_children(GTK_TREE_MODEL(fileListModel), NULL); row++)
+ {
+ valid = gtk_tree_model_get_iter(GTK_TREE_MODEL(fileListModel), &currentIter, currentPath);
+ if (valid)
+ {
+ gtk_tree_model_get(GTK_TREE_MODEL(fileListModel), &currentIter, LIST_FILE_POINTER, &currentETFile, -1);
+
+ if (currentETFile == searchETFile)
+ {
+ Browser_List_Select_File_By_Iter(&currentIter, select_it);
+ return currentPath;
+ //break;
+ }
+ }
+ gtk_tree_path_next(currentPath);
+ }
+ gtk_tree_path_free(currentPath);
+
+ return NULL;
+}
+
+
+/*
+ * Select the specified file in the list, by an iter
+ */
+void Browser_List_Select_File_By_Iter (GtkTreeIter *rowIter, gboolean select_it)
+{
+ if (!BrowserList) return;
+
+ if (select_it)
+ {
+ GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(BrowserList));
+ if (selection)
+ {
+ g_signal_handlers_block_by_func(G_OBJECT(selection),G_CALLBACK(Browser_List_Row_Selected),NULL);
+ gtk_tree_selection_select_iter(selection, rowIter);
+ g_signal_handlers_unblock_by_func(G_OBJECT(selection),G_CALLBACK(Browser_List_Row_Selected),NULL);
+ }
+ }
+ Browser_List_Set_Row_Visible(GTK_TREE_MODEL(fileListModel), rowIter);
+}
+
+/*
+ * Select the specified file in the list, by a string representation of an iter
+ * e.g. output of gtk_tree_model_get_string_from_iter()
+ */
+void Browser_List_Select_File_By_Iter_String (const gchar* stringIter, gboolean select_it)
+{
+ GtkTreeIter iter;
+
+ if (!fileListModel || !BrowserList) return;
+
+ if (gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(fileListModel), &iter, stringIter))
+ {
+ if (select_it)
+ {
+ GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(BrowserList));
+
+ // FIX ME : Why signal was blocked if selected? Don't remember...
+ if (selection)
+ {
+ //g_signal_handlers_block_by_func(G_OBJECT(selection),G_CALLBACK(Browser_List_Row_Selected),NULL);
+ gtk_tree_selection_select_iter(selection, &iter);
+ //g_signal_handlers_unblock_by_func(G_OBJECT(selection),G_CALLBACK(Browser_List_Row_Selected),NULL);
+ }
+ }
+ Browser_List_Set_Row_Visible(GTK_TREE_MODEL(fileListModel), &iter);
+ }
+}
+
+/*
+ * Select the specified file in the list, by fuzzy string matching based on
+ * the Damerau-Levenshtein Metric (patch from Santtu Lakkala - 23/08/2004)
+ */
+ET_File *Browser_List_Select_File_By_DLM (const gchar* string, gboolean select_it)
+{
+ GtkTreeIter iter;
+ GtkTreeIter iter2;
+ GtkTreeSelection *selection;
+ ET_File *current_etfile = NULL, *retval = NULL;
+ gchar *current_filename = NULL, *current_title = NULL;
+ int max = 0, this;
+
+ if (!fileListModel || !BrowserList) return NULL;
+
+ if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(fileListModel), &iter))
+ {
+ do
+ {
+ gtk_tree_model_get(GTK_TREE_MODEL(fileListModel), &iter,
+ LIST_FILE_NAME, &current_filename,
+ LIST_FILE_POINTER, &current_etfile, -1);
+ current_title = ((File_Tag *)current_etfile->FileTag->data)->title;
+
+ if ((this = dlm((current_title ? current_title : current_filename), string)) > max) // See "dlm.c"
+ {
+ max = this;
+ iter2 = iter;
+ retval = current_etfile;
+ }
+ } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(fileListModel), &iter));
+
+ if (select_it)
+ {
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(BrowserList));
+ if (selection)
+ {
+ g_signal_handlers_block_by_func(G_OBJECT(selection),G_CALLBACK(Browser_List_Row_Selected),NULL);
+ gtk_tree_selection_select_iter(selection, &iter2);
+ g_signal_handlers_unblock_by_func(G_OBJECT(selection),G_CALLBACK(Browser_List_Row_Selected),NULL);
+ }
+ }
+ Browser_List_Set_Row_Visible(GTK_TREE_MODEL(fileListModel), &iter2);
+ }
+ return retval;
+}
+
+
+/*
+ * Unselect the specified file in the list, by its ETFile
+ */
+void Browser_List_Unselect_File_By_Etfile(ET_File *searchETFile)
+{
+ gint row;
+ GtkTreePath *currentPath = NULL;
+ GtkTreeIter currentIter;
+ ET_File *currentETFile;
+ gboolean valid;
+
+ if (searchETFile == NULL)
+ return;
+
+ // Go through the file list until it is found
+ for (row=0; row < gtk_tree_model_iter_n_children(GTK_TREE_MODEL(fileListModel), NULL); row++)
+ {
+ if (row == 0)
+ currentPath = gtk_tree_path_new_first();
+ else
+ gtk_tree_path_next(currentPath);
+
+ valid = gtk_tree_model_get_iter(GTK_TREE_MODEL(fileListModel), &currentIter, currentPath);
+ if (valid)
+ {
+ gtk_tree_model_get(GTK_TREE_MODEL(fileListModel), &currentIter, LIST_FILE_POINTER, &currentETFile, -1);
+
+ if (currentETFile == searchETFile)
+ {
+ Browser_List_Unselect_File_By_Iter(&currentIter);
+ break;
+ }
+ }
+ }
+}
+
+
+/*
+ * Unselect the specified file, by its iter.
+ */
+void Browser_List_Unselect_File_By_Iter (GtkTreeIter *rowIter)
+{
+ GtkTreeSelection *selection;
+
+ if (!BrowserList) return;
+
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(BrowserList));
+ if (selection)
+ {
+ g_signal_handlers_block_by_func(G_OBJECT(selection),G_CALLBACK(Browser_List_Row_Selected),NULL);
+ gtk_tree_selection_unselect_iter(selection, rowIter);
+ g_signal_handlers_unblock_by_func(G_OBJECT(selection),G_CALLBACK(Browser_List_Row_Selected),NULL);
+ }
+}
+
+
+/*
+ * Unselect the specified file in the list, by a string representation of an iter
+ * e.g. output of gtk_tree_model_get_string_from_iter()
+ */
+void Browser_List_Unselect_File_By_Iter_String(const gchar* stringIter)
+{
+ GtkTreeIter iter;
+
+ if (gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(fileListModel), &iter, stringIter))
+ Browser_List_Unselect_File_By_Iter(&iter);
+}
+
+/*
+ * Clear all entries on the file list
+ */
+void Browser_List_Clear()
+{
+ gtk_list_store_clear(fileListModel);
+ gtk_list_store_clear(artistListModel);
+ gtk_list_store_clear(albumListModel);
+
+}
+
+/*
+ * Refresh the list sorting (call me after SORTING_FILE_MODE has changed)
+ */
+void Browser_List_Refresh_Sort (void)
+{
+ gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(fileListModel), 0, Browser_List_Sort_Func, NULL, NULL);
+}
+
+/*
+ * Intelligently sort the file list based on the current sorting method
+ */
+gint Browser_List_Sort_Func (GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer data)
+{
+ ET_File *ETFile1;
+ ET_File *ETFile2;
+ //gchar *text1;
+ //gchar *text2;
+ gint result = 0;
+
+ gtk_tree_model_get(model, a, LIST_FILE_POINTER, &ETFile1, -1);
+ gtk_tree_model_get(model, b, LIST_FILE_POINTER, &ETFile2, -1);
+ //gtk_tree_model_get(model, a, LIST_FILE_POINTER, &ETFile1, LIST_FILE_NAME, &text1, -1);
+ //gtk_tree_model_get(model, b, LIST_FILE_POINTER, &ETFile2, LIST_FILE_NAME, &text2, -1);
+
+ switch (SORTING_FILE_MODE)
+ {
+ case SORTING_UNKNOWN:
+ case SORTING_BY_ASCENDING_FILENAME:
+ result = ET_Comp_Func_Sort_File_By_Ascending_Filename(ETFile1, ETFile2);
+ break;
+ case SORTING_BY_DESCENDING_FILENAME:
+ result = ET_Comp_Func_Sort_File_By_Descending_Filename(ETFile1, ETFile2);
+ break;
+ case SORTING_BY_ASCENDING_TRACK_NUMBER:
+ result = ET_Comp_Func_Sort_File_By_Ascending_Track_Number(ETFile1, ETFile2);
+ break;
+ case SORTING_BY_DESCENDING_TRACK_NUMBER:
+ result = ET_Comp_Func_Sort_File_By_Descending_Track_Number(ETFile1, ETFile2);
+ break;
+ case SORTING_BY_ASCENDING_CREATION_DATE:
+ result = ET_Comp_Func_Sort_File_By_Ascending_Creation_Date(ETFile1, ETFile2);
+ break;
+ case SORTING_BY_DESCENDING_CREATION_DATE:
+ result = ET_Comp_Func_Sort_File_By_Descending_Creation_Date(ETFile1, ETFile2);
+ break;
+ case SORTING_BY_ASCENDING_TITLE:
+ result = ET_Comp_Func_Sort_File_By_Ascending_Title(ETFile1, ETFile2);
+ break;
+ case SORTING_BY_DESCENDING_TITLE:
+ result = ET_Comp_Func_Sort_File_By_Descending_Title(ETFile1, ETFile2);
+ break;
+ case SORTING_BY_ASCENDING_ARTIST:
+ result = ET_Comp_Func_Sort_File_By_Ascending_Artist(ETFile1, ETFile2);
+ break;
+ case SORTING_BY_DESCENDING_ARTIST:
+ result = ET_Comp_Func_Sort_File_By_Descending_Artist(ETFile1, ETFile2);
+ break;
+ case SORTING_BY_ASCENDING_ALBUM:
+ result = ET_Comp_Func_Sort_File_By_Ascending_Album(ETFile1, ETFile2);
+ break;
+ case SORTING_BY_DESCENDING_ALBUM:
+ result = ET_Comp_Func_Sort_File_By_Descending_Album(ETFile1, ETFile2);
+ break;
+ case SORTING_BY_ASCENDING_YEAR:
+ result = ET_Comp_Func_Sort_File_By_Ascending_Year(ETFile1, ETFile2);
+ break;
+ case SORTING_BY_DESCENDING_YEAR:
+ result = ET_Comp_Func_Sort_File_By_Descending_Year(ETFile1, ETFile2);
+ break;
+ case SORTING_BY_ASCENDING_GENRE:
+ result = ET_Comp_Func_Sort_File_By_Ascending_Genre(ETFile1, ETFile2);
+ break;
+ case SORTING_BY_DESCENDING_GENRE:
+ result = ET_Comp_Func_Sort_File_By_Descending_Genre(ETFile1, ETFile2);
+ break;
+ case SORTING_BY_ASCENDING_COMMENT:
+ result = ET_Comp_Func_Sort_File_By_Ascending_Comment(ETFile1, ETFile2);
+ break;
+ case SORTING_BY_DESCENDING_COMMENT:
+ result = ET_Comp_Func_Sort_File_By_Descending_Comment(ETFile1, ETFile2);
+ break;
+ case SORTING_BY_ASCENDING_COMPOSER:
+ result = ET_Comp_Func_Sort_File_By_Ascending_Composer(ETFile1, ETFile2);
+ break;
+ case SORTING_BY_DESCENDING_COMPOSER:
+ result = ET_Comp_Func_Sort_File_By_Descending_Composer(ETFile1, ETFile2);
+ break;
+ case SORTING_BY_ASCENDING_ORIG_ARTIST:
+ result = ET_Comp_Func_Sort_File_By_Ascending_Orig_Artist(ETFile1, ETFile2);
+ break;
+ case SORTING_BY_DESCENDING_ORIG_ARTIST:
+ result = ET_Comp_Func_Sort_File_By_Descending_Orig_Artist(ETFile1, ETFile2);
+ break;
+ case SORTING_BY_ASCENDING_COPYRIGHT:
+ result = ET_Comp_Func_Sort_File_By_Ascending_Copyright(ETFile1, ETFile2);
+ break;
+ case SORTING_BY_DESCENDING_COPYRIGHT:
+ result = ET_Comp_Func_Sort_File_By_Descending_Copyright(ETFile1, ETFile2);
+ break;
+ case SORTING_BY_ASCENDING_URL:
+ result = ET_Comp_Func_Sort_File_By_Ascending_Url(ETFile1, ETFile2);
+ break;
+ case SORTING_BY_DESCENDING_URL:
+ result = ET_Comp_Func_Sort_File_By_Descending_Url(ETFile1, ETFile2);
+ break;
+ case SORTING_BY_ASCENDING_ENCODED_BY:
+ result = ET_Comp_Func_Sort_File_By_Ascending_Encoded_By(ETFile1, ETFile2);
+ break;
+ case SORTING_BY_DESCENDING_ENCODED_BY:
+ result = ET_Comp_Func_Sort_File_By_Descending_Encoded_By(ETFile1, ETFile2);
+ break;
+ case SORTING_BY_ASCENDING_FILE_TYPE:
+ result = ET_Comp_Func_Sort_File_By_Ascending_File_Type(ETFile1, ETFile2);
+ break;
+ case SORTING_BY_DESCENDING_FILE_TYPE:
+ result = ET_Comp_Func_Sort_File_By_Descending_File_Type(ETFile1, ETFile2);
+ break;
+ case SORTING_BY_ASCENDING_FILE_SIZE:
+ result = ET_Comp_Func_Sort_File_By_Ascending_File_Size(ETFile1, ETFile2);
+ break;
+ case SORTING_BY_DESCENDING_FILE_SIZE:
+ result = ET_Comp_Func_Sort_File_By_Descending_File_Size(ETFile1, ETFile2);
+ break;
+ case SORTING_BY_ASCENDING_FILE_DURATION:
+ result = ET_Comp_Func_Sort_File_By_Ascending_File_Duration(ETFile1, ETFile2);
+ break;
+ case SORTING_BY_DESCENDING_FILE_DURATION:
+ result = ET_Comp_Func_Sort_File_By_Descending_File_Duration(ETFile1, ETFile2);
+ break;
+ case SORTING_BY_ASCENDING_FILE_BITRATE:
+ result = ET_Comp_Func_Sort_File_By_Ascending_File_Bitrate(ETFile1, ETFile2);
+ break;
+ case SORTING_BY_DESCENDING_FILE_BITRATE:
+ result = ET_Comp_Func_Sort_File_By_Descending_File_Bitrate(ETFile1, ETFile2);
+ break;
+ case SORTING_BY_ASCENDING_FILE_SAMPLERATE:
+ result = ET_Comp_Func_Sort_File_By_Ascending_File_Samplerate(ETFile1, ETFile2);
+ break;
+ case SORTING_BY_DESCENDING_FILE_SAMPLERATE:
+ result = ET_Comp_Func_Sort_File_By_Descending_File_Samplerate(ETFile1, ETFile2);
+ break;
+ }
+
+ // Frees allocated item from gtk_tree_model_get...
+ //g_free(text1);
+ //g_free(text2);
+
+ return result;
+}
+
+/*
+ * Select all files on the file list
+ */
+void Browser_List_Select_All_Files (void)
+{
+ GtkTreeSelection *selection;
+
+ if (!BrowserList) return;
+
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(BrowserList));
+ if (selection)
+ {
+ // Must block the select signal to avoid the selecting, one by one, of all files in the main files list
+ g_signal_handlers_block_by_func(G_OBJECT(selection),G_CALLBACK(Browser_List_Row_Selected),NULL);
+ gtk_tree_selection_select_all(selection);
+ g_signal_handlers_unblock_by_func(G_OBJECT(selection),G_CALLBACK(Browser_List_Row_Selected),NULL);
+ }
+}
+
+/*
+ * Unselect all files on the file list
+ */
+void Browser_List_Unselect_All_Files (void)
+{
+ GtkTreeSelection *selection;
+
+ if (!BrowserList) return;
+
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(BrowserList));
+ if (selection)
+ {
+ gtk_tree_selection_unselect_all(selection);
+ }
+}
+
+/*
+ * Invert the selection of the file list
+ */
+void Browser_List_Invert_File_Selection (void)
+{
+ GtkTreeIter iter;
+ GtkTreeSelection *selection;
+ gboolean valid;
+
+ if (!fileListModel || !BrowserList) return;
+
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(BrowserList));
+ if (selection)
+ {
+ /* Must block the select signal to avoid selecting all files (one by one) in the main files list */
+ g_signal_handlers_block_by_func(G_OBJECT(selection), G_CALLBACK(Browser_List_Row_Selected), NULL);
+ valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(fileListModel), &iter);
+ while (valid)
+ {
+ if (gtk_tree_selection_iter_is_selected(selection, &iter))
+ {
+ gtk_tree_selection_unselect_iter(selection, &iter);
+ } else
+ {
+ gtk_tree_selection_select_iter(selection, &iter);
+ }
+ valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(fileListModel), &iter);
+ }
+ g_signal_handlers_unblock_by_func(G_OBJECT(selection), G_CALLBACK(Browser_List_Row_Selected), NULL);
+ }
+}
+
+
+/*
+ * Load the list of Artists found in the tags
+ */
+gint Browser_Artist_List_Data_Comp_Func_Sort_By_Ascending_Album (ET_File *ETFile1, ET_File *ETFile2)
+{
+ gchar *current_album1 = ((File_Tag *)((GList *)ETFile1->FileTag)->data)->album;
+ gchar *current_album2 = ((File_Tag *)((GList *)ETFile2->FileTag)->data)->album;
+
+ if (!current_album1 && !current_album2)
+ return 0;
+ else if (!current_album1)
+ return -1;
+ else if (!current_album2)
+ return 1;
+ else
+ return strcmp(current_album1,current_album2);
+}
+
+void Browser_Artist_List_Load_Files (ET_File *etfile_to_select)
+{
+ GList *ArtistList;
+ GList *AlbumList;
+ GList *etfilelist;
+ ET_File *etfile;
+ GList *list;
+ GtkTreeIter iter;
+ GtkTreeSelection *selection;
+ gchar *artistname, *artist_to_select = NULL;
+
+ if (!BrowserArtistList) return;
+
+ if (etfile_to_select)
+ artist_to_select = ((File_Tag *)etfile_to_select->FileTag->data)->artist;
+
+ gtk_list_store_clear(artistListModel);
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(BrowserArtistList));
+
+ ArtistList = ETCore->ETArtistAlbumFileList;
+ while (ArtistList)
+ {
+ gint nbr_files = 0;
+
+ // Insert a line for each artist
+ AlbumList = (GList *)ArtistList->data;
+ etfilelist = (GList *)AlbumList->data;
+ etfile = (ET_File *)etfilelist->data;
+ artistname = ((File_Tag *)etfile->FileTag->data)->artist;
+
+ // Third column text : number of files
+ list = g_list_first(AlbumList);
+ while (list)
+ {
+ nbr_files += g_list_length(g_list_first((GList *)list->data));
+ list = list->next;
+ }
+
+ // Add the new row
+ gtk_list_store_append(artistListModel, &iter);
+ gtk_list_store_set(artistListModel, &iter,
+ ARTIST_NAME, artistname,
+ ARTIST_NUM_ALBUMS, g_list_length(g_list_first(AlbumList)),
+ ARTIST_NUM_FILES, nbr_files,
+ ARTIST_ALBUM_LIST_POINTER, AlbumList,
+ -1);
+
+ // Todo: Use something better than string comparison
+ if ( (!artistname && !artist_to_select)
+ || (artistname && artist_to_select && strcmp(artistname,artist_to_select) == 0) )
+ {
+ GtkTreePath *path = gtk_tree_model_get_path(GTK_TREE_MODEL(artistListModel), &iter);
+
+ g_signal_handlers_block_by_func(G_OBJECT(selection),G_CALLBACK(Browser_Artist_List_Row_Selected),NULL);
+ gtk_tree_selection_select_iter(selection, &iter);
+ g_signal_handlers_unblock_by_func(G_OBJECT(selection),G_CALLBACK(Browser_Artist_List_Row_Selected),NULL);
+
+ gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(BrowserArtistList), path, NULL, FALSE, 0, 0);
+ gtk_tree_path_free(path);
+
+ Browser_Album_List_Load_Files(AlbumList, etfile_to_select);
+
+ // Now that we've found the artist, no need to continue searching
+ artist_to_select = NULL;
+ }
+
+ // Set color of the row
+ Browser_Artist_List_Set_Row_Appearance(&iter);
+
+ ArtistList = ArtistList->next;
+ }
+
+ // Select the first line if we weren't asked to select anything
+ if (!etfile_to_select && gtk_tree_model_get_iter_first(GTK_TREE_MODEL(artistListModel), &iter))
+ {
+ gtk_tree_model_get(GTK_TREE_MODEL(artistListModel), &iter,
+ ARTIST_ALBUM_LIST_POINTER, &AlbumList,
+ -1);
+ ET_Save_File_Data_From_UI(ETCore->ETFileDisplayed);
+ Browser_Album_List_Load_Files(AlbumList,NULL);
+ }
+}
+
+
+/*
+ * Callback to select-row event
+ */
+void Browser_Artist_List_Row_Selected(GtkTreeSelection* selection, gpointer data)
+{
+ GList *AlbumList;
+ GtkTreeIter iter;
+
+ // Display the relevant albums
+ if(!gtk_tree_selection_get_selected(selection, NULL, &iter))
+ return; // We might be called with no row selected
+
+ // Save the current displayed data
+ ET_Save_File_Data_From_UI(ETCore->ETFileDisplayed);
+
+ gtk_tree_model_get(GTK_TREE_MODEL(artistListModel), &iter, ARTIST_ALBUM_LIST_POINTER, &AlbumList, -1);
+ Browser_Album_List_Load_Files(AlbumList, NULL);
+}
+
+/*
+ * Set the color of the row of BrowserArtistList
+ */
+void Browser_Artist_List_Set_Row_Appearance (GtkTreeIter *iter)
+{
+ GList *AlbumList;
+ GList *etfilelist;
+
+ gtk_tree_model_get(GTK_TREE_MODEL(artistListModel), iter, ARTIST_ALBUM_LIST_POINTER, &AlbumList, -1);
+
+ // Reset the style of the row if one of the files was changed
+ while (AlbumList)
+ {
+ etfilelist = (GList *)AlbumList->data;
+ while (etfilelist)
+ {
+ if ( ET_Check_If_File_Is_Saved((ET_File *)etfilelist->data) == FALSE )
+ {
+ if (CHANGED_FILES_DISPLAYED_TO_BOLD)
+ {
+ // Set the font-style to "bold"
+ gtk_list_store_set(artistListModel, iter, ARTIST_FONT_WEIGHT, PANGO_WEIGHT_BOLD, -1);
+ } else
+ {
+ // Set the background-color to "red"
+ gtk_list_store_set(artistListModel, iter, ARTIST_FONT_WEIGHT, PANGO_WEIGHT_NORMAL, ARTIST_ROW_FOREGROUND, &RED, -1);
+ }
+ break;
+ }
+ etfilelist = etfilelist->next;
+ }
+ AlbumList = AlbumList->next;
+ }
+}
+
+
+
+/*
+ * Load the list of Albums for each Artist
+ */
+void Browser_Album_List_Load_Files (GList *albumlist, ET_File *etfile_to_select)
+{
+ GList *AlbumList;
+ GList *etfilelist = NULL;
+ ET_File *etfile;
+ GtkTreeIter iter;
+ GtkTreeSelection *selection;
+ gchar *albumname, *album_to_select = NULL;
+
+ if (!BrowserAlbumList) return;
+
+ if (etfile_to_select)
+ album_to_select = ((File_Tag *)etfile_to_select->FileTag->data)->album;
+
+ gtk_list_store_clear(albumListModel);
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(BrowserAlbumList));
+
+ // Create a first row to select all albums of the artist
+ // FIX ME : the attached list must be freed!
+ AlbumList = albumlist;
+ while (AlbumList)
+ {
+ GList *etfilelist_tmp;
+ etfilelist_tmp = (GList *)AlbumList->data;
+ // We must make a copy to not "alter" the initial list by appending an other list
+ etfilelist_tmp = g_list_copy(etfilelist_tmp);
+ etfilelist = g_list_concat(etfilelist, etfilelist_tmp);
+
+ AlbumList = AlbumList->next;
+ }
+ gtk_list_store_append(albumListModel, &iter);
+ gtk_list_store_set(albumListModel, &iter,
+ ALBUM_NAME, _("<All albums>"),
+ ALBUM_NUM_FILES, g_list_length(g_list_first(etfilelist)),
+ ALBUM_ETFILE_LIST_POINTER, etfilelist,
+ -1);
+
+ // Create a line for each album of the artist
+ AlbumList = albumlist;
+ while (AlbumList)
+ {
+ // Insert a line for each album
+ etfilelist = (GList *)AlbumList->data;
+ etfile = (ET_File *)etfilelist->data;
+ albumname = ((File_Tag *)etfile->FileTag->data)->album;
+
+ // Add the new row
+ gtk_list_store_append(albumListModel, &iter);
+ gtk_list_store_set(albumListModel, &iter,
+ ALBUM_NAME, albumname,
+ ALBUM_NUM_FILES, g_list_length(g_list_first(etfilelist)),
+ ALBUM_ETFILE_LIST_POINTER, etfilelist,
+ -1);
+
+ if ( (!albumname && !album_to_select)
+ || (albumname && album_to_select && strcmp(albumname,album_to_select) == 0) )
+ {
+ GtkTreePath *path = gtk_tree_model_get_path(GTK_TREE_MODEL(albumListModel), &iter);
+
+ g_signal_handlers_block_by_func(G_OBJECT(selection),G_CALLBACK(Browser_Album_List_Row_Selected),NULL);
+ gtk_tree_selection_select_iter(selection, &iter);
+ g_signal_handlers_unblock_by_func(G_OBJECT(selection),G_CALLBACK(Browser_Album_List_Row_Selected),NULL);
+
+ gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(BrowserAlbumList), path, NULL, FALSE, 0, 0);
+ gtk_tree_path_free(path);
+
+ ET_Set_Displayed_File_List(etfilelist);
+ Browser_List_Load_File_List(etfilelist,etfile_to_select);
+
+ // Now that we've found the album, no need to continue searching
+ album_to_select = NULL;
+ }
+
+ // Set color of the row
+ Browser_Album_List_Set_Row_Appearance(&iter);
+
+ AlbumList = AlbumList->next;
+ }
+
+ // Select the first line if we werent asked to select anything
+ if (!etfile_to_select && gtk_tree_model_get_iter_first(GTK_TREE_MODEL(albumListModel), &iter))
+ {
+ gtk_tree_model_get(GTK_TREE_MODEL(albumListModel), &iter,
+ ALBUM_ETFILE_LIST_POINTER, &etfilelist,
+ -1);
+ ET_Save_File_Data_From_UI(ETCore->ETFileDisplayed);
+
+ // Set the attached list as "Displayed List"
+ ET_Set_Displayed_File_List(etfilelist);
+ Browser_List_Load_File_List(etfilelist, NULL);
+
+ // Displays the first item
+ Action_Select_Nth_File_By_Etfile((ET_File *)etfilelist->data);
+ }
+}
+
+/*
+ * Callback to select-row event
+ */
+void Browser_Album_List_Row_Selected (GtkTreeSelection *selection, gpointer data)
+{
+ GList *etfilelist;
+ GtkTreeIter iter;
+
+
+ // We might be called with no rows selected
+ if (!gtk_tree_selection_get_selected(selection, NULL, &iter))
+ return;
+
+ gtk_tree_model_get(GTK_TREE_MODEL(albumListModel), &iter, ALBUM_ETFILE_LIST_POINTER, &etfilelist, -1);
+
+ // Save the current displayed data
+ ET_Save_File_Data_From_UI(ETCore->ETFileDisplayed);
+
+ // Set the attached list as "Displayed List"
+ ET_Set_Displayed_File_List(etfilelist);
+
+ Browser_List_Load_File_List(etfilelist, NULL);
+
+ // Displays the first item
+ Action_Select_Nth_File_By_Etfile((ET_File *)etfilelist->data);
+}
+
+
+/*
+ * Set the color of the row of BrowserAlbumList
+ */
+void Browser_Album_List_Set_Row_Appearance (GtkTreeIter *iter)
+{
+ GList *etfilelist;
+
+ gtk_tree_model_get(GTK_TREE_MODEL(albumListModel), iter, ALBUM_ETFILE_LIST_POINTER, &etfilelist, -1);
+
+ // Reset the style of the row if one of the files was changed
+ while (etfilelist)
+ {
+ if ( ET_Check_If_File_Is_Saved((ET_File *)etfilelist->data) == FALSE )
+ {
+ if (CHANGED_FILES_DISPLAYED_TO_BOLD)
+ {
+ // Set the font-style to "bold"
+ gtk_list_store_set(albumListModel, iter, ALBUM_FONT_WEIGHT, PANGO_WEIGHT_BOLD, -1);
+ } else
+ {
+ // Set the background-color to "red"
+ gtk_list_store_set(albumListModel, iter, ALBUM_ROW_FOREGROUND, &RED, -1);
+ }
+ break;
+ }
+ etfilelist = etfilelist->next;
+ }
+}
+
+void Browser_Display_Tree_Or_Artist_Album_List (void)
+{
+ ET_File *etfile = ETCore->ETFileDisplayed; // ETFile to display again after changing browser view
+ GtkWidget *TBViewMode;
+
+ // Save the current displayed data
+ ET_Save_File_Data_From_UI(ETCore->ETFileDisplayed);
+
+ // Toggle button to switch view
+ TBViewMode = gtk_ui_manager_get_widget(UIManager, "/ToolBar/ViewModeToggle");
+
+ // Button pressed in the toolbar
+ if ( gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(TBViewMode)) )
+ {
+ /*
+ * Artist + Album view
+ */
+
+ // Display Artist + Album lists
+ gtk_notebook_set_current_page(GTK_NOTEBOOK(BrowserNoteBook),1);
+ ET_Create_Artist_Album_File_List();
+ Browser_Artist_List_Load_Files(etfile);
+
+ }else
+ {
+
+ /*
+ * Browser (classic) view
+ */
+ // Set the whole list as "Displayed list"
+ ET_Set_Displayed_File_List(ETCore->ETFileList);
+
+ // Display Tree Browser
+ gtk_notebook_set_current_page(GTK_NOTEBOOK(BrowserNoteBook),0);
+ Browser_List_Load_File_List(ETCore->ETFileDisplayedList, etfile);
+
+ // Displays the first file if nothing specified
+ if (!etfile)
+ {
+ GList *etfilelist = ET_Displayed_File_List_First();
+ if (etfilelist)
+ etfile = (ET_File *)etfilelist->data;
+ Action_Select_Nth_File_By_Etfile(etfile);
+ }
+ }
+
+ //ET_Display_File_Data_To_UI(etfile); // Causes a crash
+}
+
+/*
+ * Disable (FALSE) / Enable (TRUE) all user widgets in the browser area (Tree + List + Entry)
+ */
+void Browser_Area_Set_Sensitive (gboolean activate)
+{
+ gtk_widget_set_sensitive(GTK_WIDGET(BrowserEntryCombo),activate);
+ gtk_widget_set_sensitive(GTK_WIDGET(BrowserTree), activate);
+ gtk_widget_set_sensitive(GTK_WIDGET(BrowserList), activate);
+ gtk_widget_set_sensitive(GTK_WIDGET(BrowserArtistList),activate);
+ gtk_widget_set_sensitive(GTK_WIDGET(BrowserAlbumList), activate);
+ gtk_widget_set_sensitive(GTK_WIDGET(BrowserButton), activate);
+}
+
+
+/*
+ * Browser_Popup_Menu_Handler : displays the corresponding menu
+ */
+gboolean Browser_Popup_Menu_Handler (GtkMenu *menu, GdkEventButton *event)
+{
+ if (event && (event->type==GDK_BUTTON_PRESS) && (event->button == 3))
+ {
+ gtk_menu_popup(menu,NULL,NULL,NULL,NULL,event->button,event->time);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/*
+ * Destroy the whole tree up to the root node
+ */
+void Browser_Tree_Initialize (void)
+{
+ GtkTreeIter parent_iter;
+ GtkTreeIter dummy_iter;
+
+ if (!directoryTreeModel) return;
+
+ gtk_tree_store_clear(directoryTreeModel);
+
+#ifdef WIN32
+
+ /* Code strangely familiar with gtkfilesystemwin32.c */
+
+ GdkPixbuf *drive_pixmap;
+ DWORD drives;
+ UINT drive_type;
+ gchar drive[4] = "A:/";
+ gchar drive_backslashed[5] = "A:\\";
+ gchar drive_slashless[3] = "A:";
+ gchar drive_label[256];
+
+ drives = GetLogicalDrives();
+ if (!drives)
+ g_warning ("GetLogicalDrives failed.");
+
+ while (drives && drive[0] <= 'Z')
+ {
+ if (drives & 1)
+ {
+ char *drive_dir_name;
+
+ drive_type = GetDriveType(drive_backslashed);
+
+ // DRIVE_REMOVABLE 2
+ // DRIVE_FIXED 3
+ // DRIVE_REMOTE 4
+ // DRIVE_CDROM 5
+ // DRIVE_RAMDISK 6
+ // DRIVE_UNKNOWN 0
+ // DRIVE_NO_ROOT_DIR 1
+ switch(drive_type)
+ {
+ case DRIVE_FIXED:
+ drive_pixmap = harddrive_pixmap;
+ break;
+ case DRIVE_REMOVABLE:
+ drive_pixmap = removable_pixmap;
+ break;
+ case DRIVE_CDROM:
+ drive_pixmap = cdrom_pixmap;
+ break;
+ case DRIVE_REMOTE:
+ drive_pixmap = network_pixmap;
+ break;
+ case DRIVE_RAMDISK:
+ drive_pixmap = ramdisk_pixmap;
+ break;
+ default:
+ drive_pixmap = closed_folder_pixmap;
+ }
+
+ drive_label[0] = 0;
+
+ GetVolumeInformation(drive_backslashed, drive_label, 256, NULL, NULL, NULL, NULL, 0);
+
+ /* Drive letter first so alphabetical drive list order works */
+ drive_dir_name = g_strconcat("(", drive_slashless, ") ", drive_label, NULL);
+
+ gtk_tree_store_append(directoryTreeModel, &parent_iter, NULL);
+ gtk_tree_store_set(directoryTreeModel, &parent_iter,
+ TREE_COLUMN_DIR_NAME, drive_dir_name,
+ TREE_COLUMN_FULL_PATH, drive_backslashed,
+ TREE_COLUMN_HAS_SUBDIR, TRUE,
+ TREE_COLUMN_SCANNED, FALSE,
+ TREE_COLUMN_PIXBUF, drive_pixmap,
+ -1);
+ // Insert dummy node
+ gtk_tree_store_append(directoryTreeModel, &dummy_iter, &parent_iter);
+
+ g_free(drive_dir_name);
+ }
+ drives >>= 1;
+ drive[0]++;
+ drive_backslashed[0]++;
+ drive_slashless[0]++;
+ }
+
+#else
+
+ gtk_tree_store_append(directoryTreeModel, &parent_iter, NULL);
+ gtk_tree_store_set(directoryTreeModel, &parent_iter,
+ TREE_COLUMN_DIR_NAME, G_DIR_SEPARATOR_S,
+ TREE_COLUMN_FULL_PATH, G_DIR_SEPARATOR_S,
+ TREE_COLUMN_HAS_SUBDIR, TRUE,
+ TREE_COLUMN_SCANNED, FALSE,
+ TREE_COLUMN_PIXBUF, closed_folder_pixmap,
+ -1);
+ // insert dummy node
+ gtk_tree_store_append(directoryTreeModel, &dummy_iter, &parent_iter);
+
+#endif
+
+}
+
+/*
+ * Browser_Tree_Rebuild: Refresh the tree browser by destroying it and rebuilding it.
+ * Opens tree nodes corresponding to 'path_to_load' if this parameter isn't NULL.
+ * If NULL, selects the current path.
+ */
+void Browser_Tree_Rebuild (gchar *path_to_load)
+{
+ gchar *current_path = NULL;
+ GtkTreeSelection *selection;
+
+ /* May be called from GtkUIManager callback */
+ if (GTK_IS_ACTION(path_to_load))
+ path_to_load = NULL;
+
+ if (path_to_load != NULL)
+ {
+ Browser_Tree_Initialize();
+ Browser_Tree_Select_Dir(path_to_load);
+ return;
+ }
+
+ /* Memorize the current path to load it again at the end */
+ current_path = Browser_Tree_Get_Path_Of_Selected_Node();
+ if (current_path==NULL && BrowserEntryCombo)
+ {
+ /* If no node selected, get path from BrowserEntry or default path */
+ if (BrowserCurrentPath != NULL)
+ current_path = g_strdup(BrowserCurrentPath);
+ else if (g_utf8_strlen(gtk_entry_get_text(GTK_ENTRY(GTK_BIN(BrowserEntryCombo)->child)), -1) > 0)
+ current_path = filename_from_display(gtk_entry_get_text(GTK_ENTRY(GTK_BIN(BrowserEntryCombo)->child)));
+ else
+ current_path = g_strdup(DEFAULT_PATH_TO_MP3);
+ }
+
+ Browser_Tree_Initialize();
+ /* Select again the memorized path without loading files */
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(BrowserTree));
+ if (selection)
+ {
+ g_signal_handlers_block_by_func(G_OBJECT(selection),G_CALLBACK(Browser_Tree_Node_Selected),NULL);
+ Browser_Tree_Select_Dir(current_path);
+ g_signal_handlers_unblock_by_func(G_OBJECT(selection),G_CALLBACK(Browser_Tree_Node_Selected),NULL);
+ }
+ g_free(current_path);
+
+ Update_Command_Buttons_Sensivity();
+}
+
+/*
+ * Renames a directory
+ * last_path:
+ * new_path:
+ * Parameters are non-utf8!
+ */
+void Browser_Tree_Rename_Directory (gchar *last_path, gchar *new_path)
+{
+
+ gchar **textsplit;
+ gint i;
+ GtkTreeIter iter;
+ GtkTreePath *childpath;
+ GtkTreePath *parentpath;
+ gchar *new_basename;
+ gchar *new_basename_utf8;
+ gchar *path;
+ gboolean valid;
+
+ if (!last_path || !new_path)
+ return;
+
+ /*
+ * Find the existing tree entry
+ */
+ textsplit = g_strsplit(last_path, G_DIR_SEPARATOR_S, 0);
+
+#ifdef WIN32
+ if (!Browser_Win32_Get_Drive_Root(textsplit[0], &iter, &parentpath))
+ return;
+#else
+ parentpath = gtk_tree_path_new_first();
+#endif
+
+ for (i = 1; textsplit[i] != NULL; i++)
+ {
+ valid = gtk_tree_model_get_iter(GTK_TREE_MODEL(directoryTreeModel), &iter, parentpath);
+ childpath = Find_Child_Node(&iter, textsplit[i]);
+ if (childpath == NULL)
+ {
+ // ERROR! Could not find it!
+ gchar *text_utf8 = filename_to_display(textsplit[i]);
+ Log_Print(_("Error: Searching for %s, could not find node %s in tree."), last_path, text_utf8);
+ g_strfreev(textsplit);
+ g_free(text_utf8);
+ return;
+ }
+ gtk_tree_path_free(parentpath);
+ parentpath = childpath;
+ }
+
+ valid = gtk_tree_model_get_iter(GTK_TREE_MODEL(directoryTreeModel), &iter, parentpath);
+ gtk_tree_path_free(parentpath);
+
+ /* Rename the on-screen node */
+ new_basename = g_path_get_basename(new_path);
+ new_basename_utf8 = filename_to_display(new_basename);
+ gtk_tree_store_set(directoryTreeModel, &iter,
+ TREE_COLUMN_DIR_NAME, new_basename_utf8,
+ TREE_COLUMN_FULL_PATH, new_path,
+ -1);
+
+ /* Update fullpath of child nodes */
+ Browser_Tree_Handle_Rename(&iter, last_path, new_path);
+
+ /* Update the variable of the current path */
+ path = Browser_Tree_Get_Path_Of_Selected_Node();
+ Browser_Update_Current_Path(path);
+ g_free(path);
+
+ g_strfreev(textsplit);
+ g_free(new_basename);
+ g_free(new_basename_utf8);
+}
+
+/*
+ * Recursive function to update paths of all child nodes
+ */
+void Browser_Tree_Handle_Rename (GtkTreeIter *parentnode, gchar *old_path, gchar *new_path)
+{
+ GtkTreeIter iter;
+ gchar *path;
+ gchar *path_shift;
+ gchar *path_new;
+
+ // If there are no children then nothing needs to be done!
+ if(!gtk_tree_model_iter_children(GTK_TREE_MODEL(directoryTreeModel), &iter, parentnode))
+ return;
+
+ do
+ {
+ gtk_tree_model_get(GTK_TREE_MODEL(directoryTreeModel), &iter,
+ TREE_COLUMN_FULL_PATH, &path, -1);
+ if(path == NULL)
+ continue;
+
+ path_shift = g_utf8_offset_to_pointer(path, g_utf8_strlen(old_path, -1));
+ path_new = g_strconcat(new_path, path_shift, NULL);
+
+ gtk_tree_store_set(directoryTreeModel, &iter,
+ TREE_COLUMN_FULL_PATH, path_new, -1);
+
+ g_free(path_new);
+ g_free(path);
+
+ // Recurse if necessary
+ if(gtk_tree_model_iter_has_child(GTK_TREE_MODEL(directoryTreeModel), &iter))
+ Browser_Tree_Handle_Rename(&iter, old_path, new_path);
+
+ } while(gtk_tree_model_iter_next(GTK_TREE_MODEL(directoryTreeModel), &iter));
+
+}
+
+/*
+ * Find the child node of "parentnode" that has text of "childtext
+ * Returns NULL on failure
+ */
+GtkTreePath *Find_Child_Node (GtkTreeIter *parentnode, gchar *childtext)
+{
+ gint row;
+ GtkTreeIter iter;
+ gchar *text;
+ gchar *temp;
+
+ for (row=0; row < gtk_tree_model_iter_n_children(GTK_TREE_MODEL(directoryTreeModel), parentnode); row++)
+ {
+ if (row == 0)
+ {
+ if (gtk_tree_model_iter_children(GTK_TREE_MODEL(directoryTreeModel), &iter, parentnode) == FALSE) return NULL;
+ } else
+ {
+ if (gtk_tree_model_iter_next(GTK_TREE_MODEL(directoryTreeModel), &iter) == FALSE)
+ return NULL;
+ }
+ gtk_tree_model_get(GTK_TREE_MODEL(directoryTreeModel), &iter,
+ TREE_COLUMN_FULL_PATH, &temp, -1);
+ text = g_path_get_basename(temp);
+ g_free(temp);
+ if(strcmp(childtext,text) == 0)
+ {
+ g_free(text);
+ return gtk_tree_model_get_path(GTK_TREE_MODEL(directoryTreeModel), &iter);
+ }
+ g_free(text);
+
+ }
+
+ return NULL;
+}
+
+/*
+ * Check if path has any subdirectories
+ * Returns true if subdirectories exist.
+ * path should be in raw filename format (non-UTF8)
+ */
+static gboolean check_for_subdir (gchar *path)
+{
+ DIR *dir;
+ struct dirent *dirent;
+ struct stat statbuf;
+ gchar *npath;
+
+ if( (dir=opendir(path)) )
+ {
+ while( (dirent=readdir(dir)) )
+ {
+ // We don't read the directories '.' and '..', but may read hidden directories like '.mydir'
+ if ( (g_ascii_strcasecmp (dirent->d_name,"..") != 0)
+ && ( (g_ascii_strncasecmp(dirent->d_name,".", 1) != 0) || (BROWSE_HIDDEN_DIR && strlen(dirent->d_name) > 1)) )
+ {
+#ifdef WIN32
+ // On win32 : stat("/path/to/dir") succeed, while stat("/path/to/dir/") fails
+ npath = g_strconcat(path,dirent->d_name,NULL);
+#else
+ npath = g_strconcat(path,dirent->d_name,G_DIR_SEPARATOR_S,NULL);
+#endif
+
+ if (stat(npath,&statbuf) == -1)
+ {
+ g_free(npath);
+ continue;
+ }
+
+ g_free(npath);
+
+ if(S_ISDIR(statbuf.st_mode))
+ {
+ closedir(dir);
+ return TRUE;
+ }
+ }
+ }
+ closedir(dir);
+ }
+ return FALSE;
+}
+
+/*
+ * Check if you have access permissions for directory path. Returns 1 if ok, else 0.
+ */
+gboolean Check_For_Access_Permission (gchar *path)
+{
+ DIR *dir;
+
+ if( (dir=opendir(path)) == NULL )
+ {
+ if (errno == EACCES)
+ return FALSE;
+ } else
+ {
+ closedir(dir);
+ }
+ return TRUE;
+}
+
+
+/*
+ * Sets the selection function. If set, this function is called before any node
+ * is selected or unselected, giving some control over which nodes are selected.
+ * The select function should return TRUE if the state of the node may be toggled,
+ * and FALSE if the state of the node should be left unchanged.
+ */
+gboolean Browser_List_Select_Func (GtkTreeSelection *selection, GtkTreeModel *model, GtkTreePath *path, gboolean path_currently_selected, gpointer data)
+{
+ // This line will be selected at the end of the event.
+ // We store the last ETFile selected, as gtk_tree_selection_get_selected_rows
+ // returns the selection, in the ascending line order, instead of the real
+ // order of line selection (so we can't displayed the last selected file)
+ // FIXME : should generate a list to get the previous selected file if unselected the last selected file
+ if (!path_currently_selected)
+ {
+ GtkTreeIter iter;
+ if (gtk_tree_model_get_iter(GTK_TREE_MODEL(fileListModel), &iter, path))
+ gtk_tree_model_get(GTK_TREE_MODEL(fileListModel), &iter,
+ LIST_FILE_POINTER, &LastBrowserListETFileSelected, -1);
+ }else
+ {
+ LastBrowserListETFileSelected = NULL;
+ }
+ //g_print(">>>%s -> %d -> %x\n",gtk_tree_path_to_string(path),path_currently_selected,LastBrowserListETFileSelected);
+
+ return TRUE;
+}
+
+
+/*
+ * Open up a node on the browser tree
+ * Scanning and showing all subdirectories
+ */
+static void expand_cb (GtkWidget *tree, GtkTreeIter *iter, GtkTreePath *gtreePath, gpointer data)
+{
+ DIR *dir;
+ struct dirent *dirent;
+ gchar *path;
+ gchar *dirname_utf8;
+ struct stat statbuf;
+ gchar *fullpath_file;
+ gchar *parentPath;
+ gboolean treeScanned;
+ gboolean has_subdir = FALSE;
+ GtkTreeIter currentIter;
+ GtkTreeIter subNodeIter;
+ GdkPixbuf *pixbuf;
+
+ if (!directoryTreeModel) return;
+
+ gtk_tree_model_get(GTK_TREE_MODEL(directoryTreeModel), iter,
+ TREE_COLUMN_FULL_PATH, &parentPath,
+ TREE_COLUMN_SCANNED, &treeScanned, -1);
+
+ if (treeScanned)
+ return;
+
+ if ( (dir=opendir(parentPath)) )
+ {
+ while ( (dirent=readdir(dir)) )
+ {
+ path = g_strconcat(parentPath, dirent->d_name, NULL);
+ stat(path, &statbuf);
+
+ // We don't read the directories '.' and '..', but may read hidden directories like '.mydir'
+ if (S_ISDIR(statbuf.st_mode)
+ && ( (g_ascii_strcasecmp (dirent->d_name,"..") != 0)
+ && ((g_ascii_strncasecmp(dirent->d_name,".", 1) != 0) || (BROWSE_HIDDEN_DIR && strlen(dirent->d_name) > 1)) ) )
+ {
+
+ if (path[strlen(path)-1]!=G_DIR_SEPARATOR)
+ fullpath_file = g_strconcat(path,G_DIR_SEPARATOR_S,NULL);
+ else
+ fullpath_file = g_strdup(path);
+
+ dirname_utf8 = filename_to_display(dirent->d_name);
+ //if (!dirname_utf8)
+ //{
+ // gchar *escaped_temp = g_strescape(dirent->d_name, NULL);
+ // g_free(escaped_temp);
+ //}
+
+ if (check_for_subdir(fullpath_file))
+ has_subdir = TRUE;
+ else
+ has_subdir = FALSE;
+
+ /* Select pixmap for accessible/unaccessible directory */
+ if (Check_For_Access_Permission(path))
+ pixbuf = closed_folder_pixmap;
+ else
+ pixbuf = closed_folder_locked_pixmap;
+
+ gtk_tree_store_append(directoryTreeModel, &currentIter, iter);
+ gtk_tree_store_set(directoryTreeModel, &currentIter,
+ TREE_COLUMN_DIR_NAME, dirname_utf8,
+ TREE_COLUMN_FULL_PATH, fullpath_file,
+ TREE_COLUMN_HAS_SUBDIR, !has_subdir,
+ TREE_COLUMN_SCANNED, FALSE,
+ TREE_COLUMN_PIXBUF, pixbuf, -1);
+
+ if (has_subdir)
+ {
+ // Insert a dummy node
+ gtk_tree_store_append(directoryTreeModel, &subNodeIter, &currentIter);
+ }
+
+ g_free(fullpath_file);
+ g_free(dirname_utf8);
+ }
+ g_free(path);
+
+ }
+ closedir(dir);
+ }
+
+ // remove dummy node
+ gtk_tree_model_iter_children(GTK_TREE_MODEL(directoryTreeModel), &subNodeIter, iter);
+ gtk_tree_store_remove(directoryTreeModel, &subNodeIter);
+
+#ifdef WIN32
+ // set open folder pixmap except on drive (depth == 0)
+ if (gtk_tree_path_get_depth(gtreePath) > 1)
+ {
+ // update the icon of the node to opened folder :-)
+ gtk_tree_store_set(directoryTreeModel, iter,
+ TREE_COLUMN_SCANNED, TRUE,
+ TREE_COLUMN_PIXBUF, opened_folder_pixmap, -1);
+ }
+#else
+ // update the icon of the node to opened folder :-)
+ gtk_tree_store_set(directoryTreeModel, iter,
+ TREE_COLUMN_SCANNED, TRUE,
+ TREE_COLUMN_PIXBUF, opened_folder_pixmap, -1);
+#endif
+
+ gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(directoryTreeModel),
+ TREE_COLUMN_DIR_NAME, GTK_SORT_ASCENDING);
+
+ g_free(parentPath);
+}
+
+static void collapse_cb (GtkWidget *tree, GtkTreeIter *iter, GtkTreePath *treePath, gpointer data)
+{
+ GtkTreeIter subNodeIter;
+
+ if (!directoryTreeModel) return;
+
+ gtk_tree_model_iter_children(GTK_TREE_MODEL(directoryTreeModel),
+ &subNodeIter, iter);
+ while (gtk_tree_model_iter_has_child(GTK_TREE_MODEL(directoryTreeModel), iter))
+ {
+ gtk_tree_model_iter_children(GTK_TREE_MODEL(directoryTreeModel), &subNodeIter, iter);
+ gtk_tree_store_remove(directoryTreeModel, &subNodeIter);
+ }
+
+#ifdef WIN32
+ // set closed folder pixmap except on drive (depth == 0)
+ if(gtk_tree_path_get_depth(treePath) > 1)
+ {
+ // update the icon of the node to closed folder :-)
+ gtk_tree_store_set(directoryTreeModel, iter,
+ TREE_COLUMN_SCANNED, FALSE,
+ TREE_COLUMN_PIXBUF, closed_folder_pixmap, -1);
+ }
+#else
+ // update the icon of the node to closed folder :-)
+ gtk_tree_store_set(directoryTreeModel, iter,
+ TREE_COLUMN_SCANNED, FALSE,
+ TREE_COLUMN_PIXBUF, closed_folder_pixmap, -1);
+#endif
+
+ // insert dummy node
+ gtk_tree_store_append(directoryTreeModel, &subNodeIter, iter);
+}
+
+/*
+ * Create item of the browser (Entry + Tree + List).
+ */
+GtkWidget *Create_Browser_Items (GtkWidget *parent)
+{
+ GtkWidget *VerticalBox;
+ GtkWidget *HBox;
+ GtkWidget *ScrollWindowDirectoryTree;
+ GtkWidget *ScrollWindowFileList;
+ GtkWidget *ScrollWindowArtistList;
+ GtkWidget *ScrollWindowAlbumList;
+ GtkWidget *Label;
+ GtkWidget *Icon;
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+ GtkTooltips *Tips;
+ GtkWidget *PopupMenu;
+ gchar *ArtistList_Titles[] = {N_("Artist"),N_("# Albums"),N_("# Files")};
+ gchar *AlbumList_Titles[] = {N_("Album"),N_("# Files")};
+
+ Tips = gtk_tooltips_new();
+ VerticalBox = gtk_vbox_new(FALSE,2);
+ gtk_container_set_border_width(GTK_CONTAINER(VerticalBox),2);
+
+
+ // HBox for BrowserEntry + BrowserLabel
+ HBox = gtk_hbox_new(FALSE,0);
+ gtk_box_pack_start(GTK_BOX(VerticalBox),HBox,FALSE,TRUE,0);
+
+ /*
+ * The button to go to the parent directory
+ */
+ BrowserButton = gtk_button_new();
+ Icon = gtk_image_new_from_stock("easytag-parent-folder", GTK_ICON_SIZE_SMALL_TOOLBAR); // On Win32, GTK_ICON_SIZE_BUTTON enlarge the combobox...
+ gtk_container_add(GTK_CONTAINER(BrowserButton),Icon);
+ gtk_box_pack_start(GTK_BOX(HBox),BrowserButton,FALSE,FALSE,0);
+ gtk_button_set_relief(GTK_BUTTON(BrowserButton),GTK_RELIEF_NONE);
+ g_signal_connect(G_OBJECT(BrowserButton),"clicked",G_CALLBACK(Browser_Button_Clicked),NULL);
+ gtk_tooltips_set_tip(Tips,BrowserButton,_("Go to parent directory"),NULL);
+
+ /*
+ * The entry box for displaying path
+ */
+ if (BrowserEntryModel != NULL)
+ gtk_list_store_clear(BrowserEntryModel);
+ else
+ BrowserEntryModel = gtk_list_store_new(MISC_COMBO_COUNT, G_TYPE_STRING);
+
+ BrowserEntryCombo = gtk_combo_box_entry_new_with_model(GTK_TREE_MODEL(BrowserEntryModel), MISC_COMBO_TEXT);
+ /* History list */
+ Load_Path_Entry_List(BrowserEntryModel, MISC_COMBO_TEXT);
+ //gtk_combo_box_set_wrap_width(GTK_COMBO_BOX(BrowserEntryCombo),2); // Two columns to display paths
+
+ g_signal_connect(G_OBJECT(GTK_ENTRY(GTK_BIN(BrowserEntryCombo)->child)),"activate",G_CALLBACK(Browser_Entry_Activated),NULL);
+ gtk_box_pack_start(GTK_BOX(HBox),BrowserEntryCombo,TRUE,TRUE,1);
+ gtk_tooltips_set_tip(Tips,GTK_WIDGET(GTK_ENTRY(GTK_BIN(BrowserEntryCombo)->child)),_("Enter a directory to browse."),NULL);
+
+
+ /*
+ * The label for displaying number of files in path (without subdirs)
+ */
+ BrowserLabel = gtk_label_new(" ... ");
+ gtk_box_pack_start(GTK_BOX(HBox),BrowserLabel,FALSE,FALSE,2);
+
+
+ /* Create pixmaps */
+ if(!opened_folder_pixmap)
+ {
+ opened_folder_pixmap = gdk_pixbuf_new_from_xpm_data(opened_folder_xpm);
+ closed_folder_pixmap = gdk_pixbuf_new_from_xpm_data(closed_folder_xpm);
+ closed_folder_locked_pixmap = gdk_pixbuf_new_from_xpm_data(closed_folder_locked_xpm);
+
+#ifdef WIN32
+ /* get GTK's theme harddrive and removable icons and render it in a pixbuf */
+ harddrive_pixmap = gtk_icon_set_render_icon(gtk_style_lookup_icon_set(parent->style, GTK_STOCK_HARDDISK),
+ parent->style,
+ gtk_widget_get_direction (parent),
+ GTK_STATE_NORMAL,
+ GTK_ICON_SIZE_BUTTON,
+ parent, NULL);
+
+ removable_pixmap = gtk_icon_set_render_icon(gtk_style_lookup_icon_set(parent->style, GTK_STOCK_FLOPPY),
+ parent->style,
+ gtk_widget_get_direction (parent),
+ GTK_STATE_NORMAL,
+ GTK_ICON_SIZE_BUTTON,
+ parent, NULL);
+
+ cdrom_pixmap = gtk_icon_set_render_icon(gtk_style_lookup_icon_set(parent->style, GTK_STOCK_CDROM),
+ parent->style,
+ gtk_widget_get_direction (parent),
+ GTK_STATE_NORMAL,
+ GTK_ICON_SIZE_BUTTON,
+ parent, NULL);
+
+ network_pixmap = gtk_icon_set_render_icon(gtk_style_lookup_icon_set(parent->style, GTK_STOCK_NETWORK),
+ parent->style,
+ gtk_widget_get_direction (parent),
+ GTK_STATE_NORMAL,
+ GTK_ICON_SIZE_BUTTON,
+ parent, NULL);
+
+ ramdisk_pixmap = gdk_pixbuf_new_from_xpm_data(ram_disk_xpm);
+
+#endif
+ }
+
+ /* Browser NoteBook :
+ * - one tab for the BrowserTree
+ * - one tab for the BrowserArtistList and the BrowserAlbumList
+ */
+ BrowserNoteBook = gtk_notebook_new();
+ //gtk_notebook_popup_enable(GTK_NOTEBOOK(BrowserNoteBook));
+ gtk_notebook_set_show_tabs(GTK_NOTEBOOK(BrowserNoteBook),FALSE);
+ gtk_notebook_set_show_border(GTK_NOTEBOOK(BrowserNoteBook),FALSE);
+
+
+ /*
+ * The ScrollWindow and the Directory-Tree
+ */
+ ScrollWindowDirectoryTree = gtk_scrolled_window_new(NULL,NULL);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ScrollWindowDirectoryTree),
+ GTK_POLICY_AUTOMATIC,GTK_POLICY_AUTOMATIC);
+ directoryTreeModel = gtk_tree_store_new(TREE_COLUMN_COUNT,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ G_TYPE_BOOLEAN,
+ G_TYPE_BOOLEAN,
+ GDK_TYPE_PIXBUF);
+
+ Label = gtk_label_new(_("Tree"));
+ gtk_notebook_append_page(GTK_NOTEBOOK(BrowserNoteBook),ScrollWindowDirectoryTree,Label);
+
+ /* The tree view */
+ BrowserTree = gtk_tree_view_new_with_model(GTK_TREE_MODEL(directoryTreeModel));
+ gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(BrowserTree), FALSE);
+ renderer = gtk_cell_renderer_pixbuf_new();
+ column = gtk_tree_view_column_new();
+ gtk_tree_view_column_pack_start(column, renderer, FALSE);
+ gtk_tree_view_column_set_attributes(column, renderer,
+ "pixbuf", TREE_COLUMN_PIXBUF, NULL);
+ renderer = gtk_cell_renderer_text_new();
+ gtk_tree_view_column_pack_start(column, renderer, FALSE);
+ gtk_tree_view_column_set_attributes(column, renderer,
+ "text", TREE_COLUMN_DIR_NAME, NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(BrowserTree), column);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+ gtk_container_add(GTK_CONTAINER(ScrollWindowDirectoryTree),BrowserTree);
+
+ Browser_Tree_Initialize();
+
+
+ /* Signals */
+ g_signal_connect(G_OBJECT(BrowserTree), "row-expanded", G_CALLBACK(expand_cb),NULL);
+ g_signal_connect(G_OBJECT(BrowserTree), "row-collapsed", G_CALLBACK(collapse_cb),NULL);
+ g_signal_connect(G_OBJECT(gtk_tree_view_get_selection(GTK_TREE_VIEW(BrowserTree))),
+ "changed", G_CALLBACK(Browser_Tree_Node_Selected), NULL);
+
+ g_signal_connect(G_OBJECT(BrowserTree),"key_press_event", G_CALLBACK(Browser_Tree_Key_Press),NULL);
+
+ /* Create Popup Menu on browser tree view */
+ PopupMenu = gtk_ui_manager_get_widget(UIManager, "/DirPopup");
+ g_signal_connect_swapped(G_OBJECT(BrowserTree),"button_press_event",
+ G_CALLBACK(Browser_Popup_Menu_Handler), G_OBJECT(PopupMenu));
+
+
+
+ /*
+ * The ScrollWindows with the Artist and Album Lists
+ */
+
+ ArtistAlbumVPaned = gtk_vpaned_new();
+
+ Label = gtk_label_new(_("Artist & Album"));
+ gtk_notebook_append_page(GTK_NOTEBOOK(BrowserNoteBook),ArtistAlbumVPaned,Label);
+
+ ScrollWindowArtistList = gtk_scrolled_window_new(NULL,NULL);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ScrollWindowArtistList),
+ GTK_POLICY_AUTOMATIC,GTK_POLICY_AUTOMATIC);
+
+ artistListModel = gtk_list_store_new(ARTIST_COLUMN_COUNT,
+ G_TYPE_STRING,
+ G_TYPE_UINT,
+ G_TYPE_UINT,
+ G_TYPE_POINTER,
+ PANGO_TYPE_STYLE,
+ G_TYPE_INT,
+ GDK_TYPE_COLOR);
+
+ BrowserArtistList = gtk_tree_view_new_with_model(GTK_TREE_MODEL(artistListModel));
+ gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(BrowserArtistList), TRUE);
+ renderer = gtk_cell_renderer_text_new();
+
+ column = gtk_tree_view_column_new_with_attributes(_(ArtistList_Titles[0]), renderer,
+ "text", ARTIST_NAME,
+ "weight", ARTIST_FONT_WEIGHT,
+ "style", ARTIST_FONT_STYLE,
+ "foreground-gdk", ARTIST_ROW_FOREGROUND,
+ NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(BrowserArtistList), column);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+
+ column = gtk_tree_view_column_new_with_attributes(_(ArtistList_Titles[1]), renderer,
+ "text", ARTIST_NUM_ALBUMS,
+ "weight", ARTIST_FONT_WEIGHT,
+ "style", ARTIST_FONT_STYLE,
+ "foreground-gdk", ARTIST_ROW_FOREGROUND,
+ NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(BrowserArtistList), column);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+
+ column = gtk_tree_view_column_new_with_attributes(_(ArtistList_Titles[2]), renderer,
+ "text", ARTIST_NUM_FILES,
+ "weight", ARTIST_FONT_WEIGHT,
+ "style", ARTIST_FONT_STYLE,
+ "foreground-gdk", ARTIST_ROW_FOREGROUND,
+ NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(BrowserArtistList), column);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+
+ gtk_tree_view_set_reorderable(GTK_TREE_VIEW(BrowserArtistList), FALSE);
+ gtk_tree_selection_set_mode(gtk_tree_view_get_selection(GTK_TREE_VIEW(BrowserArtistList)),GTK_SELECTION_SINGLE);
+ g_signal_connect(G_OBJECT(gtk_tree_view_get_selection(GTK_TREE_VIEW(BrowserArtistList))),"changed",G_CALLBACK(Browser_Artist_List_Row_Selected),NULL);
+
+ gtk_container_add(GTK_CONTAINER(ScrollWindowArtistList),BrowserArtistList);
+
+ // Create Popup Menu on browser artist list
+ PopupMenu = gtk_ui_manager_get_widget(UIManager, "/DirArtistPopup");
+ g_signal_connect_swapped(G_OBJECT(BrowserArtistList),"button_press_event",
+ G_CALLBACK(Browser_Popup_Menu_Handler), G_OBJECT(PopupMenu));
+ // Not available yet!
+ //ui_widget_set_sensitive(MENU_FILE, AM_ARTIST_OPEN_FILE_WITH, FALSE);
+
+ ScrollWindowAlbumList = gtk_scrolled_window_new(NULL,NULL);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ScrollWindowAlbumList),
+ GTK_POLICY_AUTOMATIC,GTK_POLICY_AUTOMATIC);
+
+ albumListModel = gtk_list_store_new(ALBUM_COLUMN_COUNT,
+ G_TYPE_STRING,
+ G_TYPE_UINT,
+ G_TYPE_POINTER,
+ PANGO_TYPE_STYLE,
+ G_TYPE_INT,
+ GDK_TYPE_COLOR);
+
+ BrowserAlbumList = gtk_tree_view_new_with_model(GTK_TREE_MODEL(albumListModel));
+ gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(BrowserAlbumList), TRUE);
+ renderer = gtk_cell_renderer_text_new();
+
+ column = gtk_tree_view_column_new_with_attributes(_(AlbumList_Titles[0]), renderer,
+ "text", ALBUM_NAME,
+ "weight", ALBUM_FONT_WEIGHT,
+ "style", ALBUM_FONT_STYLE,
+ "foreground-gdk", ALBUM_ROW_FOREGROUND,
+ NULL);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(BrowserAlbumList), column);
+
+ column = gtk_tree_view_column_new_with_attributes(_(AlbumList_Titles[1]), renderer,
+ "text", ALBUM_NUM_FILES,
+ "weight", ALBUM_FONT_WEIGHT,
+ "style", ALBUM_FONT_STYLE,
+ "foreground-gdk", ALBUM_ROW_FOREGROUND,
+ NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(BrowserAlbumList), column);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+
+ gtk_tree_view_set_reorderable(GTK_TREE_VIEW(BrowserAlbumList), FALSE);
+ gtk_tree_selection_set_mode(gtk_tree_view_get_selection(GTK_TREE_VIEW(BrowserAlbumList)), GTK_SELECTION_SINGLE);
+
+ g_signal_connect(G_OBJECT(gtk_tree_view_get_selection(GTK_TREE_VIEW(BrowserAlbumList))),"changed",G_CALLBACK(Browser_Album_List_Row_Selected),NULL);
+ gtk_container_add(GTK_CONTAINER(ScrollWindowAlbumList),BrowserAlbumList);
+
+ // Create Popup Menu on browser album list
+ PopupMenu = gtk_ui_manager_get_widget(UIManager, "/DirAlbumPopup");
+ g_signal_connect_swapped(G_OBJECT(BrowserArtistList),"button_press_event",
+ G_CALLBACK(Browser_Popup_Menu_Handler), G_OBJECT(PopupMenu));
+ // Not available yet!
+ //ui_widget_set_sensitive(MENU_FILE, AM_ALBUM_OPEN_FILE_WITH, FALSE);
+
+
+ gtk_paned_pack1(GTK_PANED(ArtistAlbumVPaned),ScrollWindowArtistList,TRUE,TRUE); // Top side
+ gtk_paned_pack2(GTK_PANED(ArtistAlbumVPaned),ScrollWindowAlbumList,TRUE,TRUE); // Bottom side
+ gtk_paned_set_position(GTK_PANED(ArtistAlbumVPaned),PANE_HANDLE_POSITION3);
+
+
+ /*
+ * The ScrollWindow and the List
+ */
+ ScrollWindowFileList = gtk_scrolled_window_new(NULL,NULL);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ScrollWindowFileList),
+ GTK_POLICY_AUTOMATIC,GTK_POLICY_AUTOMATIC);
+
+ /* The file list */
+ fileListModel = gtk_list_store_new(LIST_COLUMN_COUNT,
+ G_TYPE_STRING,
+ G_TYPE_POINTER,
+ G_TYPE_INT,
+ G_TYPE_BOOLEAN,
+ G_TYPE_INT,
+ GDK_TYPE_COLOR,
+ GDK_TYPE_COLOR);
+
+ BrowserList = gtk_tree_view_new_with_model(GTK_TREE_MODEL(fileListModel));
+ gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(BrowserList), FALSE);
+ gtk_container_add(GTK_CONTAINER(ScrollWindowFileList), BrowserList);
+ gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(BrowserList), FALSE);
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new();
+ gtk_tree_view_column_pack_start(column, renderer, FALSE);
+ gtk_tree_view_column_set_attributes(column, renderer,
+ "text", LIST_FILE_NAME,
+ "weight", LIST_FONT_WEIGHT,
+ "background-gdk", LIST_ROW_BACKGROUND,
+ "foreground-gdk", LIST_ROW_FOREGROUND,
+ NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(BrowserList), column);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+ gtk_tree_view_set_reorderable(GTK_TREE_VIEW(BrowserList), FALSE);
+ gtk_tree_selection_set_mode(gtk_tree_view_get_selection(GTK_TREE_VIEW(BrowserList)),GTK_SELECTION_MULTIPLE);
+ // When selecting a line
+ gtk_tree_selection_set_select_function(gtk_tree_view_get_selection(GTK_TREE_VIEW(BrowserList)), Browser_List_Select_Func, NULL, NULL);
+ // To sort list
+ //gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(fileListModel), 0, Browser_List_Sort_Func, NULL, NULL);
+ Browser_List_Refresh_Sort();
+ gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(fileListModel), 0, GTK_SORT_ASCENDING);
+
+ g_signal_connect(G_OBJECT(gtk_tree_view_get_selection(GTK_TREE_VIEW(BrowserList))),
+ "changed", G_CALLBACK(Browser_List_Row_Selected), NULL);
+ g_signal_connect(G_OBJECT(BrowserList),"key_press_event", G_CALLBACK(Browser_List_Key_Press),NULL);
+ g_signal_connect(G_OBJECT(BrowserList),"button_press_event", G_CALLBACK(Browser_List_Button_Press),NULL);
+
+
+ /*
+ * Create Popup Menu on file list
+ */
+ PopupMenu = gtk_ui_manager_get_widget(UIManager, "/FilePopup");
+ g_signal_connect_swapped(G_OBJECT(BrowserList),"button_press_event",
+ G_CALLBACK(Browser_Popup_Menu_Handler), G_OBJECT(PopupMenu));
+
+ /*
+ * The list store for run program combos
+ */
+ RunProgramModel = gtk_list_store_new(MISC_COMBO_COUNT, G_TYPE_STRING);
+
+ /*
+ * The pane for the tree and list
+ */
+ BrowserHPaned = gtk_hpaned_new();
+ gtk_box_pack_start(GTK_BOX(VerticalBox),BrowserHPaned,TRUE,TRUE,0);
+ gtk_paned_pack1(GTK_PANED(BrowserHPaned),BrowserNoteBook,TRUE,TRUE); // Left side
+ gtk_paned_pack2(GTK_PANED(BrowserHPaned),ScrollWindowFileList,TRUE,TRUE); // Right side
+ gtk_paned_set_position(GTK_PANED(BrowserHPaned),PANE_HANDLE_POSITION2);
+
+ gtk_widget_show_all(VerticalBox);
+
+ /* Set home variable as current path */
+ Browser_Update_Current_Path(HOME_VARIABLE);
+
+ return VerticalBox;
+}
+
+
+
+/*
+ * The window to Rename a directory into the browser.
+ */
+void Browser_Open_Rename_Directory_Window (void)
+{
+ GtkWidget *Frame;
+ GtkWidget *VBox;
+ GtkWidget *HBox;
+ GtkWidget *Label;
+ GtkWidget *ButtonBox;
+ GtkWidget *Button;
+ GtkWidget *Separator;
+ GtkTooltips *Tips;
+ gchar *directory_parent = NULL;
+ gchar *directory_name = NULL;
+ gchar *directory_name_utf8 = NULL;
+ gchar *address = NULL;
+ gchar *string;
+
+ if (RenameDirectoryWindow != NULL)
+ {
+ gdk_window_raise(RenameDirectoryWindow->window);
+ return;
+ }
+
+ /* We get the full path but we musn't display the parent directories */
+ directory_parent = g_strdup(BrowserCurrentPath);
+ if (!directory_parent || strlen(directory_parent) == 0)
+ {
+ g_free(directory_parent);
+ return;
+ }
+
+ // Remove the last '/' in the path if it exists
+ if (strlen(directory_parent)>1 && directory_parent[strlen(directory_parent)-1]==G_DIR_SEPARATOR)
+ directory_parent[strlen(directory_parent)-1]=0;
+ // Get name of the directory to rename (without path)
+ address = strrchr(directory_parent,G_DIR_SEPARATOR);
+ if (!address) return;
+ directory_name = g_strdup(address+1);
+ *(address+1) = 0;
+
+ if (!directory_name || strlen(directory_name)==0)
+ {
+ g_free(directory_name);
+ g_free(directory_parent);
+ return;
+ }
+
+ /* The tooltips */
+ Tips = gtk_tooltips_new();
+
+ directory_name_utf8 = filename_to_display(directory_name);
+
+ RenameDirectoryWindow = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title(GTK_WINDOW(RenameDirectoryWindow),_("Rename the directory"));
+ gtk_window_set_transient_for(GTK_WINDOW(RenameDirectoryWindow),GTK_WINDOW(MainWindow));
+ gtk_window_set_position(GTK_WINDOW(RenameDirectoryWindow),GTK_WIN_POS_CENTER_ON_PARENT);
+
+ /* We attach usefull data to the combobox */
+ g_object_set_data(G_OBJECT(RenameDirectoryWindow), "Parent_Directory", directory_parent);
+ g_object_set_data(G_OBJECT(RenameDirectoryWindow), "Current_Directory", directory_name);
+
+ Frame = gtk_frame_new(NULL);
+ gtk_container_add(GTK_CONTAINER(RenameDirectoryWindow),Frame);
+ gtk_container_set_border_width(GTK_CONTAINER(Frame),2);
+
+ VBox = gtk_vbox_new(FALSE,4);
+ gtk_container_add(GTK_CONTAINER(Frame),VBox);
+ gtk_container_set_border_width(GTK_CONTAINER(VBox), 4);
+
+ string = g_strdup_printf(_("Rename the directory '%s' to : "),directory_name_utf8);
+ Label = gtk_label_new(_(string));
+ g_free(string);
+ gtk_box_pack_start(GTK_BOX(VBox),Label,FALSE,TRUE,0);
+ gtk_label_set_line_wrap(GTK_LABEL(Label),TRUE);
+
+ /* The combobox to rename the directory */
+ RenameDirectoryCombo = gtk_combo_box_entry_new_text();
+ gtk_box_pack_start(GTK_BOX(VBox),RenameDirectoryCombo,FALSE,FALSE,0);
+ /* Set the directory into the combobox */
+ gtk_combo_box_prepend_text(GTK_COMBO_BOX(RenameDirectoryCombo), directory_name_utf8);
+ gtk_combo_box_prepend_text(GTK_COMBO_BOX(RenameDirectoryCombo), "");
+ gtk_entry_set_text(GTK_ENTRY(GTK_BIN(RenameDirectoryCombo)->child),directory_name_utf8);
+ Attach_Popup_Menu_To_Tag_Entries(GTK_ENTRY(GTK_BIN(RenameDirectoryCombo)->child));
+
+ /* Rename directory : check box + combo box + Status icon */
+ HBox = gtk_hbox_new(FALSE,2);
+ gtk_box_pack_start(GTK_BOX(VBox),HBox,TRUE,TRUE,0);
+
+ RenameDirectoryWithMask = gtk_check_button_new_with_label(_("Use mask :"));
+ gtk_box_pack_start(GTK_BOX(HBox),RenameDirectoryWithMask,FALSE,FALSE,0);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(RenameDirectoryWithMask),RENAME_DIRECTORY_WITH_MASK);
+ gtk_tooltips_set_tip(Tips,RenameDirectoryWithMask,_("If activated, it will use "
+ "masks to rename directory."),NULL);
+ g_signal_connect(G_OBJECT(RenameDirectoryWithMask),"toggled",G_CALLBACK(Rename_Directory_With_Mask_Toggled),NULL);
+
+ // Set up list model which is used by the combobox
+ /* Rename directory from mask */
+ if (!RenameDirectoryMaskModel)
+ RenameDirectoryMaskModel = gtk_list_store_new(MASK_EDITOR_COUNT, G_TYPE_STRING);
+ else
+ gtk_list_store_clear(RenameDirectoryMaskModel);
+
+ // The combo box to select the mask to apply
+ RenameDirectoryMaskCombo = gtk_combo_box_entry_new();
+ gtk_combo_box_set_model(GTK_COMBO_BOX(RenameDirectoryMaskCombo), GTK_TREE_MODEL(RenameDirectoryMaskModel));
+ gtk_combo_box_entry_set_text_column(GTK_COMBO_BOX_ENTRY(RenameDirectoryMaskCombo), MASK_EDITOR_TEXT);
+ gtk_widget_set_size_request(RenameDirectoryMaskCombo, 80, -1);
+
+ gtk_box_pack_start(GTK_BOX(HBox),RenameDirectoryMaskCombo,TRUE,TRUE,0);
+ gtk_tooltips_set_tip(Tips,GTK_WIDGET(GTK_ENTRY(GTK_BIN(RenameDirectoryMaskCombo)->child)),
+ _("Select or type in a mask using codes (see Legend in Scanner Window) to rename "
+ "the directory from tag fields."),NULL);
+ // Signal to generate preview (preview of the new directory)
+ g_signal_connect_swapped(G_OBJECT(GTK_ENTRY(GTK_BIN(RenameDirectoryMaskCombo)->child)),"changed",
+ G_CALLBACK(Scan_Rename_Directory_Generate_Preview),NULL);
+
+ // Load masks into the combobox from a file
+ Load_Rename_Directory_Masks_List(RenameDirectoryMaskModel, MASK_EDITOR_TEXT, Rename_Directory_Masks);
+ if (RENAME_DIRECTORY_DEFAULT_MASK)
+ {
+ Add_String_To_Combo_List(RenameDirectoryMaskModel, RENAME_DIRECTORY_DEFAULT_MASK);
+ gtk_entry_set_text(GTK_ENTRY(GTK_BIN(RenameDirectoryMaskCombo)->child), RENAME_DIRECTORY_DEFAULT_MASK);
+ }else
+ {
+ gtk_combo_box_set_active(GTK_COMBO_BOX(RenameDirectoryMaskCombo), 0);
+ }
+
+ // Mask status icon
+ RenameDirectoryMaskStatusIconBox = Create_Pixmap_Icon_With_Event_Box("easytag-forbidden");
+ gtk_box_pack_start(GTK_BOX(HBox),RenameDirectoryMaskStatusIconBox,FALSE,FALSE,0);
+ gtk_tooltips_set_tip(Tips,RenameDirectoryMaskStatusIconBox,_("Invalid Scanner Mask"),NULL);
+ // Signal connection to check if mask is correct into the mask entry
+ g_signal_connect_swapped(G_OBJECT(GTK_ENTRY(GTK_BIN(RenameDirectoryMaskCombo)->child)),"changed",
+ G_CALLBACK(Scan_Check_Rename_File_Mask),G_OBJECT(RenameDirectoryMaskStatusIconBox));
+
+ // Preview label
+ RenameDirectoryPreviewLabel = gtk_label_new(_("Rename directory preview..."));
+ gtk_label_set_line_wrap(GTK_LABEL(RenameDirectoryPreviewLabel),TRUE);
+ ////gtk_widget_show(FillTagPreviewLabel);
+ gtk_box_pack_start(GTK_BOX(VBox),RenameDirectoryPreviewLabel,TRUE,TRUE,0);
+
+ /* Separator line */
+ Separator = gtk_hseparator_new();
+ gtk_box_pack_start(GTK_BOX(VBox),Separator,FALSE,FALSE,0);
+
+ ButtonBox = gtk_hbutton_box_new ();
+ gtk_box_pack_start(GTK_BOX(VBox),ButtonBox,FALSE,FALSE,0);
+ gtk_button_box_set_layout(GTK_BUTTON_BOX(ButtonBox),GTK_BUTTONBOX_END);
+ gtk_box_set_spacing(GTK_BOX(ButtonBox),10);
+
+ /* Button to cancel */
+ Button = Create_Button_With_Pixmap(BUTTON_CANCEL);
+ gtk_container_add(GTK_CONTAINER(ButtonBox),Button);
+ GTK_WIDGET_SET_FLAGS(Button,GTK_CAN_DEFAULT);
+ //gtk_widget_grab_default(Button);
+ g_signal_connect_swapped(G_OBJECT(Button),"clicked",G_CALLBACK(Destroy_Rename_Directory_Window), G_OBJECT(RenameDirectoryCombo));
+
+ /* Button to save: to rename directory */
+ Button = Create_Button_With_Pixmap(BUTTON_APPLY);
+ gtk_container_add(GTK_CONTAINER(ButtonBox),Button);
+ GTK_WIDGET_SET_FLAGS(Button,GTK_CAN_DEFAULT);
+ g_signal_connect_swapped(G_OBJECT(Button),"clicked", G_CALLBACK(Rename_Directory),NULL);
+ g_signal_connect_swapped(G_OBJECT(GTK_ENTRY(GTK_BIN(RenameDirectoryCombo)->child)),"changed",
+ G_CALLBACK(Entry_Changed_Disable_Object),G_OBJECT(Button));
+
+ g_signal_connect_swapped(G_OBJECT(RenameDirectoryWindow),"destroy", G_CALLBACK(Destroy_Rename_Directory_Window), NULL);
+ g_signal_connect_swapped(G_OBJECT(RenameDirectoryWindow),"delete_event", G_CALLBACK(Destroy_Rename_Directory_Window), NULL);
+ g_signal_connect(G_OBJECT(RenameDirectoryWindow),"key_press_event", G_CALLBACK(Rename_Directory_Window_Key_Press),NULL);
+ gtk_widget_show(RenameDirectoryWindow);
+
+ // Just center it over the main window
+ gtk_window_set_position(GTK_WINDOW(RenameDirectoryWindow), GTK_WIN_POS_CENTER_ON_PARENT);
+ gtk_window_set_policy(GTK_WINDOW(RenameDirectoryWindow),FALSE,FALSE,TRUE);
+ gtk_widget_set_size_request(GTK_WIDGET(RenameDirectoryWindow), 350, -1);
+
+ // To avoid/minimize 'flicker'
+ gtk_widget_show_all(RenameDirectoryWindow);
+
+ // To initialize the 'Use mask' check button state
+ g_signal_emit_by_name(G_OBJECT(RenameDirectoryWithMask),"toggled");
+
+ // To initialize PreviewLabel + MaskStatusIconBox
+ g_signal_emit_by_name(G_OBJECT(GTK_BIN(RenameDirectoryMaskCombo)->child),"changed");
+
+ g_free(directory_name_utf8);
+}
+
+void Destroy_Rename_Directory_Window (void)
+{
+ if (RenameDirectoryWindow)
+ {
+ g_free(g_object_get_data(G_OBJECT(RenameDirectoryWindow),"Parent_Directory"));
+ g_free(g_object_get_data(G_OBJECT(RenameDirectoryWindow),"Current_Directory"));
+
+ // Prevent recursion (double-freeing)
+ // We can't unblock after the destroy is complete, it must be done automatically
+ g_signal_handlers_block_by_func(RenameDirectoryWindow, Destroy_Rename_Directory_Window, NULL);
+
+ if (RENAME_DIRECTORY_DEFAULT_MASK) g_free(RENAME_DIRECTORY_DEFAULT_MASK);
+ RENAME_DIRECTORY_DEFAULT_MASK = g_strdup(gtk_entry_get_text(GTK_ENTRY(GTK_BIN(RenameDirectoryMaskCombo)->child)));
+ Add_String_To_Combo_List(RenameDirectoryMaskModel, RENAME_DIRECTORY_DEFAULT_MASK);
+ Save_Rename_Directory_Masks_List(RenameDirectoryMaskModel, MASK_EDITOR_TEXT);
+
+ RENAME_DIRECTORY_WITH_MASK = GTK_TOGGLE_BUTTON(RenameDirectoryWithMask)->active;
+
+ gtk_list_store_clear(RenameDirectoryMaskModel);
+
+ gtk_widget_destroy(RenameDirectoryWindow);
+ RenameDirectoryWindow = (GtkWidget *)NULL;
+ }
+}
+
+void Rename_Directory (void)
+{
+ DIR *dir;
+ gchar *directory_parent;
+ gchar *directory_last_name;
+ gchar *directory_new_name;
+ gchar *directory_new_name_file;
+ gchar *last_path;
+ gchar *last_path_utf8;
+ gchar *new_path;
+ gchar *new_path_utf8;
+ gchar *tmp_path;
+ gchar *tmp_path_utf8;
+ gint fd_tmp;
+
+
+ if (!RenameDirectoryWindow)
+ return;
+
+ directory_parent = g_object_get_data(G_OBJECT(RenameDirectoryWindow),"Parent_Directory");
+ directory_last_name = g_object_get_data(G_OBJECT(RenameDirectoryWindow),"Current_Directory");
+
+ if (GTK_TOGGLE_BUTTON(RenameDirectoryWithMask)->active)
+ {
+ // Renamed from mask
+ gchar *mask = g_strdup(gtk_entry_get_text(GTK_ENTRY(GTK_BIN(RenameDirectoryMaskCombo)->child)));
+ directory_new_name = Scan_Generate_New_Directory_Name_From_Mask(ETCore->ETFileDisplayed,mask,FALSE);
+ g_free(mask);
+
+ }else
+ {
+ // Renamed 'manually'
+ directory_new_name = g_strdup(gtk_entry_get_text(GTK_ENTRY(GTK_BIN(RenameDirectoryCombo)->child)));
+ }
+
+ /* Check if a name for the directory have been supplied */
+ if (!directory_new_name || g_utf8_strlen(directory_new_name, -1) < 1)
+ {
+ GtkWidget *msgbox;
+
+ msgbox = msg_box_new(_("Error..."),_("You must type a directory name!"),
+ GTK_STOCK_DIALOG_ERROR,BUTTON_OK,0);
+ msg_box_hide_check_button(MSG_BOX(msgbox));
+ msg_box_run(MSG_BOX(msgbox));
+ gtk_widget_destroy(msgbox);
+ g_free(directory_new_name);
+ return;
+ }
+
+ /* Check that we can write the new directory name */
+ directory_new_name_file = filename_from_display(directory_new_name);
+ if (!directory_new_name_file)
+ {
+ GtkWidget *msgbox;
+
+ msgbox = msg_box_new(_("Error..."),_("Could not convert '%s' into filename encoding. Please use another name."),GTK_STOCK_DIALOG_ERROR,BUTTON_OK,0);
+ msg_box_hide_check_button(MSG_BOX(msgbox));
+ msg_box_run(MSG_BOX(msgbox));
+ gtk_widget_destroy(msgbox);
+ g_free(directory_new_name);
+ g_free(directory_new_name_file);
+ }
+
+ /* If the directory name haven't been changed, we do nothing! */
+ if (directory_last_name && directory_new_name_file
+ && strcmp(directory_last_name,directory_new_name_file)==0)
+ {
+ Destroy_Rename_Directory_Window();
+ g_free(directory_new_name);
+ g_free(directory_new_name_file);
+ return;
+ }
+
+ /* Build the current and new absolute paths */
+ last_path = g_strconcat(directory_parent, directory_last_name, NULL);
+ last_path_utf8 = filename_to_display(last_path);
+ new_path = g_strconcat(directory_parent, directory_new_name_file, NULL);
+ new_path_utf8 = filename_to_display(new_path);
+
+ /* Check if the new directory name doesn't already exists, and detect if
+ * it's only a case change (needed for vfat) */
+ if ( (dir=opendir(new_path))!=NULL )
+ {
+ gchar *msg;
+ GtkWidget *msgbox;
+ //gint button;
+
+ closedir(dir);
+ if (strcasecmp(last_path,new_path) != 0)
+ {
+ // TODO
+ // // The same directory already exists. So we ask if we want to move the files
+ // msg = g_strdup_printf(_("The directory already exists!\n(%s)\nDo you want "
+ // "to move the files?"),new_path_utf8);
+ // msgbox = msg_box_new(_("Confirm..."),msg,GTK_STOCK_DIALOG_QUESTION,
+ // BUTTON_NO,BUTTON_YES,0);
+ // g_free(msg);
+ // msg_box_hide_check_button(MSG_BOX(msgbox));
+ // button = msg_box_run(MSG_BOX(msgbox));
+ // gtk_widget_destroy(msgbox);
+ //
+ // switch (button)
+ // {
+ // case BUTTON_YES:
+ // // Here we must rename all files with the new location, and remove the directory
+ //
+ // Rename_File ()
+ //
+ // break;
+ // case BUTTON_NO:
+ // break;
+ // }
+
+ msg = g_strdup_printf(_("Can't rename because this directory name "
+ "already exists!\n(%s)"),new_path_utf8);
+ msgbox = msg_box_new(_("Error..."),msg,GTK_STOCK_DIALOG_ERROR,BUTTON_OK,0);
+ g_free(msg);
+ msg_box_hide_check_button(MSG_BOX(msgbox));
+ msg_box_run(MSG_BOX(msgbox));
+ gtk_widget_destroy(msgbox);
+
+ g_free(directory_new_name);
+ g_free(directory_new_name_file);
+ g_free(last_path);
+ g_free(last_path_utf8);
+ g_free(new_path);
+ g_free(new_path_utf8);
+
+ return;
+ }
+ }
+
+ /* Temporary path (useful when changing only string case) */
+ tmp_path = g_strdup_printf("%s.XXXXXX",last_path);
+ tmp_path_utf8 = filename_to_display(tmp_path);
+
+ if ( (fd_tmp = mkstemp(tmp_path)) >= 0 )
+ {
+ close(fd_tmp);
+ unlink(tmp_path);
+ }
+
+ /* Rename the directory from 'last name' to 'tmp name' */
+ if ( rename(last_path,tmp_path)!=0 )
+ {
+ gchar *msg;
+ GtkWidget *msgbox;
+
+ msg = g_strdup_printf(_("Can't rename directory \n'%s'\n to \n'%s'!\n(%s)"),
+ last_path_utf8,tmp_path_utf8,g_strerror(errno));
+ msgbox = msg_box_new(_("Error..."),msg,GTK_STOCK_DIALOG_ERROR,BUTTON_OK,0);
+ g_free(msg);
+ msg_box_hide_check_button(MSG_BOX(msgbox));
+ msg_box_run(MSG_BOX(msgbox));
+ gtk_widget_destroy(msgbox);
+
+ g_free(directory_new_name);
+ g_free(directory_new_name_file);
+ g_free(last_path);
+ g_free(last_path_utf8);
+ g_free(new_path);
+ g_free(new_path_utf8);
+ g_free(tmp_path);
+ g_free(tmp_path_utf8);
+
+ return;
+ }
+
+ /* Rename the directory from 'tmp name' to 'new name' (final name) */
+ if ( rename(tmp_path,new_path)!=0 )
+ {
+ gchar *msg;
+ GtkWidget *msgbox;
+
+ msg = g_strdup_printf(_("Can't rename directory \n'%s'\n to \n'%s'!\n(%s)"),
+ tmp_path_utf8,new_path_utf8,g_strerror(errno));
+ msgbox = msg_box_new(_("Error..."),msg,GTK_STOCK_DIALOG_ERROR,BUTTON_OK,0);
+ g_free(msg);
+ msg_box_hide_check_button(MSG_BOX(msgbox));
+ msg_box_run(MSG_BOX(msgbox));
+ gtk_widget_destroy(msgbox);
+
+ g_free(directory_new_name);
+ g_free(directory_new_name_file);
+ g_free(last_path);
+ g_free(last_path_utf8);
+ g_free(new_path);
+ g_free(new_path_utf8);
+ g_free(tmp_path);
+ g_free(tmp_path_utf8);
+
+ return;
+ }
+
+ ET_Update_Directory_Name_Into_File_List(last_path,new_path);
+ Browser_Tree_Rename_Directory(last_path,new_path);
+
+ // To update file path in the browser entry
+ if (ETCore->ETFileDisplayedList)
+ {
+ ET_Display_File_Data_To_UI(ETCore->ETFileDisplayed);
+ }else
+ {
+ gchar *tmp = filename_to_display(Browser_Get_Current_Path());
+ Browser_Entry_Set_Text(tmp);
+ g_free(tmp);
+ }
+
+ Destroy_Rename_Directory_Window();
+ g_free(last_path);
+ g_free(last_path_utf8);
+ g_free(new_path);
+ g_free(new_path_utf8);
+ g_free(tmp_path);
+ g_free(tmp_path_utf8);
+ g_free(directory_new_name);
+ g_free(directory_new_name_file);
+ Statusbar_Message(_("Directory renamed"),TRUE);
+}
+
+gboolean Rename_Directory_Window_Key_Press (GtkWidget *window, GdkEvent *event)
+{
+ GdkEventKey *kevent;
+
+ if (event && event->type == GDK_KEY_PRESS)
+ {
+ kevent = (GdkEventKey *)event;
+ switch(kevent->keyval)
+ {
+ case GDK_Escape:
+ // Destroy_Rename_Directory_Window();
+ g_signal_emit_by_name(window, "destroy");
+ break;
+ }
+ }
+ return FALSE;
+}
+
+void Rename_Directory_With_Mask_Toggled (void)
+{
+ gtk_widget_set_sensitive(GTK_WIDGET(RenameDirectoryCombo), !GTK_TOGGLE_BUTTON(RenameDirectoryWithMask)->active);
+ gtk_widget_set_sensitive(GTK_WIDGET(RenameDirectoryMaskCombo), GTK_TOGGLE_BUTTON(RenameDirectoryWithMask)->active);
+ gtk_widget_set_sensitive(GTK_WIDGET(RenameDirectoryMaskStatusIconBox), GTK_TOGGLE_BUTTON(RenameDirectoryWithMask)->active);
+ gtk_widget_set_sensitive(GTK_WIDGET(RenameDirectoryPreviewLabel), GTK_TOGGLE_BUTTON(RenameDirectoryWithMask)->active);
+}
+
+
+/*
+ * Window where is typed the name of the program to run, which
+ * receives the current directory as parameter.
+ */
+void Browser_Open_Run_Program_Tree_Window (void)
+{
+ GtkWidget *Frame;
+ GtkWidget *VBox;
+ GtkWidget *HBox;
+ GtkWidget *Label;
+ GtkWidget *RunProgramComboBox;
+ GtkWidget *ButtonBox;
+ GtkWidget *Button;
+ GtkWidget *Separator;
+ GtkTooltips *Tips;
+ gchar *current_directory = NULL;
+
+ if (RunProgramTreeWindow != NULL)
+ {
+ gdk_window_raise(RunProgramTreeWindow->window);
+ return;
+ }
+
+ // Current directory
+ current_directory = g_strdup(BrowserCurrentPath);
+ if (!current_directory || strlen(current_directory)==0)
+ return;
+
+ RunProgramTreeWindow = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title(GTK_WINDOW(RunProgramTreeWindow),_("Browse Directory with ..."));
+ gtk_window_set_transient_for(GTK_WINDOW(RunProgramTreeWindow),GTK_WINDOW(MainWindow));
+ g_signal_connect(G_OBJECT(RunProgramTreeWindow),"destroy", G_CALLBACK(Destroy_Run_Program_Tree_Window),NULL);
+ g_signal_connect(G_OBJECT(RunProgramTreeWindow),"delete_event", G_CALLBACK(Destroy_Run_Program_Tree_Window),NULL);
+ g_signal_connect(G_OBJECT(RunProgramTreeWindow),"key_press_event", G_CALLBACK(Run_Program_Tree_Window_Key_Press),NULL);
+
+ // Just center it over mainwindow
+ gtk_window_set_position(GTK_WINDOW(RunProgramTreeWindow), GTK_WIN_POS_CENTER_ON_PARENT);
+
+ Tips = gtk_tooltips_new();
+
+ Frame = gtk_frame_new(NULL);
+ gtk_container_add(GTK_CONTAINER(RunProgramTreeWindow),Frame);
+ gtk_container_set_border_width(GTK_CONTAINER(Frame),2);
+
+ VBox = gtk_vbox_new(FALSE,4);
+ gtk_container_add(GTK_CONTAINER(Frame),VBox);
+ gtk_container_set_border_width(GTK_CONTAINER(VBox), 4);
+
+ Label = gtk_label_new(_("Program to run :"));
+ gtk_box_pack_start(GTK_BOX(VBox),Label,TRUE,FALSE,0);
+ gtk_label_set_line_wrap(GTK_LABEL(Label),TRUE);
+
+ HBox = gtk_hbox_new(FALSE,4);
+ gtk_box_pack_start(GTK_BOX(VBox),HBox,FALSE,FALSE,2);
+ gtk_container_set_border_width(GTK_CONTAINER(HBox), 2);
+
+ /* The combobox to enter the program to run */
+ RunProgramComboBox = gtk_combo_box_entry_new_with_model(GTK_TREE_MODEL(RunProgramModel), MISC_COMBO_TEXT);
+ gtk_box_pack_start(GTK_BOX(HBox),RunProgramComboBox,TRUE,TRUE,0);
+ gtk_widget_set_size_request(GTK_WIDGET(RunProgramComboBox),250,-1);
+ gtk_tooltips_set_tip(Tips,GTK_WIDGET(GTK_ENTRY(GTK_BIN(RunProgramComboBox)->child)),_("Enter the program to run. "
+ "It will receive the current directory as parameter."),NULL);
+
+ /* History list */
+ gtk_list_store_clear(RunProgramModel);
+ Load_Run_Program_With_Directory_List(RunProgramModel, MISC_COMBO_TEXT);
+ g_signal_connect_swapped(G_OBJECT(GTK_ENTRY(GTK_BIN(RunProgramComboBox)->child)),"activate",
+ G_CALLBACK(Run_Program_With_Directory),G_OBJECT(RunProgramComboBox));
+
+ /* The button to Browse */
+ Button = Create_Button_With_Pixmap(BUTTON_BROWSE);
+ gtk_box_pack_start(GTK_BOX(HBox),Button,FALSE,FALSE,0);
+ g_signal_connect_swapped(G_OBJECT(Button),"clicked",
+ G_CALLBACK(File_Selection_Window_For_File),G_OBJECT(GTK_BIN(RunProgramComboBox)->child));
+
+ /* We attach usefull data to the combobox (into Run_Program_With_Directory) */
+ g_object_set_data(G_OBJECT(RunProgramComboBox), "Current_Directory", current_directory);
+
+ /* Separator line */
+ Separator = gtk_hseparator_new();
+ gtk_box_pack_start(GTK_BOX(VBox),Separator,FALSE,FALSE,0);
+
+ ButtonBox = gtk_hbutton_box_new ();
+ gtk_box_pack_start(GTK_BOX(VBox),ButtonBox,FALSE,FALSE,0);
+ gtk_button_box_set_layout(GTK_BUTTON_BOX(ButtonBox),GTK_BUTTONBOX_END);
+ gtk_box_set_spacing(GTK_BOX(ButtonBox), 10);
+
+ /* Button to cancel */
+ Button = Create_Button_With_Pixmap(BUTTON_CANCEL);
+ gtk_container_add(GTK_CONTAINER(ButtonBox),Button);
+ GTK_WIDGET_SET_FLAGS(Button,GTK_CAN_DEFAULT);
+ gtk_widget_grab_default(Button);
+ g_signal_connect(G_OBJECT(Button),"clicked", G_CALLBACK(Destroy_Run_Program_Tree_Window),NULL);
+
+ /* Button to execute */
+ Button = Create_Button_With_Pixmap(BUTTON_EXECUTE);
+ gtk_container_add(GTK_CONTAINER(ButtonBox),Button);
+ GTK_WIDGET_SET_FLAGS(Button,GTK_CAN_DEFAULT);
+ g_signal_connect_swapped(G_OBJECT(Button),"clicked", G_CALLBACK(Run_Program_With_Directory),G_OBJECT(RunProgramComboBox));
+ g_signal_connect_swapped(G_OBJECT(GTK_ENTRY(GTK_BIN(RunProgramComboBox)->child)),"changed", G_CALLBACK(Entry_Changed_Disable_Object),G_OBJECT(Button));
+ g_signal_emit_by_name(G_OBJECT(GTK_ENTRY(GTK_BIN(RunProgramComboBox)->child)),"changed",NULL);
+
+ gtk_widget_show_all(RunProgramTreeWindow);
+}
+
+void Destroy_Run_Program_Tree_Window (void)
+{
+ if (RunProgramTreeWindow)
+ {
+ gtk_widget_destroy(RunProgramTreeWindow);
+ RunProgramTreeWindow = (GtkWidget *)NULL;
+ }
+}
+
+gboolean Run_Program_Tree_Window_Key_Press (GtkWidget *window, GdkEvent *event)
+{
+ GdkEventKey *kevent;
+
+ if (event && event->type == GDK_KEY_PRESS)
+ {
+ kevent = (GdkEventKey *)event;
+ switch(kevent->keyval)
+ {
+ case GDK_Escape:
+ Destroy_Run_Program_Tree_Window();
+ break;
+ }
+ }
+ return FALSE;
+}
+
+void Run_Program_With_Directory (GtkObject *combobox)
+{
+ gchar *program_name;
+ gchar *current_directory;
+ GList *args_list = NULL;
+ gboolean program_ran;
+
+ if (!GTK_IS_COMBO_BOX(combobox)) return;
+
+ program_name = g_strdup(gtk_entry_get_text(GTK_ENTRY(GTK_BIN(combobox)->child)));
+ current_directory = g_object_get_data(G_OBJECT(combobox), "Current_Directory");
+#ifdef WIN32
+ /* On win32 : 'winamp.exe "c:\path\to\dir"' succeed, while 'winamp.exe "c:\path\to\dir\"' fails */
+ ET_Win32_Path_Remove_Trailing_Backslash(current_directory);
+#endif
+
+ // List of parameters (here only one! : the current directory)
+ args_list = g_list_append(args_list,current_directory);
+
+ program_ran = Run_Program(program_name,args_list);
+ g_list_free(args_list);
+
+ if (program_ran)
+ {
+ // Append newest choice to the drop down list
+ Add_String_To_Combo_List(RunProgramModel, program_name);
+
+ // Save list attached to the combobox
+ Save_Run_Program_With_Directory_List(RunProgramModel, MISC_COMBO_TEXT);
+
+ Destroy_Run_Program_Tree_Window();
+ }
+ g_free(program_name);
+}
+
+/*
+ * Window where is typed the name of the program to run, which
+ * receives the current file as parameter.
+ */
+void Browser_Open_Run_Program_List_Window (void)
+{
+ GtkWidget *Frame;
+ GtkWidget *VBox;
+ GtkWidget *HBox;
+ GtkWidget *Label;
+ GtkWidget *RunProgramComboBox;
+ GtkWidget *ButtonBox;
+ GtkWidget *Button;
+ GtkWidget *Separator;
+ GtkTooltips *Tips;
+
+ if (RunProgramListWindow != NULL)
+ {
+ gdk_window_raise(RunProgramListWindow->window);
+ return;
+ }
+
+ RunProgramListWindow = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title(GTK_WINDOW(RunProgramListWindow),_("Open File with ..."));
+ gtk_window_set_transient_for(GTK_WINDOW(RunProgramListWindow),GTK_WINDOW(MainWindow));
+ g_signal_connect(G_OBJECT(RunProgramListWindow),"destroy", G_CALLBACK(Destroy_Run_Program_List_Window),NULL);
+ g_signal_connect(G_OBJECT(RunProgramListWindow),"delete_event", G_CALLBACK(Destroy_Run_Program_List_Window),NULL);
+ g_signal_connect(G_OBJECT(RunProgramListWindow),"key_press_event", G_CALLBACK(Run_Program_List_Window_Key_Press),NULL);
+
+ // Just center over mainwindow
+ gtk_window_set_position(GTK_WINDOW(RunProgramListWindow),GTK_WIN_POS_CENTER_ON_PARENT);
+
+ Tips = gtk_tooltips_new();
+
+ Frame = gtk_frame_new(NULL);
+ gtk_container_add(GTK_CONTAINER(RunProgramListWindow),Frame);
+ gtk_container_set_border_width(GTK_CONTAINER(Frame),2);
+
+ VBox = gtk_vbox_new(FALSE,4);
+ gtk_container_add(GTK_CONTAINER(Frame),VBox);
+ gtk_container_set_border_width(GTK_CONTAINER(VBox), 4);
+
+ Label = gtk_label_new(_("Program to run :"));
+ gtk_box_pack_start(GTK_BOX(VBox),Label,TRUE,TRUE,0);
+ gtk_label_set_line_wrap(GTK_LABEL(Label),TRUE);
+
+ HBox = gtk_hbox_new(FALSE,4);
+ gtk_box_pack_start(GTK_BOX(VBox),HBox,FALSE,FALSE,2);
+ gtk_container_set_border_width(GTK_CONTAINER(HBox), 2);
+
+ /* The combobox to enter the program to run */
+ RunProgramComboBox = gtk_combo_box_entry_new_with_model(GTK_TREE_MODEL(RunProgramModel), MISC_COMBO_TEXT);
+ gtk_box_pack_start(GTK_BOX(HBox),RunProgramComboBox,TRUE,TRUE,0);
+ gtk_widget_set_size_request(GTK_WIDGET(RunProgramComboBox),250,-1);
+ gtk_tooltips_set_tip(Tips,GTK_WIDGET(GTK_ENTRY(GTK_BIN(RunProgramComboBox)->child)),_("Enter the program to run. "
+ "It will receive the current file as parameter."),NULL);
+
+ /* History list */
+ gtk_list_store_clear(RunProgramModel);
+ Load_Run_Program_With_File_List(RunProgramModel, MISC_COMBO_TEXT);
+ g_signal_connect_swapped(G_OBJECT(GTK_ENTRY(GTK_BIN(RunProgramComboBox)->child)),"activate",
+ G_CALLBACK(Run_Program_With_Selected_Files),G_OBJECT(RunProgramComboBox));
+
+ /* The button to Browse */
+ Button = Create_Button_With_Pixmap(BUTTON_BROWSE);
+ gtk_box_pack_start(GTK_BOX(HBox),Button,FALSE,FALSE,0);
+ g_signal_connect_swapped(G_OBJECT(Button),"clicked",
+ G_CALLBACK(File_Selection_Window_For_File),G_OBJECT(GTK_BIN(RunProgramComboBox)->child));
+
+ /* We attach usefull data to the combobox (into Run_Program_With_Directory) */
+ //g_object_set_data(G_OBJECT(Combo), "Current_File", current_file);
+
+ /* Separator line */
+ Separator = gtk_hseparator_new();
+ gtk_box_pack_start(GTK_BOX(VBox),Separator,FALSE,FALSE,0);
+
+ ButtonBox = gtk_hbutton_box_new ();
+ gtk_box_pack_start(GTK_BOX(VBox),ButtonBox,FALSE,FALSE,0);
+ gtk_button_box_set_layout(GTK_BUTTON_BOX(ButtonBox),GTK_BUTTONBOX_END);
+ gtk_box_set_spacing(GTK_BOX(ButtonBox), 10);
+
+ /* Button to cancel */
+ Button = Create_Button_With_Pixmap(BUTTON_CANCEL);
+ gtk_container_add(GTK_CONTAINER(ButtonBox),Button);
+ GTK_WIDGET_SET_FLAGS(Button,GTK_CAN_DEFAULT);
+ gtk_widget_grab_default(Button);
+ g_signal_connect(G_OBJECT(Button),"clicked", G_CALLBACK(Destroy_Run_Program_List_Window),NULL);
+
+ /* Button to execute */
+ Button = Create_Button_With_Pixmap(BUTTON_EXECUTE);
+ gtk_container_add(GTK_CONTAINER(ButtonBox),Button);
+ GTK_WIDGET_SET_FLAGS(Button,GTK_CAN_DEFAULT);
+ g_signal_connect_swapped(G_OBJECT(Button),"clicked", G_CALLBACK(Run_Program_With_Selected_Files),G_OBJECT(RunProgramComboBox));
+ g_signal_connect_swapped(G_OBJECT(GTK_ENTRY(GTK_BIN(RunProgramComboBox)->child)),"changed", G_CALLBACK(Entry_Changed_Disable_Object),G_OBJECT(Button));
+ g_signal_emit_by_name(G_OBJECT(GTK_ENTRY(GTK_BIN(RunProgramComboBox)->child)),"changed",NULL);
+
+ gtk_widget_show_all(RunProgramListWindow);
+}
+
+void Destroy_Run_Program_List_Window (void)
+{
+ if (RunProgramListWindow)
+ {
+ gtk_widget_destroy(RunProgramListWindow);
+ RunProgramListWindow = (GtkWidget *)NULL;
+ }
+}
+
+gboolean Run_Program_List_Window_Key_Press(GtkWidget *window, GdkEvent *event)
+{
+ GdkEventKey *kevent;
+
+ if (event && event->type == GDK_KEY_PRESS)
+ {
+ kevent = (GdkEventKey *)event;
+ switch(kevent->keyval)
+ {
+ case GDK_Escape:
+ Destroy_Run_Program_List_Window();
+ break;
+ }
+ }
+ return FALSE;
+}
+
+void Run_Program_With_Selected_Files (GtkObject *combobox)
+{
+ gchar *program_name;
+ ET_File *ETFile;
+ GList *selected_paths;
+ GList *args_list = NULL;
+ GtkTreeIter iter;
+ gboolean program_ran;
+ gboolean valid;
+
+ if (!GTK_IS_COMBO_BOX(combobox) || !ETCore->ETFileDisplayedList)
+ return;
+
+ // Programe name to run
+ program_name = g_strdup(gtk_entry_get_text(GTK_ENTRY(GTK_BIN(combobox)->child)));
+
+ // List of files to pass as parameters
+ selected_paths = gtk_tree_selection_get_selected_rows(gtk_tree_view_get_selection(GTK_TREE_VIEW(BrowserList)), NULL);
+ while (selected_paths)
+ {
+ valid = gtk_tree_model_get_iter(GTK_TREE_MODEL(fileListModel), &iter, (GtkTreePath*)selected_paths->data);
+ if (valid)
+ {
+ gtk_tree_model_get(GTK_TREE_MODEL(fileListModel), &iter,
+ LIST_FILE_POINTER, &ETFile,
+ -1);
+
+ args_list = g_list_append(args_list,((File_Name *)ETFile->FileNameCur->data)->value);
+ //args_list = g_list_append(args_list,((File_Name *)ETFile->FileNameCur->data)->value_utf8);
+ }
+
+ if (!selected_paths->next) break;
+ selected_paths = selected_paths->next;
+ }
+
+ program_ran = Run_Program(program_name,args_list);
+
+ g_list_foreach(selected_paths, (GFunc)gtk_tree_path_free, NULL);
+ g_list_free(selected_paths);
+ g_list_free(args_list);
+
+ if (program_ran)
+ {
+ // Append newest choice to the drop down list
+ //gtk_list_store_prepend(GTK_LIST_STORE(RunProgramModel), &iter);
+ //gtk_list_store_set(RunProgramModel, &iter, MISC_COMBO_TEXT, program_name, -1);
+ Add_String_To_Combo_List(GTK_LIST_STORE(RunProgramModel), (gchar *)program_name);
+
+ // Save list attached to the combobox
+ Save_Run_Program_With_File_List(RunProgramModel, MISC_COMBO_TEXT);
+
+ Destroy_Run_Program_List_Window();
+ }
+ g_free(program_name);
+}
+
+/*
+ * Run a program with a list of parameters
+ * - args_list : list of filename (with path)
+ */
+gboolean Run_Program (gchar *program_name, GList *args_list)
+{
+#ifdef WIN32
+ GList *filelist;
+ gchar **argv;
+ gint argv_index = 0;
+ gchar *argv_join;
+ gchar *full_command;
+ STARTUPINFO siStartupInfo;
+ PROCESS_INFORMATION piProcessInfo;
+#else
+ pid_t pid;
+#endif
+ gchar *msg;
+ gchar *program_path;
+
+
+ /* Check if a name for the program have been supplied */
+ if (!program_name || strlen(program_name)<1)
+ {
+ GtkWidget *msgbox;
+
+ msgbox = msg_box_new(_("Error..."),_("You must type a program name!"),GTK_STOCK_DIALOG_ERROR,BUTTON_OK,0);
+ msg_box_hide_check_button(MSG_BOX(msgbox));
+ msg_box_run(MSG_BOX(msgbox));
+ gtk_widget_destroy(msgbox);
+ return FALSE;
+ }
+
+ if ( !(program_path = Check_If_Executable_Exists(program_name)) )
+ {
+ GtkWidget *msgbox;
+ gchar *msg;
+
+ msg = g_strdup_printf(_("The program '%s' can't be found!"),program_name);
+ msgbox = msg_box_new(_("Error..."),msg,GTK_STOCK_DIALOG_ERROR,BUTTON_OK,0);
+ msg_box_hide_check_button(MSG_BOX(msgbox));
+ msg_box_run(MSG_BOX(msgbox));
+ gtk_widget_destroy(msgbox);
+ g_free(msg);
+ return FALSE;
+ }
+
+
+#ifdef WIN32
+
+ filelist = args_list;
+
+ // See documentation : http://c.developpez.com/faq/vc/?page=ProcessThread and http://www.answers.com/topic/createprocess
+ ZeroMemory(&siStartupInfo, sizeof(siStartupInfo));
+ siStartupInfo.cb = sizeof(siStartupInfo);
+ ZeroMemory(&piProcessInfo, sizeof(piProcessInfo));
+
+ argv = g_new0(gchar *,g_list_length(filelist) + 2); // "+2" for 1rst arg 'foo' and last arg 'NULL'
+ //argv[argv_index++] = "foo";
+
+ // Load files as arguments
+ while (filelist)
+ {
+ // We must enclose filename between " because of possible (probable!) spaces in filenames"
+ argv[argv_index++] = g_strconcat("\"", (gchar *)filelist->data, "\"", NULL);
+ filelist = filelist->next;
+ }
+ argv[argv_index] = NULL; // Ends the list of arguments
+
+ // Make a command line with all arguments (joins strings together to form one long string separated by a space)
+ argv_join = g_strjoinv(" ", argv);
+ // Build the full command to pass to CreateProcess (FIX ME : it will ignore args of program)
+ full_command = g_strconcat("\"",program_path,"\" ",argv_join,NULL);
+
+ //if (CreateProcess(program_path, // Here it doesn't seem to load all the selected files
+ // argv_join,
+ if (CreateProcess(NULL,
+ full_command,
+ NULL,
+ NULL,
+ FALSE,
+ CREATE_DEFAULT_ERROR_MODE,
+ NULL,
+ NULL,
+ &siStartupInfo,
+ &piProcessInfo) == FALSE)
+ {
+ Log_Print(_("Can't execute %s (error %d)!\n"), program_name, GetLastError());
+ }
+
+ // Free allocated parameters (for each filename)
+ for (argv_index = 1; argv[argv_index]; argv_index++)
+ g_free(argv[argv_index]);
+
+ g_free(argv_join);
+ g_free(full_command);
+ g_free(program_path);
+
+#else
+
+ g_free(program_path); // Freed as never used
+
+ pid = fork();
+ switch (pid)
+ {
+ case -1:
+ Log_Print(_("Can't fork another process!\n"));
+ //exit(-1);
+ break;
+ case 0:
+ {
+ gchar **argv;
+ gint argv_index = 0;
+ gchar **argv_user;
+ gint argv_user_number;
+
+ argv_user = g_strsplit(program_name," ",0); // the string may contains arguments, space is the delimiter
+ // Number of arguments into 'argv_user'
+ for (argv_user_number=0;argv_user[argv_user_number];argv_user_number++);
+
+ argv = g_new0(gchar *,argv_user_number + g_list_length(args_list) + 1); // +1 for NULL
+
+ // Load 'user' arguments (program name and more...)
+ while (argv_user[argv_index])
+ {
+ argv[argv_index] = argv_user[argv_index];
+ argv_index++;
+ }
+ // Load arguments from 'args_list'
+ while (args_list)
+ {
+ argv[argv_index] = (gchar *)args_list->data;
+ argv_index++;
+ args_list = args_list->next;
+ }
+ argv[argv_index] = NULL;
+
+ // Execution ...
+ execvp(argv[0],argv);
+
+ msg = g_strdup_printf(_("Executed command : '%s %s'"),program_name,"...");
+ Statusbar_Message(msg,TRUE);
+ g_free(msg);
+ //_exit(-1);
+ break;
+ }
+ default:
+ break;
+ }
+ return TRUE;
+
+#endif
+}
diff --git a/src/browser.h b/src/browser.h
new file mode 100755
index 0000000..3720c38
--- /dev/null
+++ b/src/browser.h
@@ -0,0 +1,178 @@
+/* browser.h - 2000/04/28 */
+/*
+ * EasyTAG - Tag editor for MP3 and Ogg Vorbis files
+ * Copyright (C) 2000-2003 Jerome Couderc <easytag@gmail.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef __BROWSER_H__
+#define __BROWSER_H__
+
+#include "et_core.h"
+
+
+/****************
+ * Declarations *
+ ****************/
+
+/*
+ * Data attached to each row of the artist list
+ */
+#if 0
+typedef struct _ArtistRow ArtistRow;
+struct _ArtistRow
+{
+ GList *AlbumList; // It's a list of AlbumList items...
+};
+#endif
+
+/*
+ * Data attached to each row of the artist list
+ */
+#if 0
+typedef struct _AlbumRow AlbumRow;
+struct _AlbumRow
+{
+ GList *ETFileList; // It's a list of ETFile items...
+};
+#endif
+
+/*
+ * To number columns of ComboBox
+ */
+enum
+{
+ MISC_COMBO_TEXT, // = 0 (First column)
+ MISC_COMBO_COUNT // = 1 (Number of columns in ComboBox)
+};
+
+
+enum
+{
+ TREE_COLUMN_DIR_NAME,
+ TREE_COLUMN_FULL_PATH,
+ TREE_COLUMN_SCANNED,
+ TREE_COLUMN_HAS_SUBDIR,
+ TREE_COLUMN_PIXBUF,
+ TREE_COLUMN_COUNT
+};
+
+enum
+{
+ LIST_FILE_NAME,
+ LIST_FILE_POINTER,
+ LIST_FILE_KEY,
+ LIST_FILE_OTHERDIR,
+ LIST_FONT_WEIGHT,
+ LIST_ROW_BACKGROUND,
+ LIST_ROW_FOREGROUND,
+ LIST_COLUMN_COUNT
+};
+
+enum
+{
+ ARTIST_NAME,
+ ARTIST_NUM_ALBUMS,
+ ARTIST_NUM_FILES,
+ ARTIST_ALBUM_LIST_POINTER,
+ ARTIST_FONT_STYLE,
+ ARTIST_FONT_WEIGHT,
+ ARTIST_ROW_FOREGROUND,
+ ARTIST_COLUMN_COUNT
+};
+
+enum
+{
+ ALBUM_NAME,
+ ALBUM_NUM_FILES,
+ ALBUM_ETFILE_LIST_POINTER,
+ ALBUM_FONT_STYLE,
+ ALBUM_FONT_WEIGHT,
+ ALBUM_ROW_FOREGROUND,
+ ALBUM_COLUMN_COUNT
+};
+
+
+GtkWidget *BrowserTree;
+GtkWidget *BrowserList;
+GtkWidget *BrowserArtistList;
+GtkWidget *BrowserAlbumList;
+GtkWidget *BrowserEntryCombo;
+GtkListStore *BrowserEntryModel;
+GtkWidget *BrowserHPaned;
+GtkWidget *ArtistAlbumVPaned;
+
+GtkWidget *RenameDirectoryWindow;
+GtkWidget *RenameDirectoryMaskCombo;
+GtkWidget *RenameDirectoryPreviewLabel;
+
+
+
+/**************
+ * Prototypes *
+ **************/
+
+GtkWidget *Create_Browser_Items (GtkWidget *parent);
+gint Browser_Tree_Select_Dir (gchar *current_path);
+void Browser_Tree_Rebuild (gchar *path_to_load);
+void Browser_Tree_Collapse (void);
+
+void Browser_List_Load_File_List (GList *etfilelist, ET_File *etfile_to_select);
+void Browser_List_Refresh_Whole_List (void);
+void Browser_List_Refresh_File_In_List (ET_File *ETFile);
+void Browser_List_Clear (void);
+void Browser_List_Select_File_By_Etfile (ET_File *ETFile, gboolean select_it);
+GtkTreePath *Browser_List_Select_File_By_Etfile2 (ET_File *searchETFile, gboolean select_it, GtkTreePath *startPath);
+void Browser_List_Select_File_By_Iter (GtkTreeIter *iter, gboolean select_it);
+void Browser_List_Select_File_By_Iter_String(const gchar* stringiter, gboolean select_it);
+ET_File *Browser_List_Select_File_By_DLM (const gchar* string, gboolean select_it);
+void Browser_List_Unselect_File_By_Etfile (ET_File *ETFile);
+void Browser_List_Unselect_File_By_Iter (GtkTreeIter *iter);
+void Browser_List_Unselect_File_By_Iter_String(const gchar* stringiter);
+void Browser_List_Scroll_Vertical (/*GtkCList *clist, GtkScrollType scroll_type,
+ gfloat position, gpointer user_data*/);
+void Browser_List_Set_Row_Appearance (GtkTreeIter *iter);
+void Browser_List_Refresh_Sort (void);
+void Browser_List_Select_All_Files (void);
+void Browser_List_Unselect_All_Files (void);
+void Browser_List_Invert_File_Selection (void);
+void Browser_List_Remove_File (ET_File *ETFile);
+ET_File *Browser_List_Get_ETFile_From_Path (GtkTreePath *path);
+ET_File *Browser_List_Get_ETFile_From_Iter (GtkTreeIter *iter);
+
+void Browser_Artist_List_Load_Files (ET_File *etfile_to_select);
+
+void Browser_Entry_Set_Text (gchar *text);
+void Browser_Label_Set_Text (gchar *text);
+
+void Browser_Display_Tree_Or_Artist_Album_List (void);
+
+void Browser_Area_Set_Sensitive (gboolean activate);
+
+void Browser_Load_Home_Directory (void);
+void Browser_Load_Default_Directory (void);
+void Browser_Reload_Directory (void);
+gchar *Browser_Tree_Get_Path_Of_Selected_Node (void);
+void Set_Current_Path_As_Default (void);
+gchar *Browser_Get_Current_Path (void);
+
+void Browser_Open_Rename_Directory_Window (void);
+void Browser_Open_Run_Program_Tree_Window (void);
+void Browser_Open_Run_Program_List_Window (void);
+
+
+#endif /* __BROWSER_H__ */
diff --git a/src/cddb.c b/src/cddb.c
new file mode 100755
index 0000000..42610da
--- /dev/null
+++ b/src/cddb.c
@@ -0,0 +1,4105 @@
+/* cddb.c - 2000/09/15 */
+/*
+ * EasyTAG - Tag editor for MP3 and Ogg Vorbis files
+ * Copyright (C) 2000-2003 Jerome Couderc <easytag@gmail.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+#include <glib/gi18n-lib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#ifdef WIN32
+# include "win32/win32dep.h"
+#else
+# include <sys/socket.h>
+// Patch OpenBSD from Jim Geovedi
+# include <netinet/in.h>
+# include <arpa/inet.h>
+// End patch
+# include <netdb.h>
+#endif
+#include <errno.h>
+
+#include "cddb.h"
+#include "easytag.h"
+#include "et_core.h"
+#include "browser.h"
+#include "scan.h"
+#include "log.h"
+#include "misc.h"
+#include "setting.h"
+#include "id3_tag.h"
+#include "setting.h"
+#include "msgbox.h"
+#include "charset.h"
+
+enum
+{
+ CDDB_ALBUM_LIST_BITMAP,
+ CDDB_ALBUM_LIST_ALBUM,
+ CDDB_ALBUM_LIST_CATEGORY,
+ CDDB_ALBUM_LIST_DATA,
+ CDDB_ALBUM_LIST_FONT_STYLE,
+ CDDB_ALBUM_LIST_FONT_WEIGHT,
+ CDDB_ALBUM_LIST_FOREGROUND_COLOR,
+ CDDB_ALBUM_LIST_COUNT
+};
+
+enum
+{
+ CDDB_TRACK_LIST_NUMBER,
+ CDDB_TRACK_LIST_NAME,
+ CDDB_TRACK_LIST_TIME,
+ CDDB_TRACK_LIST_DATA,
+ CDDB_TRACK_LIST_ETFILE,
+ CDDB_TRACK_LIST_COUNT
+};
+
+enum
+{
+ SORT_LIST_NUMBER,
+ SORT_LIST_NAME
+};
+
+
+#define CDDB_GENRE_MAX ( sizeof(cddb_genre_vs_id3_genre)/sizeof(cddb_genre_vs_id3_genre[0]) - 1 )
+static char *cddb_genre_vs_id3_genre [][2] =
+{
+ /* Cddb Genre - ID3 Genre */
+ {"Blues", "Blues"},
+ {"Classical", "Classical"},
+ {"Country", "Country"},
+ {"Data", "Other"},
+ {"Folk", "Folk"},
+ {"Jazz", "Jazz"},
+ {"NewAge", "New Age"},
+ {"Reggae", "Reggae"},
+ {"Rock", "Rock"},
+ {"Soundtrack", "Soundtrack"},
+ {"Misc", "Other"}
+};
+
+
+// File for result of the Cddb/Freedb request (on remote access)
+gchar *CDDB_RESULT_FILE = ".easytag/cddb_result_file.tmp";
+
+
+/****************
+ * Declarations *
+ ****************/
+GtkWidget *CddbNoteBook;
+GList *CddbAlbumList = NULL;
+
+GtkWidget *CddbSearchStringCombo = NULL;
+GtkListStore *CddbSearchStringModel = NULL;
+
+GtkWidget *CddbSearchStringInResultCombo;
+GtkListStore *CddbSearchStringInResultModel = NULL;
+
+GtkWidget *CddbAlbumListView = NULL;
+GtkListStore *CddbAlbumListModel = NULL;
+GtkWidget *CddbTrackListView = NULL;
+GtkListStore *CddbTrackListModel = NULL;
+GtkWidget *CddbApplyButton = NULL;
+GtkWidget *CddbSearchButton = NULL;
+GtkWidget *CddbSearchAutoButton = NULL;
+GtkWidget *CddbStatusBar;
+guint CddbStatusBarContext;
+
+GtkWidget *CddbStopSearchButton;
+GtkWidget *CddbStopSearchAutoButton;
+GtkWidget *CddbSearchStringInResultNextButton;
+GtkWidget *CddbSearchStringInResultPrevButton;
+GtkWidget *CddbDisplayRedLinesButton;
+GtkWidget *CddbSelectAllInResultButton;
+GtkWidget *CddbUnselectAllInResultButton;
+GtkWidget *CddbInvertSelectionInResultButton;
+
+gboolean CddbStopSearch = FALSE;
+
+
+/**************
+ * Prototypes *
+ **************/
+gboolean Cddb_Destroy_Window (GtkWidget *widget, GdkEvent *event, gpointer data);
+gboolean Cddb_Window_Key_Press (GtkWidget *window, GdkEvent *event);
+void Cddb_Show_Album_Info (GtkTreeSelection *selection, gpointer data);
+
+gboolean Cddb_Free_Album_List (void);
+gboolean Cddb_Free_Track_Album_List (GList *track_list);
+
+gint Cddb_Open_Connection (gchar *host, gint port);
+void Cddb_Close_Connection (gint socket_id);
+gint Cddb_Read_Line (FILE **file, gchar **cddb_out);
+gint Cddb_Read_Http_Header (FILE **file, gchar **cddb_out);
+gint Cddb_Read_Cddb_Header (FILE **file, gchar **cddb_out);
+
+gint Cddb_Write_Result_To_File (gint socket_id, gulong *bytes_read_total);
+
+gboolean Cddb_Search_Album_List_From_String (void);
+gboolean Cddb_Search_Album_List_From_String_Freedb (void);
+gboolean Cddb_Search_Album_List_From_String_Gnudb (void);
+gboolean Cddb_Search_Album_From_Selected_Files (void);
+gboolean Cddb_Get_Album_Tracks_List_CB (GtkTreeSelection *selection, gpointer data);
+gboolean Cddb_Get_Album_Tracks_List (GtkTreeSelection *selection);
+
+void Cddb_Load_Album_List (gboolean only_red_lines);
+void Cddb_Load_Track_Album_List (GList *track_list);
+gboolean Cddb_Set_Track_Infos_To_File_List (void);
+void Cddb_Album_List_Set_Row_Appearance (GtkTreeIter *row);
+GdkPixbuf *Cddb_Get_Pixbuf_From_Server_Name (gchar *server_name);
+
+void Cddb_Search_In_All_Fields_Check_Button_Toggled (void);
+void Cddb_Search_In_All_Categories_Check_Button_Toggled (void);
+void Cddb_Set_To_All_Fields_Check_Button_Toggled (void);
+void Cddb_Stop_Search (void);
+void Cddb_Notebook_Switch_Page (GtkNotebook *notebook, GtkNotebookPage *page, guint page_num, gpointer user_data);
+void Cddb_Search_String_In_Result (GtkWidget *entry, GtkButton *button);
+void Cddb_Display_Red_Lines_In_Result (void);
+
+void Cddb_Set_Apply_Button_Sensivity (void);
+void Cddb_Set_Search_Button_Sensivity (void);
+void Cddb_Use_Dlm_2_Check_Button_Toggled (void);
+void Cddb_Show_Categories_Button_Toggled (void);
+gchar *Cddb_Generate_Request_String_With_Fields_And_Categories_Options (void);
+gchar *Cddb_Get_Id3_Genre_From_Cddb_Genre (gchar *cddb_genre);
+
+GtkWidget *Create_Cddb_Track_List_Popup_Menu (GtkWidget *listView);
+void Cddb_Track_List_Row_Selected (GtkTreeSelection *selection, gpointer data);
+gboolean Cddb_Track_List_Button_Press (GtkTreeView *treeView, GdkEventButton *event);
+
+void Cddb_Track_List_Select_All (void);
+void Cddb_Track_List_Unselect_All (void);
+void Cddb_Track_List_Invert_Selection (void);
+
+gint Cddb_Track_List_Sort_Func (GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer data);
+void Cddb_Track_List_Sort_By_Ascending_Track_Number (void);
+void Cddb_Track_List_Sort_By_Ascending_Track_Name (void);
+
+char *base64_encode (char *str);
+gchar *Cddb_Format_Proxy_Authentification (void);
+
+
+
+/*************
+ * Functions *
+ *************/
+void Init_CddbWindow (void)
+{
+ CddbWindow = (GtkWidget *)NULL;
+}
+
+/*
+ * The window to connect to the cd data base.
+ */
+
+void Open_Cddb_Window (void)
+{
+ GtkWidget *MainVBox, *VBox, *vbox, *hbox, *notebookvbox;
+ GtkWidget *Frame;
+ GtkWidget *Table;
+ GtkWidget *Label;
+ GtkWidget *Button;
+ GtkWidget *Separator;
+ GtkWidget *ScrollWindow;
+ GtkWidget *PopupMenu;
+ GtkWidget *Icon;
+ GtkTooltips *Tips;
+ gchar *CddbAlbumList_Titles[] = { "", N_("Artist / Album"), N_("Category")};
+ gchar *CddbTrackList_Titles[] = { "#", N_("Track Name"), N_("Time")};
+ GtkCellRenderer* renderer;
+ GtkTreeViewColumn* column;
+
+ if (CddbWindow != NULL)
+ {
+ gdk_window_raise(CddbWindow->window);
+ return;
+ }
+ Tips = gtk_tooltips_new();
+ CddbWindow = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title(GTK_WINDOW(CddbWindow),_("CD Data Base Search"));
+ gtk_window_set_position(GTK_WINDOW(CddbWindow),GTK_WIN_POS_CENTER);
+
+ // This part is needed to set correctly the position of handle panes
+ gtk_window_set_default_size(GTK_WINDOW(CddbWindow),CDDB_WINDOW_WIDTH,CDDB_WINDOW_HEIGHT);
+
+ g_signal_connect(G_OBJECT(CddbWindow),"delete_event", G_CALLBACK(Cddb_Destroy_Window),NULL);
+ g_signal_connect(G_OBJECT(CddbWindow),"key_press_event", G_CALLBACK(Cddb_Window_Key_Press),NULL);
+
+ MainVBox = gtk_vbox_new(FALSE,0);
+ gtk_container_add(GTK_CONTAINER(CddbWindow),MainVBox);
+ gtk_container_set_border_width(GTK_CONTAINER(MainVBox),1);
+
+ Frame = gtk_frame_new(NULL);
+ gtk_box_pack_start(GTK_BOX(MainVBox),Frame,TRUE,TRUE,0);
+ gtk_container_set_border_width(GTK_CONTAINER(Frame),2);
+
+ VBox = gtk_vbox_new(FALSE,4);
+ gtk_container_add(GTK_CONTAINER(Frame),VBox);
+ gtk_container_set_border_width(GTK_CONTAINER(VBox),2);
+
+
+ /*
+ * Cddb NoteBook
+ */
+ CddbNoteBook = gtk_notebook_new();
+ gtk_notebook_popup_enable(GTK_NOTEBOOK(CddbNoteBook));
+ gtk_box_pack_start(GTK_BOX(VBox),CddbNoteBook,FALSE,FALSE,0);
+
+ /*
+ * 1 - Page for automatic search (generate the CDDBId from files)
+ */
+ Label = gtk_label_new(_("Automatic Search"));
+ Frame = gtk_frame_new(NULL);
+ gtk_notebook_append_page(GTK_NOTEBOOK(CddbNoteBook),Frame,Label);
+ gtk_container_border_width(GTK_CONTAINER(Frame),2);
+
+ notebookvbox = gtk_vbox_new(FALSE,4);
+ gtk_container_add(GTK_CONTAINER(Frame),notebookvbox);
+ gtk_container_border_width(GTK_CONTAINER(notebookvbox),2);
+
+ hbox = gtk_hbox_new(FALSE,4);
+ gtk_box_pack_start(GTK_BOX(notebookvbox),hbox,FALSE,FALSE,0);
+
+ Label = gtk_label_new(_("Request CD database :"));
+ gtk_misc_set_alignment(GTK_MISC(Label),1.0,0.5);
+ gtk_box_pack_start(GTK_BOX(hbox),Label,FALSE,FALSE,0);
+
+ // Button to generate CddbId and request string from the selected files
+ CddbSearchAutoButton = Create_Button_With_Pixmap(BUTTON_SEARCH);
+ gtk_box_pack_start(GTK_BOX(hbox),CddbSearchAutoButton,FALSE,FALSE,0);
+ GTK_WIDGET_SET_FLAGS(CddbSearchAutoButton,GTK_CAN_DEFAULT);
+ gtk_widget_grab_default(CddbSearchAutoButton);
+ g_signal_connect(GTK_OBJECT(CddbSearchAutoButton),"clicked",G_CALLBACK(Cddb_Search_Album_From_Selected_Files),NULL);
+ gtk_tooltips_set_tip(Tips,CddbSearchAutoButton,_("Request automatically the "
+ "CDDB database using the selected files (the order is important!) to "
+ "generate the CddbID."),NULL);
+
+ // Button to stop the search
+ CddbStopSearchAutoButton = Create_Button_With_Icon_And_Label(GTK_STOCK_STOP,NULL);
+ gtk_box_pack_start(GTK_BOX(hbox),CddbStopSearchAutoButton,FALSE,FALSE,0);
+ gtk_button_set_relief(GTK_BUTTON(CddbStopSearchAutoButton),GTK_RELIEF_NONE);
+ gtk_widget_set_sensitive(GTK_WIDGET(CddbStopSearchAutoButton),FALSE);
+ g_signal_connect(G_OBJECT(CddbStopSearchAutoButton), "clicked", G_CALLBACK(Cddb_Stop_Search), NULL);
+ gtk_tooltips_set_tip(Tips,CddbStopSearchAutoButton,_("Stop the search ..."),NULL);
+
+ // Separator line
+ Separator = gtk_vseparator_new();
+ gtk_box_pack_start(GTK_BOX(hbox),Separator,FALSE,FALSE,0);
+
+ // Check box to run the scanner
+ CddbUseLocalAccess = gtk_check_button_new_with_label(_("Use local Cddb"));
+ gtk_box_pack_start(GTK_BOX(hbox),CddbUseLocalAccess,FALSE,FALSE,0);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(CddbUseLocalAccess),CDDB_USE_LOCAL_ACCESS);
+ gtk_tooltips_set_tip(Tips,CddbUseLocalAccess,_("When activating this option, after loading the "
+ "fields, the current selected scanner will be ran (the scanner window must be opened)."),NULL);
+
+ // Separator line
+ Separator = gtk_vseparator_new();
+ gtk_box_pack_start(GTK_BOX(hbox),Separator,FALSE,FALSE,0);
+
+ // Button to select all files in list
+ Button = Create_Button_With_Icon_And_Label("easytag-select-all",NULL);
+ gtk_box_pack_start(GTK_BOX(hbox),Button,FALSE,FALSE,0);
+ gtk_button_set_relief(GTK_BUTTON(Button),GTK_RELIEF_NONE);
+ gtk_tooltips_set_tip(Tips,Button,_("Select All Files"),NULL);
+ g_signal_connect(G_OBJECT(Button),"clicked",G_CALLBACK(Action_Select_All_Files),NULL);
+
+ // Button to invert selection of files in list
+ Button = Create_Button_With_Icon_And_Label("easytag-invert-selection",NULL);
+ gtk_box_pack_start(GTK_BOX(hbox),Button,FALSE,FALSE,0);
+ gtk_button_set_relief(GTK_BUTTON(Button),GTK_RELIEF_NONE);
+ gtk_tooltips_set_tip(Tips,Button,_("Invert Files Selection"),NULL);
+ g_signal_connect(G_OBJECT(Button),"clicked",G_CALLBACK(Action_Invert_Files_Selection),NULL);
+
+/* // Button to sort by ascending filename
+ Button = Create_Button_With_Icon_And_Label(GTK_STOCK_SORT_ASCENDING,NULL);
+ gtk_box_pack_start(GTK_BOX(hbox),Button,FALSE,FALSE,0);
+ gtk_button_set_relief(GTK_BUTTON(Button),GTK_RELIEF_NONE);
+ gtk_tooltips_set_tip(Tips,Button,_("Sort list ascending by filename"),NULL);
+ g_signal_connect(G_OBJECT(Button),"clicked",G_CALLBACK(ET_Sort_Displayed_File_List_And_Update_UI),GINT_TO_POINTER(SORTING_BY_ASCENDING_FILENAME));
+
+ // Button to sort by ascending track number
+ Button = Create_Button_With_Icon_And_Label(GTK_STOCK_SORT_ASCENDING,NULL);
+ gtk_box_pack_start(GTK_BOX(hbox),Button,FALSE,FALSE,0);
+ gtk_button_set_relief(GTK_BUTTON(Button),GTK_RELIEF_NONE);
+ gtk_tooltips_set_tip(Tips,Button,_("Sort list ascending by track number"),NULL);
+ g_signal_connect(G_OBJECT(Button),"clicked",G_CALLBACK(ET_Sort_Displayed_File_List_And_Update_UI),GINT_TO_POINTER(SORTING_BY_ASCENDING_TRACK_NUMBER));
+*/
+ // Button to quit
+ Button = Create_Button_With_Pixmap(BUTTON_CLOSE);
+ gtk_box_pack_end(GTK_BOX(hbox),Button,FALSE,FALSE,0);
+ GTK_WIDGET_SET_FLAGS(Button,GTK_CAN_DEFAULT);
+ g_signal_connect(G_OBJECT(Button),"clicked", G_CALLBACK(Cddb_Destroy_Window),NULL);
+
+
+ /*
+ * 2 - Page for manual search
+ */
+ Label = gtk_label_new(_("Manual Search"));
+ Frame = gtk_frame_new(NULL);
+ gtk_notebook_append_page(GTK_NOTEBOOK(CddbNoteBook),Frame,Label);
+ gtk_container_border_width(GTK_CONTAINER(Frame),2);
+
+ notebookvbox = gtk_vbox_new(FALSE,4);
+ gtk_container_add(GTK_CONTAINER(Frame),notebookvbox);
+ gtk_container_border_width(GTK_CONTAINER(notebookvbox),2);
+
+ /*
+ * Words to search
+ */
+ hbox = gtk_hbox_new(FALSE,4);
+ gtk_box_pack_start(GTK_BOX(notebookvbox),hbox,FALSE,FALSE,0);
+
+ Label = gtk_label_new(_("Words :"));
+ gtk_misc_set_alignment(GTK_MISC(Label),1.0,0.5);
+ gtk_box_pack_start(GTK_BOX(hbox),Label,FALSE,FALSE,0);
+
+ if(CddbSearchStringModel == NULL)
+ CddbSearchStringModel = gtk_list_store_new(MISC_COMBO_COUNT, G_TYPE_STRING);
+ else
+ gtk_list_store_clear(CddbSearchStringModel);
+
+ CddbSearchStringCombo = gtk_combo_box_entry_new_with_model(GTK_TREE_MODEL(CddbSearchStringModel), MISC_COMBO_TEXT);
+ gtk_widget_set_size_request(GTK_WIDGET(CddbSearchStringCombo),220,-1);
+ gtk_box_pack_start(GTK_BOX(hbox),CddbSearchStringCombo,FALSE,TRUE,0);
+ gtk_tooltips_set_tip(Tips,GTK_WIDGET(GTK_ENTRY(GTK_BIN(CddbSearchStringCombo)->child)),_("Enter the words to "
+ "search (separated by a space or '+')"),NULL);
+ // History List
+ Load_Cddb_Search_String_List(CddbSearchStringModel, MISC_COMBO_TEXT);
+
+ g_signal_connect(G_OBJECT(GTK_ENTRY(GTK_BIN(CddbSearchStringCombo)->child)),"activate",
+ G_CALLBACK(Cddb_Search_Album_List_From_String),NULL);
+ gtk_entry_set_text(GTK_ENTRY(GTK_BIN(CddbSearchStringCombo)->child),"");
+
+ // Set content of the clipboard if available
+ gtk_editable_paste_clipboard(GTK_EDITABLE(GTK_BIN(CddbSearchStringCombo)->child));
+
+ // Button to run the search
+ CddbSearchButton = Create_Button_With_Pixmap(BUTTON_SEARCH);
+ gtk_box_pack_start(GTK_BOX(hbox),CddbSearchButton,FALSE,FALSE,0);
+ GTK_WIDGET_SET_FLAGS(CddbSearchButton,GTK_CAN_DEFAULT);
+ gtk_widget_grab_default(CddbSearchButton);
+ g_signal_connect(G_OBJECT(CddbSearchButton),"clicked",
+ G_CALLBACK(Cddb_Search_Album_List_From_String),NULL);
+ g_signal_connect(G_OBJECT(GTK_ENTRY(GTK_BIN(CddbSearchStringCombo)->child)),"changed",
+ G_CALLBACK(Cddb_Set_Search_Button_Sensivity),NULL);
+
+ // Button to stop the search
+ CddbStopSearchButton = Create_Button_With_Icon_And_Label(GTK_STOCK_STOP,NULL);
+ gtk_box_pack_start(GTK_BOX(hbox),CddbStopSearchButton,FALSE,FALSE,0);
+ gtk_button_set_relief(GTK_BUTTON(CddbStopSearchButton),GTK_RELIEF_NONE);
+ gtk_widget_set_sensitive(GTK_WIDGET(CddbStopSearchButton),FALSE);
+ g_signal_connect(G_OBJECT(CddbStopSearchButton), "clicked", G_CALLBACK(Cddb_Stop_Search), NULL);
+ gtk_tooltips_set_tip(Tips,CddbStopSearchButton,_("Stop the search ..."),NULL);
+
+ // Button to quit
+ Button = Create_Button_With_Pixmap(BUTTON_CLOSE);
+ gtk_box_pack_end(GTK_BOX(hbox),Button,FALSE,FALSE,0);
+ GTK_WIDGET_SET_FLAGS(Button,GTK_CAN_DEFAULT);
+ g_signal_connect(G_OBJECT(Button),"clicked", G_CALLBACK(Cddb_Destroy_Window),NULL);
+
+
+ /*
+ * Search options
+ */
+ Frame = gtk_frame_new(_("Search In :"));
+ gtk_box_pack_start(GTK_BOX(notebookvbox),Frame,FALSE,TRUE,0);
+
+ Table = gtk_table_new(7,4,FALSE);
+ gtk_container_add(GTK_CONTAINER(Frame),Table);
+ gtk_table_set_row_spacings(GTK_TABLE(Table),1);
+ gtk_table_set_col_spacings(GTK_TABLE(Table),1);
+
+ CddbSearchInAllFields = gtk_check_button_new_with_label(_("All Fields"));
+ Separator = gtk_vseparator_new();
+ CddbSearchInArtistField = gtk_check_button_new_with_label(_("Artist"));
+ CddbSearchInTitleField = gtk_check_button_new_with_label(_("Album"));
+ CddbSearchInTrackNameField = gtk_check_button_new_with_label(_("Track Name"));
+ CddbSearchInOtherField = gtk_check_button_new_with_label(_("Other"));
+ gtk_table_attach(GTK_TABLE(Table),CddbSearchInAllFields, 0,1,0,1,GTK_FILL,GTK_FILL,0,0);
+ gtk_table_attach(GTK_TABLE(Table),Separator, 1,2,0,1,GTK_FILL,GTK_FILL,0,0);
+ gtk_table_attach(GTK_TABLE(Table),CddbSearchInArtistField, 2,3,0,1,GTK_FILL,GTK_FILL,0,0);
+ gtk_table_attach(GTK_TABLE(Table),CddbSearchInTitleField, 3,4,0,1,GTK_FILL,GTK_FILL,0,0);
+ gtk_table_attach(GTK_TABLE(Table),CddbSearchInTrackNameField,4,5,0,1,GTK_FILL,GTK_FILL,0,0);
+ gtk_table_attach(GTK_TABLE(Table),CddbSearchInOtherField, 5,6,0,1,GTK_FILL,GTK_FILL,0,0);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(CddbSearchInAllFields), CDDB_SEARCH_IN_ALL_FIELDS);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(CddbSearchInArtistField), CDDB_SEARCH_IN_ARTIST_FIELD);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(CddbSearchInTitleField), CDDB_SEARCH_IN_TITLE_FIELD);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(CddbSearchInTrackNameField), CDDB_SEARCH_IN_TRACK_NAME_FIELD);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(CddbSearchInOtherField), CDDB_SEARCH_IN_OTHER_FIELD);
+ g_signal_connect(G_OBJECT(CddbSearchInAllFields), "toggled", G_CALLBACK(Cddb_Search_In_All_Fields_Check_Button_Toggled),NULL);
+ g_signal_connect(G_OBJECT(CddbSearchInAllFields), "toggled", G_CALLBACK(Cddb_Set_Search_Button_Sensivity),NULL);
+ g_signal_connect(G_OBJECT(CddbSearchInArtistField), "toggled", G_CALLBACK(Cddb_Set_Search_Button_Sensivity),NULL);
+ g_signal_connect(G_OBJECT(CddbSearchInTitleField), "toggled", G_CALLBACK(Cddb_Set_Search_Button_Sensivity),NULL);
+ g_signal_connect(G_OBJECT(CddbSearchInTrackNameField), "toggled", G_CALLBACK(Cddb_Set_Search_Button_Sensivity),NULL);
+ g_signal_connect(G_OBJECT(CddbSearchInOtherField), "toggled", G_CALLBACK(Cddb_Set_Search_Button_Sensivity),NULL);
+
+ CddbSeparatorH = gtk_hseparator_new();
+ gtk_table_attach(GTK_TABLE(Table),CddbSeparatorH,0,7,1,2,GTK_FILL,GTK_FILL,0,0);
+
+ CddbSearchInAllCategories = gtk_check_button_new_with_label(_("All Categories"));
+ CddbSeparatorV = gtk_vseparator_new();
+ CddbSearchInBluesCategory = gtk_check_button_new_with_label(_("Blues"));
+ CddbSearchInClassicalCategory = gtk_check_button_new_with_label(_("Classical"));
+ CddbSearchInCountryCategory = gtk_check_button_new_with_label(_("Country"));
+ CddbSearchInFolkCategory = gtk_check_button_new_with_label(_("Folk"));
+ CddbSearchInJazzCategory = gtk_check_button_new_with_label(_("Jazz"));
+ CddbSearchInMiscCategory = gtk_check_button_new_with_label(_("Misc"));
+ CddbSearchInNewageCategory = gtk_check_button_new_with_label(_("Newage"));
+ CddbSearchInReggaeCategory = gtk_check_button_new_with_label(_("Reggae"));
+ CddbSearchInRockCategory = gtk_check_button_new_with_label(_("Rock"));
+ CddbSearchInSoundtrackCategory = gtk_check_button_new_with_label(_("Soundtrack"));
+ gtk_table_attach(GTK_TABLE(Table),CddbSearchInAllCategories, 0,1,2,4,GTK_FILL,GTK_FILL,0,0);
+ gtk_table_attach(GTK_TABLE(Table),CddbSeparatorV, 1,2,2,4,GTK_FILL,GTK_FILL,0,0);
+ gtk_table_attach(GTK_TABLE(Table),CddbSearchInBluesCategory, 2,3,2,3,GTK_FILL,GTK_FILL,0,0);
+ gtk_table_attach(GTK_TABLE(Table),CddbSearchInClassicalCategory, 3,4,2,3,GTK_FILL,GTK_FILL,0,0);
+ gtk_table_attach(GTK_TABLE(Table),CddbSearchInCountryCategory, 4,5,2,3,GTK_FILL,GTK_FILL,0,0);
+ gtk_table_attach(GTK_TABLE(Table),CddbSearchInFolkCategory, 5,6,2,3,GTK_FILL,GTK_FILL,0,0);
+ gtk_table_attach(GTK_TABLE(Table),CddbSearchInJazzCategory, 6,7,2,3,GTK_FILL,GTK_FILL,0,0);
+ gtk_table_attach(GTK_TABLE(Table),CddbSearchInMiscCategory, 2,3,3,4,GTK_FILL,GTK_FILL,0,0);
+ gtk_table_attach(GTK_TABLE(Table),CddbSearchInNewageCategory, 3,4,3,4,GTK_FILL,GTK_FILL,0,0);
+ gtk_table_attach(GTK_TABLE(Table),CddbSearchInReggaeCategory, 4,5,3,4,GTK_FILL,GTK_FILL,0,0);
+ gtk_table_attach(GTK_TABLE(Table),CddbSearchInRockCategory, 5,6,3,4,GTK_FILL,GTK_FILL,0,0);
+ gtk_table_attach(GTK_TABLE(Table),CddbSearchInSoundtrackCategory,6,7,3,4,GTK_FILL,GTK_FILL,0,0);
+ gtk_label_set_line_wrap(GTK_LABEL(GTK_BIN(CddbSearchInAllCategories)->child),TRUE); // Wrap label of the check button.
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(CddbSearchInAllCategories), CDDB_SEARCH_IN_ALL_CATEGORIES);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(CddbSearchInBluesCategory), CDDB_SEARCH_IN_BLUES_CATEGORY);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(CddbSearchInClassicalCategory), CDDB_SEARCH_IN_CLASSICAL_CATEGORY);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(CddbSearchInCountryCategory), CDDB_SEARCH_IN_COUNTRY_CATEGORY);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(CddbSearchInFolkCategory), CDDB_SEARCH_IN_FOLK_CATEGORY);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(CddbSearchInJazzCategory), CDDB_SEARCH_IN_JAZZ_CATEGORY);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(CddbSearchInMiscCategory), CDDB_SEARCH_IN_MISC_CATEGORY);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(CddbSearchInNewageCategory), CDDB_SEARCH_IN_NEWAGE_CATEGORY);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(CddbSearchInReggaeCategory), CDDB_SEARCH_IN_REGGAE_CATEGORY);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(CddbSearchInRockCategory), CDDB_SEARCH_IN_ROCK_CATEGORY);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(CddbSearchInSoundtrackCategory),CDDB_SEARCH_IN_SOUNDTRACK_CATEGORY);
+ g_signal_connect(G_OBJECT(CddbSearchInAllCategories), "toggled",G_CALLBACK(Cddb_Search_In_All_Categories_Check_Button_Toggled),NULL);
+ g_signal_connect(G_OBJECT(CddbSearchInAllCategories), "toggled",G_CALLBACK(Cddb_Set_Search_Button_Sensivity),NULL);
+ g_signal_connect(G_OBJECT(CddbSearchInBluesCategory), "toggled",G_CALLBACK(Cddb_Set_Search_Button_Sensivity),NULL);
+ g_signal_connect(G_OBJECT(CddbSearchInClassicalCategory), "toggled",G_CALLBACK(Cddb_Set_Search_Button_Sensivity),NULL);
+ g_signal_connect(G_OBJECT(CddbSearchInCountryCategory), "toggled",G_CALLBACK(Cddb_Set_Search_Button_Sensivity),NULL);
+ g_signal_connect(G_OBJECT(CddbSearchInFolkCategory), "toggled",G_CALLBACK(Cddb_Set_Search_Button_Sensivity),NULL);
+ g_signal_connect(G_OBJECT(CddbSearchInJazzCategory), "toggled",G_CALLBACK(Cddb_Set_Search_Button_Sensivity),NULL);
+ g_signal_connect(G_OBJECT(CddbSearchInMiscCategory), "toggled",G_CALLBACK(Cddb_Set_Search_Button_Sensivity),NULL);
+ g_signal_connect(G_OBJECT(CddbSearchInNewageCategory), "toggled",G_CALLBACK(Cddb_Set_Search_Button_Sensivity),NULL);
+ g_signal_connect(G_OBJECT(CddbSearchInReggaeCategory), "toggled",G_CALLBACK(Cddb_Set_Search_Button_Sensivity),NULL);
+ g_signal_connect(G_OBJECT(CddbSearchInRockCategory), "toggled",G_CALLBACK(Cddb_Set_Search_Button_Sensivity),NULL);
+ g_signal_connect(G_OBJECT(CddbSearchInSoundtrackCategory),"toggled",G_CALLBACK(Cddb_Set_Search_Button_Sensivity),NULL);
+ gtk_tooltips_set_tip(Tips,CddbSearchInRockCategory,_("included : funk, soul, rap, pop, industrial, metal, etc."),NULL);
+ gtk_tooltips_set_tip(Tips,CddbSearchInSoundtrackCategory,_("movies, shows"),NULL);
+ gtk_tooltips_set_tip(Tips,CddbSearchInMiscCategory,_("others that do not fit in the above categories"),NULL);
+
+ // Button to display/hide the categories
+ CddbShowCategoriesButton = gtk_toggle_button_new_with_label(_(" Categories "));
+ gtk_table_attach(GTK_TABLE(Table),CddbShowCategoriesButton,6,7,0,1,GTK_FILL,GTK_FILL,4,0);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(CddbShowCategoriesButton),CDDB_SHOW_CATEGORIES);
+ g_signal_connect(G_OBJECT(CddbShowCategoriesButton),"toggled", G_CALLBACK(Cddb_Show_Categories_Button_Toggled),NULL);
+
+ /*
+ * Results command
+ */
+ Frame = gtk_frame_new(_("Results :"));
+ gtk_box_pack_start(GTK_BOX(VBox),Frame,FALSE,TRUE,0);
+
+ hbox = gtk_hbox_new(FALSE,4);
+ gtk_container_set_border_width(GTK_CONTAINER(hbox),2);
+ gtk_container_add(GTK_CONTAINER(Frame),hbox);
+
+ Label = gtk_label_new(_("Search :"));
+ gtk_misc_set_alignment(GTK_MISC(Label),1.0,0.5);
+ gtk_box_pack_start(GTK_BOX(hbox),Label,FALSE,FALSE,0);
+
+ if(CddbSearchStringInResultModel == NULL)
+ CddbSearchStringInResultModel = gtk_list_store_new(MISC_COMBO_COUNT, G_TYPE_STRING);
+ else
+ gtk_list_store_clear(CddbSearchStringInResultModel);
+
+ CddbSearchStringInResultCombo = gtk_combo_box_entry_new_with_model(GTK_TREE_MODEL(CddbSearchStringInResultModel), MISC_COMBO_TEXT);
+ gtk_box_pack_start(GTK_BOX(hbox),CddbSearchStringInResultCombo,FALSE,FALSE,0);
+ g_signal_connect_swapped(G_OBJECT(GTK_ENTRY(GTK_BIN(CddbSearchStringInResultCombo)->child)),"activate",
+ G_CALLBACK(Cddb_Search_String_In_Result), G_OBJECT(GTK_ENTRY(GTK_BIN(CddbSearchStringInResultCombo)->child)));
+ gtk_tooltips_set_tip(Tips,GTK_WIDGET(GTK_ENTRY(GTK_BIN(CddbSearchStringInResultCombo)->child)),_("Enter the words to "
+ "search in the list below"),NULL);
+
+ // History List
+ Load_Cddb_Search_String_In_Result_List(CddbSearchStringInResultModel, MISC_COMBO_TEXT);
+
+ gtk_entry_set_text(GTK_ENTRY(GTK_BIN(CddbSearchStringInResultCombo)->child),"");
+
+ CddbSearchStringInResultNextButton = Create_Button_With_Icon_And_Label(GTK_STOCK_GO_DOWN,NULL);
+ gtk_box_pack_start(GTK_BOX(hbox),CddbSearchStringInResultNextButton,FALSE,FALSE,0);
+ gtk_button_set_relief(GTK_BUTTON(CddbSearchStringInResultNextButton),GTK_RELIEF_NONE);
+ g_signal_connect_swapped(G_OBJECT(CddbSearchStringInResultNextButton),"clicked", G_CALLBACK(Cddb_Search_String_In_Result), G_OBJECT(GTK_ENTRY(GTK_BIN(CddbSearchStringInResultCombo)->child)));
+ gtk_tooltips_set_tip(Tips,CddbSearchStringInResultNextButton,_("Search Next"),NULL);
+
+ CddbSearchStringInResultPrevButton = Create_Button_With_Icon_And_Label(GTK_STOCK_GO_UP,NULL);
+ gtk_box_pack_start(GTK_BOX(hbox),CddbSearchStringInResultPrevButton,FALSE,FALSE,0);
+ gtk_button_set_relief(GTK_BUTTON(CddbSearchStringInResultPrevButton),GTK_RELIEF_NONE);
+ g_signal_connect_swapped(G_OBJECT(CddbSearchStringInResultPrevButton),"clicked", G_CALLBACK(Cddb_Search_String_In_Result), G_OBJECT(GTK_ENTRY(GTK_BIN(CddbSearchStringInResultCombo)->child)));
+ gtk_tooltips_set_tip(Tips,CddbSearchStringInResultPrevButton,_("Search Previous"),NULL);
+
+ // Separator line
+ Separator = gtk_vseparator_new();
+ gtk_box_pack_start(GTK_BOX(hbox),Separator,FALSE,FALSE,2);
+
+ CddbDisplayRedLinesButton = gtk_toggle_button_new();
+ Icon = gtk_image_new_from_stock("easytag-red-lines", GTK_ICON_SIZE_BUTTON);
+ gtk_container_add(GTK_CONTAINER(CddbDisplayRedLinesButton),Icon);
+ gtk_box_pack_start(GTK_BOX(hbox),CddbDisplayRedLinesButton,FALSE,FALSE,0);
+ gtk_button_set_relief(GTK_BUTTON(CddbDisplayRedLinesButton),GTK_RELIEF_NONE);
+ gtk_tooltips_set_tip(Tips,CddbDisplayRedLinesButton,_("Show only red lines (or show all lines) in the 'Artist / Album' list"),NULL);
+ g_signal_connect(G_OBJECT(CddbDisplayRedLinesButton),"toggled",G_CALLBACK(Cddb_Display_Red_Lines_In_Result),NULL);
+
+ CddbUnselectAllInResultButton = Create_Button_With_Icon_And_Label("easytag-unselect-all",NULL);
+ gtk_box_pack_end(GTK_BOX(hbox),CddbUnselectAllInResultButton,FALSE,FALSE,0);
+ gtk_button_set_relief(GTK_BUTTON(CddbUnselectAllInResultButton),GTK_RELIEF_NONE);
+ gtk_tooltips_set_tip(Tips,CddbUnselectAllInResultButton,_("Unselect all lines"),NULL);
+ g_signal_connect(G_OBJECT(CddbUnselectAllInResultButton),"clicked",G_CALLBACK(Cddb_Track_List_Unselect_All),NULL);
+
+ CddbInvertSelectionInResultButton = Create_Button_With_Icon_And_Label("easytag-invert-selection",NULL);
+ gtk_box_pack_end(GTK_BOX(hbox),CddbInvertSelectionInResultButton,FALSE,FALSE,0);
+ gtk_button_set_relief(GTK_BUTTON(CddbInvertSelectionInResultButton),GTK_RELIEF_NONE);
+ gtk_tooltips_set_tip(Tips,CddbInvertSelectionInResultButton,_("Invert lines selection"),NULL);
+ g_signal_connect(G_OBJECT(CddbInvertSelectionInResultButton),"clicked",G_CALLBACK(Cddb_Track_List_Invert_Selection),NULL);
+
+ CddbSelectAllInResultButton = Create_Button_With_Icon_And_Label("easytag-select-all",NULL);
+ gtk_box_pack_end(GTK_BOX(hbox),CddbSelectAllInResultButton,FALSE,FALSE,0);
+ gtk_button_set_relief(GTK_BUTTON(CddbSelectAllInResultButton),GTK_RELIEF_NONE);
+ gtk_tooltips_set_tip(Tips,CddbSelectAllInResultButton,_("Select all lines"),NULL);
+ g_signal_connect(G_OBJECT(CddbSelectAllInResultButton),"clicked",G_CALLBACK(Cddb_Track_List_Select_All),NULL);
+
+ /*
+ * Result of search
+ */
+ CddbWindowHPaned = gtk_hpaned_new();
+ gtk_box_pack_start(GTK_BOX(VBox),CddbWindowHPaned,TRUE,TRUE,0);
+ gtk_paned_set_position(GTK_PANED(CddbWindowHPaned),CDDB_PANE_HANDLE_POSITION);
+
+ // List of albums
+ ScrollWindow = gtk_scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ScrollWindow),GTK_POLICY_AUTOMATIC,GTK_POLICY_AUTOMATIC);
+ gtk_widget_set_size_request(GTK_WIDGET(ScrollWindow),-1,100);
+ gtk_paned_pack1(GTK_PANED(CddbWindowHPaned),ScrollWindow,TRUE,FALSE);
+
+ CddbAlbumListModel = gtk_list_store_new(CDDB_ALBUM_LIST_COUNT,
+ GDK_TYPE_PIXBUF,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ G_TYPE_POINTER,
+ PANGO_TYPE_STYLE,
+ G_TYPE_INT,
+ GDK_TYPE_COLOR);
+ CddbAlbumListView = gtk_tree_view_new_with_model(GTK_TREE_MODEL(CddbAlbumListModel));
+
+ renderer = gtk_cell_renderer_pixbuf_new();
+ column = gtk_tree_view_column_new_with_attributes(_(CddbAlbumList_Titles[0]), renderer,
+ "pixbuf", CDDB_ALBUM_LIST_BITMAP,
+ NULL);
+ gtk_tree_view_column_set_resizable(column, FALSE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(CddbAlbumListView), column);
+
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes(_(CddbAlbumList_Titles[1]), renderer,
+ "text", CDDB_ALBUM_LIST_ALBUM,
+ "weight", CDDB_ALBUM_LIST_FONT_WEIGHT,
+ "style", CDDB_ALBUM_LIST_FONT_STYLE,
+ "foreground-gdk", CDDB_ALBUM_LIST_FOREGROUND_COLOR,
+ NULL);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(CddbAlbumListView), column);
+
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes(_(CddbAlbumList_Titles[2]), renderer,
+ "text", CDDB_ALBUM_LIST_CATEGORY,
+ "weight", CDDB_ALBUM_LIST_FONT_WEIGHT,
+ "style", CDDB_ALBUM_LIST_FONT_STYLE,
+ "foreground-gdk", CDDB_ALBUM_LIST_FOREGROUND_COLOR,
+ NULL);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(CddbAlbumListView), column);
+ //gtk_tree_view_columns_autosize(GTK_TREE_VIEW(CddbAlbumListView));
+
+ gtk_container_add(GTK_CONTAINER(ScrollWindow), CddbAlbumListView);
+
+ gtk_tree_view_set_cursor(GTK_TREE_VIEW(CddbAlbumListView), gtk_tree_path_new_first(), NULL, FALSE);
+ g_signal_connect(G_OBJECT(gtk_tree_view_get_selection(GTK_TREE_VIEW(CddbAlbumListView))),
+ "changed", G_CALLBACK(Cddb_Show_Album_Info), NULL);
+ g_signal_connect(G_OBJECT(gtk_tree_view_get_selection(GTK_TREE_VIEW(CddbAlbumListView))),
+ "changed", G_CALLBACK(Cddb_Get_Album_Tracks_List_CB), NULL);
+
+ // List of tracks
+ ScrollWindow = gtk_scrolled_window_new(NULL,NULL);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ScrollWindow),GTK_POLICY_AUTOMATIC,GTK_POLICY_AUTOMATIC);
+
+
+ gtk_widget_set_size_request(GTK_WIDGET(ScrollWindow), -1, 100);
+ gtk_paned_pack2(GTK_PANED(CddbWindowHPaned), ScrollWindow, TRUE, FALSE);
+
+ CddbTrackListModel = gtk_list_store_new(CDDB_TRACK_LIST_COUNT,
+ G_TYPE_UINT,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ G_TYPE_POINTER,
+ G_TYPE_POINTER);
+ CddbTrackListView = gtk_tree_view_new_with_model(GTK_TREE_MODEL(CddbTrackListModel));
+ renderer = gtk_cell_renderer_text_new();
+ g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL); // Align to the right
+ column = gtk_tree_view_column_new_with_attributes(_(CddbTrackList_Titles[0]), renderer,
+ "text", CDDB_TRACK_LIST_NUMBER, NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(CddbTrackListView), column);
+ gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(CddbTrackListModel), SORT_LIST_NUMBER,
+ Cddb_Track_List_Sort_Func, GINT_TO_POINTER(SORT_LIST_NUMBER), NULL);
+ gtk_tree_view_column_set_sort_column_id(column, SORT_LIST_NUMBER);
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes(_(CddbTrackList_Titles[1]), renderer,
+ "text", CDDB_TRACK_LIST_NAME, NULL);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(CddbTrackListView), column);
+ gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(CddbTrackListModel), SORT_LIST_NAME,
+ Cddb_Track_List_Sort_Func, GINT_TO_POINTER(SORT_LIST_NAME), NULL);
+ gtk_tree_view_column_set_sort_column_id(column, SORT_LIST_NAME);
+
+ renderer = gtk_cell_renderer_text_new();
+ g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL); // Align to the right
+ column = gtk_tree_view_column_new_with_attributes(_(CddbTrackList_Titles[2]), renderer,
+ "text", CDDB_TRACK_LIST_TIME, NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(CddbTrackListView), column);
+
+ //gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(CddbTrackListModel), SORT_LIST_NUMBER, GTK_SORT_ASCENDING);
+ gtk_tree_view_set_reorderable(GTK_TREE_VIEW(CddbTrackListView), TRUE);
+
+ gtk_container_add(GTK_CONTAINER(ScrollWindow),CddbTrackListView);
+ gtk_tree_selection_set_mode(gtk_tree_view_get_selection(GTK_TREE_VIEW(CddbTrackListView)),
+ GTK_SELECTION_MULTIPLE);
+ g_signal_connect(G_OBJECT(gtk_tree_view_get_selection(GTK_TREE_VIEW(CddbTrackListView))),
+ "changed", G_CALLBACK(Cddb_Track_List_Row_Selected), NULL);
+ g_signal_connect(G_OBJECT(CddbTrackListView),"button_press_event", G_CALLBACK(Cddb_Track_List_Button_Press),NULL);
+ gtk_tooltips_set_tip(Tips, CddbTrackListView, _("Select lines to 'apply' to "
+ "your files list. All lines will be processed if no line is selected.\n"
+ "You can also reorder lines in this list before using 'apply' button."), NULL);
+
+ // Create Popup Menu on CddbTrackCList
+ PopupMenu = Create_Cddb_Track_List_Popup_Menu(CddbTrackListView);
+
+ /*
+ * Apply results to fields...
+ */
+ Frame = gtk_frame_new(_("Set Into :"));
+ gtk_box_pack_start(GTK_BOX(VBox),Frame,FALSE,TRUE,0);
+
+ vbox = gtk_vbox_new(FALSE,2);
+ gtk_container_set_border_width(GTK_CONTAINER(vbox),2);
+ gtk_container_add(GTK_CONTAINER(Frame),vbox);
+
+ CddbSetToAllFields = gtk_check_button_new_with_label(_("All"));
+ Separator = gtk_vseparator_new();
+ CddbSetToFileName = gtk_check_button_new_with_label(_("File Name"));
+ CddbSetToTitle = gtk_check_button_new_with_label(_("Title"));
+ CddbSetToArtist = gtk_check_button_new_with_label(_("Artist"));
+ CddbSetToAlbum = gtk_check_button_new_with_label(_("Album"));
+ CddbSetToYear = gtk_check_button_new_with_label(_("Year"));
+ CddbSetToTrack = gtk_check_button_new_with_label(_("Track #"));
+ CddbSetToTrackTotal = gtk_check_button_new_with_label(_("# Tracks"));
+ CddbSetToGenre = gtk_check_button_new_with_label(_("Genre"));
+ hbox = gtk_hbox_new(FALSE,0);
+ gtk_box_pack_start(GTK_BOX(vbox),hbox,FALSE,FALSE,0);
+ gtk_box_pack_start(GTK_BOX(hbox),CddbSetToAllFields, FALSE,FALSE,0);
+ gtk_box_pack_start(GTK_BOX(hbox),Separator, FALSE,FALSE,2);
+ gtk_box_pack_start(GTK_BOX(hbox),CddbSetToFileName, FALSE,FALSE,2);
+ gtk_box_pack_start(GTK_BOX(hbox),CddbSetToTitle, FALSE,FALSE,2);
+ gtk_box_pack_start(GTK_BOX(hbox),CddbSetToArtist, FALSE,FALSE,2);
+ gtk_box_pack_start(GTK_BOX(hbox),CddbSetToAlbum, FALSE,FALSE,2);
+ gtk_box_pack_start(GTK_BOX(hbox),CddbSetToYear, FALSE,FALSE,2);
+ gtk_box_pack_start(GTK_BOX(hbox),CddbSetToTrack, FALSE,FALSE,2);
+ gtk_box_pack_start(GTK_BOX(hbox),CddbSetToTrackTotal,FALSE,FALSE,2);
+ gtk_box_pack_start(GTK_BOX(hbox),CddbSetToGenre, FALSE,FALSE,2);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(CddbSetToAllFields), CDDB_SET_TO_ALL_FIELDS);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(CddbSetToTitle), CDDB_SET_TO_TITLE);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(CddbSetToArtist), CDDB_SET_TO_ARTIST);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(CddbSetToAlbum), CDDB_SET_TO_ALBUM);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(CddbSetToYear), CDDB_SET_TO_YEAR);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(CddbSetToTrack), CDDB_SET_TO_TRACK);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(CddbSetToTrackTotal),CDDB_SET_TO_TRACK_TOTAL);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(CddbSetToGenre), CDDB_SET_TO_GENRE);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(CddbSetToFileName), CDDB_SET_TO_FILE_NAME);
+ g_signal_connect(G_OBJECT(CddbSetToAllFields), "toggled",G_CALLBACK(Cddb_Set_To_All_Fields_Check_Button_Toggled),NULL);
+ g_signal_connect(G_OBJECT(CddbSetToAllFields), "toggled",G_CALLBACK(Cddb_Set_Apply_Button_Sensivity),NULL);
+ g_signal_connect(G_OBJECT(CddbSetToTitle), "toggled",G_CALLBACK(Cddb_Set_Apply_Button_Sensivity),NULL);
+ g_signal_connect(G_OBJECT(CddbSetToArtist), "toggled",G_CALLBACK(Cddb_Set_Apply_Button_Sensivity),NULL);
+ g_signal_connect(G_OBJECT(CddbSetToAlbum), "toggled",G_CALLBACK(Cddb_Set_Apply_Button_Sensivity),NULL);
+ g_signal_connect(G_OBJECT(CddbSetToYear), "toggled",G_CALLBACK(Cddb_Set_Apply_Button_Sensivity),NULL);
+ g_signal_connect(G_OBJECT(CddbSetToTrack), "toggled",G_CALLBACK(Cddb_Set_Apply_Button_Sensivity),NULL);
+ g_signal_connect(G_OBJECT(CddbSetToTrackTotal),"toggled",G_CALLBACK(Cddb_Set_Apply_Button_Sensivity),NULL);
+ g_signal_connect(G_OBJECT(CddbSetToGenre), "toggled",G_CALLBACK(Cddb_Set_Apply_Button_Sensivity),NULL);
+ g_signal_connect(G_OBJECT(CddbSetToFileName), "toggled",G_CALLBACK(Cddb_Set_Apply_Button_Sensivity),NULL);
+
+ hbox = gtk_hbox_new(FALSE,0);
+ gtk_box_pack_start(GTK_BOX(vbox),hbox,FALSE,FALSE,0);
+
+ // Check box to run the scanner
+ CddbRunScanner = gtk_check_button_new_with_label(_("Run the current scanner for each file"));
+ gtk_box_pack_start(GTK_BOX(hbox),CddbRunScanner,FALSE,TRUE,0);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(CddbRunScanner),CDDB_RUN_SCANNER);
+ gtk_tooltips_set_tip(Tips,CddbRunScanner,_("When activating this option, after loading the "
+ "fields, the current selected scanner will be ran (the scanner window must be opened)."),NULL);
+
+ // Check box to use DLM (also used in the preferences window)
+ CddbUseDLM2 = gtk_check_button_new_with_label(_("Match lines with the Levenshtein algorithm"));
+ gtk_box_pack_start(GTK_BOX(hbox),CddbUseDLM2,FALSE,FALSE,2);
+ // Doesn't activate it by default because if the new user don't pay attention to it,
+ // it will not understand why the cddb results aren't loaded correctly...
+ //gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(CddbUseDLM2),CDDB_USE_DLM);
+ gtk_tooltips_set_tip(Tips,CddbUseDLM2,_("When activating this option, the "
+ "Levenshtein algorithm (DLM : Damerau-Levenshtein Metric) will be used "
+ "to match the CDDB title against every file name in the current folder, "
+ "and to select the best match. This will be used when selecting the "
+ "corresponding audio file, or applying cddb results, instead of using "
+ "directly the position order."),NULL);
+ g_signal_connect(G_OBJECT(CddbUseDLM2),"toggled",G_CALLBACK(Cddb_Use_Dlm_2_Check_Button_Toggled),NULL);
+
+ // Button to apply
+ CddbApplyButton = Create_Button_With_Pixmap(BUTTON_APPLY);
+ gtk_box_pack_end(GTK_BOX(hbox),CddbApplyButton,FALSE,FALSE,2);
+ g_signal_connect(G_OBJECT(CddbApplyButton),"clicked", G_CALLBACK(Cddb_Set_Track_Infos_To_File_List),NULL);
+ gtk_tooltips_set_tip(Tips,CddbApplyButton,_("Load the selected lines or all lines (if no line selected)."),NULL);
+
+ /*
+ * Status bar
+ */
+ CddbStatusBar = gtk_statusbar_new();
+ gtk_box_pack_start(GTK_BOX(MainVBox),CddbStatusBar,FALSE,TRUE,0);
+ gtk_widget_set_size_request(CddbStatusBar, 300, -1);
+ CddbStatusBarContext = gtk_statusbar_get_context_id(GTK_STATUSBAR(CddbStatusBar),"Messages");
+ gtk_statusbar_push(GTK_STATUSBAR(CddbStatusBar),CddbStatusBarContext,_("Ready to search..."));
+
+
+ g_signal_emit_by_name(G_OBJECT(GTK_ENTRY(GTK_BIN(CddbSearchStringCombo)->child)),"changed");
+ g_signal_emit_by_name(G_OBJECT(CddbSearchInAllFields),"toggled");
+ g_signal_emit_by_name(G_OBJECT(CddbSearchInAllCategories),"toggled");
+ g_signal_emit_by_name(G_OBJECT(CddbSetToAllFields),"toggled");
+ CddbStopSearch = FALSE;
+
+ gtk_widget_show_all(CddbWindow);
+ if (SET_CDDB_WINDOW_POSITION
+ && CDDB_WINDOW_X > 0 && CDDB_WINDOW_Y > 0)
+ {
+ gdk_window_move(CddbWindow->window,CDDB_WINDOW_X,CDDB_WINDOW_Y);
+ }
+ // Force resize window
+ gtk_widget_set_size_request(GTK_WIDGET(CddbSearchInAllFields), CddbSearchInAllCategories->allocation.width, -1);
+ g_signal_emit_by_name(G_OBJECT(CddbShowCategoriesButton),"toggled");
+
+ g_signal_connect(GTK_OBJECT(CddbNoteBook),"switch-page",G_CALLBACK(Cddb_Notebook_Switch_Page),NULL);
+ //g_signal_emit_by_name(G_OBJECT(CddbNoteBook),"switch-page"); // Cause crash... => the 2 following lines to fix
+ gtk_notebook_set_current_page(GTK_NOTEBOOK(CddbNoteBook),1);
+ gtk_notebook_set_current_page(GTK_NOTEBOOK(CddbNoteBook),0);
+}
+
+gboolean Cddb_Destroy_Window (GtkWidget *widget, GdkEvent *event, gpointer data)
+{
+
+ CddbStopSearch = TRUE;
+ if (CddbWindow)
+ {
+ Cddb_Window_Apply_Changes();
+
+ // Save combobox history lists before exit
+ Save_Cddb_Search_String_List(CddbSearchStringModel, MISC_COMBO_TEXT);
+ Save_Cddb_Search_String_In_Result_List(CddbSearchStringInResultModel, MISC_COMBO_TEXT);
+
+ // FIX ME : This causes problem with memory !!
+ Cddb_Free_Album_List();
+
+ gtk_widget_destroy(CddbWindow);
+ CddbWindow = NULL;
+ CddbAlbumList = NULL;
+ CddbSearchStringCombo = NULL;
+ CddbSearchStringModel = NULL;
+ CddbAlbumListView = NULL;
+ CddbAlbumListModel = NULL;
+ CddbTrackListView = NULL;
+ CddbTrackListModel = NULL;
+ CddbApplyButton = NULL;
+ CddbSearchButton = NULL;
+ CddbSearchAutoButton = NULL;
+ }
+ return FALSE;
+}
+
+/*
+ * For the configuration file...
+ */
+void Cddb_Window_Apply_Changes (void)
+{
+ if (CddbWindow)
+ {
+ gint x, y, width, height;
+
+ if ( CddbWindow->window && gdk_window_is_visible(CddbWindow->window)
+ && gdk_window_get_state(CddbWindow->window)!=GDK_WINDOW_STATE_MAXIMIZED )
+ {
+ // Position and Origin of the window
+ gdk_window_get_root_origin(CddbWindow->window,&x,&y);
+ CDDB_WINDOW_X = x;
+ CDDB_WINDOW_Y = y;
+ gdk_window_get_size(CddbWindow->window,&width,&height);
+ CDDB_WINDOW_WIDTH = width;
+ CDDB_WINDOW_HEIGHT = height;
+
+ // Handle panes position
+ CDDB_PANE_HANDLE_POSITION = GTK_PANED(CddbWindowHPaned)->child1_size;
+ }
+
+ CDDB_SEARCH_IN_ALL_FIELDS = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CddbSearchInAllFields));
+ CDDB_SEARCH_IN_ARTIST_FIELD = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CddbSearchInArtistField));
+ CDDB_SEARCH_IN_TITLE_FIELD = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CddbSearchInTitleField));
+ CDDB_SEARCH_IN_TRACK_NAME_FIELD = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CddbSearchInTrackNameField));
+ CDDB_SEARCH_IN_OTHER_FIELD = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CddbSearchInOtherField));
+ CDDB_SHOW_CATEGORIES = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CddbShowCategoriesButton));
+
+ CDDB_SEARCH_IN_ALL_CATEGORIES = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CddbSearchInAllCategories));
+ CDDB_SEARCH_IN_BLUES_CATEGORY = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CddbSearchInBluesCategory));
+ CDDB_SEARCH_IN_CLASSICAL_CATEGORY = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CddbSearchInClassicalCategory));
+ CDDB_SEARCH_IN_COUNTRY_CATEGORY = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CddbSearchInCountryCategory));
+ CDDB_SEARCH_IN_FOLK_CATEGORY = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CddbSearchInFolkCategory));
+ CDDB_SEARCH_IN_JAZZ_CATEGORY = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CddbSearchInJazzCategory));
+ CDDB_SEARCH_IN_MISC_CATEGORY = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CddbSearchInMiscCategory));
+ CDDB_SEARCH_IN_NEWAGE_CATEGORY = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CddbSearchInNewageCategory));
+ CDDB_SEARCH_IN_REGGAE_CATEGORY = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CddbSearchInReggaeCategory));
+ CDDB_SEARCH_IN_ROCK_CATEGORY = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CddbSearchInRockCategory));
+ CDDB_SEARCH_IN_SOUNDTRACK_CATEGORY = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CddbSearchInSoundtrackCategory));
+
+ CDDB_SET_TO_ALL_FIELDS = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CddbSetToAllFields));
+ CDDB_SET_TO_TITLE = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CddbSetToTitle));
+ CDDB_SET_TO_ARTIST = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CddbSetToArtist));
+ CDDB_SET_TO_ALBUM = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CddbSetToAlbum));
+ CDDB_SET_TO_YEAR = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CddbSetToYear));
+ CDDB_SET_TO_TRACK = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CddbSetToTrack));
+ CDDB_SET_TO_TRACK_TOTAL = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CddbSetToTrackTotal));
+ CDDB_SET_TO_GENRE = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CddbSetToGenre));
+ CDDB_SET_TO_FILE_NAME = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CddbSetToFileName));
+
+ CDDB_RUN_SCANNER = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CddbRunScanner));
+ CDDB_USE_DLM = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CddbUseDLM2));
+ CDDB_USE_LOCAL_ACCESS = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CddbUseLocalAccess));
+
+ }
+}
+
+
+gboolean Cddb_Window_Key_Press (GtkWidget *window, GdkEvent *event)
+{
+ GdkEventKey *kevent;
+
+ if (event && event->type == GDK_KEY_PRESS)
+ {
+ kevent = (GdkEventKey *)event;
+ switch(kevent->keyval)
+ {
+ case GDK_Escape:
+ Cddb_Destroy_Window(window, event, NULL);
+ break;
+ }
+ }
+ return FALSE;
+}
+
+
+void Cddb_Search_In_All_Fields_Check_Button_Toggled (void)
+{
+ if (CddbSearchInAllFields)
+ {
+ gtk_widget_set_sensitive(CddbSearchInArtistField, !GTK_TOGGLE_BUTTON(CddbSearchInAllFields)->active);
+ gtk_widget_set_sensitive(CddbSearchInTitleField, !GTK_TOGGLE_BUTTON(CddbSearchInAllFields)->active);
+ gtk_widget_set_sensitive(CddbSearchInTrackNameField,!GTK_TOGGLE_BUTTON(CddbSearchInAllFields)->active);
+ gtk_widget_set_sensitive(CddbSearchInOtherField, !GTK_TOGGLE_BUTTON(CddbSearchInAllFields)->active);
+ }
+}
+void Cddb_Show_Categories_Button_Toggled (void)
+{
+ if (CddbShowCategoriesButton)
+ {
+ if (GTK_TOGGLE_BUTTON(CddbShowCategoriesButton)->active)
+ {
+ gtk_widget_show(CddbSeparatorH);
+ gtk_widget_show(CddbSearchInAllCategories);
+ gtk_widget_show(CddbSeparatorV);
+ gtk_widget_show(CddbSearchInBluesCategory);
+ gtk_widget_show(CddbSearchInClassicalCategory);
+ gtk_widget_show(CddbSearchInCountryCategory);
+ gtk_widget_show(CddbSearchInFolkCategory);
+ gtk_widget_show(CddbSearchInJazzCategory);
+ gtk_widget_show(CddbSearchInMiscCategory);
+ gtk_widget_show(CddbSearchInNewageCategory);
+ gtk_widget_show(CddbSearchInReggaeCategory);
+ gtk_widget_show(CddbSearchInRockCategory);
+ gtk_widget_show(CddbSearchInSoundtrackCategory);
+ }else
+ {
+ gtk_widget_hide(CddbSeparatorH);
+ gtk_widget_hide(CddbSearchInAllCategories);
+ gtk_widget_hide(CddbSeparatorV);
+ gtk_widget_hide(CddbSearchInBluesCategory);
+ gtk_widget_hide(CddbSearchInClassicalCategory);
+ gtk_widget_hide(CddbSearchInCountryCategory);
+ gtk_widget_hide(CddbSearchInFolkCategory);
+ gtk_widget_hide(CddbSearchInJazzCategory);
+ gtk_widget_hide(CddbSearchInMiscCategory);
+ gtk_widget_hide(CddbSearchInNewageCategory);
+ gtk_widget_hide(CddbSearchInReggaeCategory);
+ gtk_widget_hide(CddbSearchInRockCategory);
+ gtk_widget_hide(CddbSearchInSoundtrackCategory);
+ }
+ // Force the window to be redrawed
+ gtk_widget_queue_resize(CddbWindow);
+ }
+}
+void Cddb_Search_In_All_Categories_Check_Button_Toggled (void)
+{
+ if (CddbSearchInAllCategories)
+ {
+ gtk_widget_set_sensitive(CddbSearchInBluesCategory, !GTK_TOGGLE_BUTTON(CddbSearchInAllCategories)->active);
+ gtk_widget_set_sensitive(CddbSearchInClassicalCategory, !GTK_TOGGLE_BUTTON(CddbSearchInAllCategories)->active);
+ gtk_widget_set_sensitive(CddbSearchInCountryCategory, !GTK_TOGGLE_BUTTON(CddbSearchInAllCategories)->active);
+ gtk_widget_set_sensitive(CddbSearchInFolkCategory, !GTK_TOGGLE_BUTTON(CddbSearchInAllCategories)->active);
+ gtk_widget_set_sensitive(CddbSearchInJazzCategory, !GTK_TOGGLE_BUTTON(CddbSearchInAllCategories)->active);
+ gtk_widget_set_sensitive(CddbSearchInMiscCategory, !GTK_TOGGLE_BUTTON(CddbSearchInAllCategories)->active);
+ gtk_widget_set_sensitive(CddbSearchInNewageCategory, !GTK_TOGGLE_BUTTON(CddbSearchInAllCategories)->active);
+ gtk_widget_set_sensitive(CddbSearchInReggaeCategory, !GTK_TOGGLE_BUTTON(CddbSearchInAllCategories)->active);
+ gtk_widget_set_sensitive(CddbSearchInRockCategory, !GTK_TOGGLE_BUTTON(CddbSearchInAllCategories)->active);
+ gtk_widget_set_sensitive(CddbSearchInSoundtrackCategory,!GTK_TOGGLE_BUTTON(CddbSearchInAllCategories)->active);
+ }
+}
+void Cddb_Set_To_All_Fields_Check_Button_Toggled (void)
+{
+ if (CddbSetToAllFields)
+ {
+ gtk_widget_set_sensitive(CddbSetToTitle, !GTK_TOGGLE_BUTTON(CddbSetToAllFields)->active);
+ gtk_widget_set_sensitive(CddbSetToArtist, !GTK_TOGGLE_BUTTON(CddbSetToAllFields)->active);
+ gtk_widget_set_sensitive(CddbSetToAlbum, !GTK_TOGGLE_BUTTON(CddbSetToAllFields)->active);
+ gtk_widget_set_sensitive(CddbSetToYear, !GTK_TOGGLE_BUTTON(CddbSetToAllFields)->active);
+ gtk_widget_set_sensitive(CddbSetToTrack, !GTK_TOGGLE_BUTTON(CddbSetToAllFields)->active);
+ gtk_widget_set_sensitive(CddbSetToTrackTotal,!GTK_TOGGLE_BUTTON(CddbSetToAllFields)->active);
+ gtk_widget_set_sensitive(CddbSetToGenre, !GTK_TOGGLE_BUTTON(CddbSetToAllFields)->active);
+ gtk_widget_set_sensitive(CddbSetToFileName, !GTK_TOGGLE_BUTTON(CddbSetToAllFields)->active);
+ }
+}
+
+void Cddb_Set_Apply_Button_Sensivity (void)
+{
+ gboolean cddbsettoallfields, cddbsettotitle, cddbsettoartist, cddbsettoalbum,
+ cddbsettoyear, cddbsettotrack, cddbsettotracktotal, cddbsettogenre, cddbsettofilename;
+
+ // Tag fields
+ cddbsettoallfields = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CddbSetToAllFields));
+ cddbsettotitle = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CddbSetToTitle));
+ cddbsettoartist = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CddbSetToArtist));
+ cddbsettoalbum = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CddbSetToAlbum));
+ cddbsettoyear = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CddbSetToYear));
+ cddbsettotrack = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CddbSetToTrack));
+ cddbsettotracktotal = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CddbSetToTrackTotal));
+ cddbsettogenre = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CddbSetToGenre));
+ cddbsettofilename = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CddbSetToFileName));
+ if ( CddbApplyButton && gtk_tree_model_iter_n_children(GTK_TREE_MODEL(CddbTrackListModel), NULL) > 0
+ && (cddbsettoallfields || cddbsettotitle || cddbsettoartist || cddbsettoalbum || cddbsettoyear
+ || cddbsettotrack || cddbsettotracktotal || cddbsettogenre || cddbsettofilename) )
+ {
+ gtk_widget_set_sensitive(GTK_WIDGET(CddbApplyButton),TRUE);
+ } else
+ {
+ gtk_widget_set_sensitive(GTK_WIDGET(CddbApplyButton),FALSE);
+ }
+}
+
+void Cddb_Use_Dlm_2_Check_Button_Toggled (void)
+{
+ if (CddbUseDLM2)
+ {
+ CDDB_USE_DLM = GTK_TOGGLE_BUTTON(CddbUseDLM2)->active;
+ }
+}
+
+void Cddb_Set_Search_Button_Sensivity (void)
+{
+ gboolean cddbinallfields, cddbinartistfield, cddbintitlefield, cddbintracknamefield, cddbinotherfield;
+ gboolean cddbinallcategories, cddbinbluescategory, cddbinclassicalcategory, cddbincountrycategory,
+ cddbinfolkcategory, cddbinjazzcategory, cddbinmisccategory, cddbinnewagecategory,
+ cddbinreggaecategory, cddbinrockcategory, cddbinsoundtrackcategory;
+
+ // Fields
+ cddbinallfields = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CddbSearchInAllFields));
+ cddbinartistfield = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CddbSearchInArtistField));
+ cddbintitlefield = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CddbSearchInTitleField));
+ cddbintracknamefield = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CddbSearchInTrackNameField));
+ cddbinotherfield = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CddbSearchInOtherField));
+ // Categories
+ cddbinallcategories = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CddbSearchInAllCategories));
+ cddbinbluescategory = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CddbSearchInBluesCategory));
+ cddbinclassicalcategory = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CddbSearchInClassicalCategory));
+ cddbincountrycategory = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CddbSearchInCountryCategory));
+ cddbinfolkcategory = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CddbSearchInFolkCategory));
+ cddbinjazzcategory = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CddbSearchInJazzCategory));
+ cddbinmisccategory = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CddbSearchInMiscCategory));
+ cddbinnewagecategory = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CddbSearchInNewageCategory));
+ cddbinreggaecategory = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CddbSearchInReggaeCategory));
+ cddbinrockcategory = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CddbSearchInRockCategory));
+ cddbinsoundtrackcategory = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CddbSearchInSoundtrackCategory));
+
+ if ( CddbSearchButton && CddbSearchStringCombo && g_utf8_strlen(gtk_entry_get_text(GTK_ENTRY(GTK_BIN(CddbSearchStringCombo)->child)), -1) > 0
+ && (cddbinallfields || cddbinartistfield || cddbintitlefield || cddbintracknamefield || cddbinotherfield)
+ && (cddbinallcategories || cddbinbluescategory || cddbinclassicalcategory || cddbincountrycategory
+ || cddbinfolkcategory || cddbinjazzcategory || cddbinmisccategory || cddbinnewagecategory
+ || cddbinreggaecategory || cddbinrockcategory || cddbinsoundtrackcategory) )
+ {
+ gtk_widget_set_sensitive(GTK_WIDGET(CddbSearchButton),TRUE);
+ } else
+ {
+ gtk_widget_set_sensitive(GTK_WIDGET(CddbSearchButton),FALSE);
+ }
+}
+
+void Cddb_Stop_Search (void)
+{
+ CddbStopSearch = TRUE;
+}
+
+void Cddb_Notebook_Switch_Page (GtkNotebook *notebook, GtkNotebookPage *page, guint page_num, gpointer user_data)
+{
+ gint page_total;
+ guint page_tmp;
+
+ // For size reasons, we display children of the current tab, and hide those
+ // of others tabs => better display of notebook
+ page_total = gtk_notebook_get_n_pages(GTK_NOTEBOOK(notebook));
+ for (page_tmp = 0; page_tmp < page_total; page_tmp++)
+ {
+ GtkWidget *frame = gtk_notebook_get_nth_page(GTK_NOTEBOOK(notebook), page_tmp); // Child of the page
+ if (frame)
+ {
+ GtkWidget *box = GTK_BIN(frame)->child;
+ if (box)
+ {
+ if (page_tmp == page_num)
+ {
+ // Display children of page_tmp
+ gtk_widget_show(GTK_WIDGET(box));
+ }else
+ {
+ // Hide children of page_tmp
+ gtk_widget_hide(GTK_WIDGET(box));
+ }
+ }
+ }
+ }
+}
+
+
+/*
+ * Searches the Cddb Album List for specific terms
+ * (this is not search the remote CDDB database...)
+ */
+void Cddb_Search_String_In_Result (GtkWidget *entry, GtkButton *button)
+{
+ gchar *string;
+ gchar buffer[256];
+ gchar *pbuffer;
+ gchar *text;
+ gchar *temp;
+ gint i;
+ gint *indices = NULL;
+ gint toloop;
+ gint rowcount;
+ GtkTreeSelection* treeSelection;
+ GtkTreeIter iter;
+ GtkTreePath *rowpath;
+ gboolean result;
+ gboolean itemselected = FALSE;
+ GtkTreeIter itercopy = iter;
+
+ if (!CddbWindow || !CddbAlbumListView)
+ return;
+
+ if (!entry || !button)
+ return;
+
+ string = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
+ if (!string || strlen(string)==0)
+ return;
+ temp = g_utf8_strdown(string, -1);
+ g_free(string);
+ string = temp;
+
+ Add_String_To_Combo_List(CddbSearchStringInResultModel, string);
+
+ /* Get the currently selected row into &iter and set itemselected to reflect this */
+ treeSelection = gtk_tree_view_get_selection(GTK_TREE_VIEW(CddbAlbumListView));
+ if (gtk_tree_selection_get_selected(treeSelection, NULL, &iter) == TRUE)
+ itemselected = TRUE;
+
+ rowcount = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(CddbAlbumListModel), NULL);
+
+ if (button != GTK_BUTTON(CddbSearchStringInResultPrevButton)) /* Next result button has been clicked */
+ {
+ /* Search in the album list (from top to bottom) */
+ if (itemselected == TRUE)
+ {
+ gtk_tree_selection_unselect_iter(treeSelection, &iter);
+ result = gtk_tree_model_iter_next(GTK_TREE_MODEL(CddbAlbumListModel), &iter);
+ } else
+ {
+ result = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(CddbAlbumListModel), &iter);
+ }
+
+ itercopy = iter;
+
+ /* If list entries follow the previously selected item, loop through them looking for a match */
+ if(result == TRUE)
+ {
+ do /* Search following results */
+ {
+ gtk_tree_model_get(GTK_TREE_MODEL(CddbAlbumListModel), &iter, CDDB_ALBUM_LIST_ALBUM, &text, -1);
+ g_utf8_strncpy(buffer, text, 256);
+
+ temp = g_utf8_strdown(buffer, -1);
+ pbuffer = temp;
+
+ if (pbuffer && strstr(pbuffer, string) != NULL)
+ {
+ gtk_tree_selection_select_iter(treeSelection, &iter);
+ rowpath = gtk_tree_model_get_path(GTK_TREE_MODEL(CddbAlbumListModel), &iter);
+ gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(CddbAlbumListView), rowpath, NULL, FALSE, 0, 0);
+ gtk_tree_path_free(rowpath);
+ g_free(text);
+ g_free(temp);
+ g_free(string);
+ return;
+ }
+ g_free(temp);
+ g_free(text);
+ } while(gtk_tree_model_iter_next(GTK_TREE_MODEL(CddbAlbumListModel), &iter));
+ }
+
+ /* If no results have been found, start the search again from the beginning */
+ /* If we have had an item selected, we need to stop at that one to avoid re-searching the same entries */
+ if(itemselected == TRUE)
+ {
+ rowpath = gtk_tree_model_get_path(GTK_TREE_MODEL(CddbAlbumListModel), &itercopy);
+ if (rowpath)
+ indices = gtk_tree_path_get_indices(rowpath);
+ gtk_tree_path_free(rowpath);
+ toloop = indices[0];
+ } else
+ {
+ toloop = rowcount;
+ }
+
+ for (i = 0; i < rowcount; i++)
+ {
+ gboolean found;
+
+ if (i == 0)
+ found = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(CddbAlbumListModel), &itercopy);
+ else
+ found = gtk_tree_model_iter_next(GTK_TREE_MODEL(CddbAlbumListModel), &itercopy);
+
+ if (found)
+ {
+ gtk_tree_model_get(GTK_TREE_MODEL(CddbAlbumListModel), &itercopy, CDDB_ALBUM_LIST_ALBUM, &text, -1);
+ g_utf8_strncpy(buffer, text, 256);
+
+ temp = g_utf8_strdown(buffer, -1);
+ pbuffer = temp;
+
+ if (pbuffer && strstr(pbuffer,string) != NULL)
+ {
+ gtk_tree_selection_select_iter(treeSelection, &itercopy);
+ rowpath = gtk_tree_model_get_path(GTK_TREE_MODEL(CddbAlbumListModel), &itercopy);
+ gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(CddbAlbumListView), rowpath, NULL, FALSE, 0, 0);
+ gtk_tree_path_free(rowpath);
+ g_free(text);
+ g_free(temp);
+ g_free(string);
+ return;
+ }
+ g_free(temp);
+ g_free(text);
+ }
+ }
+ } else
+ {
+ /* Previous result button */
+
+ /* Search in the album list (from bottom/selected-item to top) */
+ if (itemselected == TRUE)
+ {
+ rowpath = gtk_tree_model_get_path(GTK_TREE_MODEL(CddbAlbumListModel), &iter);
+ gtk_tree_path_prev(rowpath);
+ } else
+ {
+ rowpath = gtk_tree_path_new_from_indices(gtk_tree_model_iter_n_children(GTK_TREE_MODEL(CddbAlbumListModel), NULL) - 1, -1);
+ }
+
+ do
+ {
+ gboolean found;
+
+ found = gtk_tree_model_get_iter(GTK_TREE_MODEL(CddbAlbumListModel), &iter, rowpath);
+ if (found)
+ {
+ gtk_tree_model_get(GTK_TREE_MODEL(CddbAlbumListModel), &iter, CDDB_ALBUM_LIST_ALBUM, &text, -1);
+ g_utf8_strncpy(buffer,text,256);
+ temp = g_utf8_strdown(buffer, -1);
+ pbuffer = temp;
+
+ if (pbuffer && strstr(pbuffer,string) != NULL)
+ {
+ gtk_tree_selection_select_iter(treeSelection, &iter);
+ gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(CddbAlbumListView), rowpath, NULL, FALSE, 0, 0);
+ gtk_tree_path_free(rowpath);
+ g_free(text);
+ g_free(temp);
+ g_free(string);
+ return;
+ }
+ g_free(temp);
+ g_free(text);
+ }
+ } while(gtk_tree_path_prev(rowpath));
+ gtk_tree_path_free(rowpath);
+ }
+ g_free(string);
+}
+
+
+/*
+ * Show collected infos of the album in the status bar
+ */
+void Cddb_Show_Album_Info (GtkTreeSelection *selection, gpointer data)
+{
+ CddbAlbum *cddbalbum = NULL;
+ gchar *msg, *duration_str;
+ GtkTreeIter row;
+
+ if (!CddbWindow)
+ return;
+
+ if (gtk_tree_selection_get_selected(selection, NULL, &row))
+ {
+ gtk_tree_model_get(GTK_TREE_MODEL(CddbAlbumListModel), &row, CDDB_ALBUM_LIST_DATA, &cddbalbum, -1);
+ }
+ if (!cddbalbum)
+ return;
+
+ duration_str = Convert_Duration((gulong)cddbalbum->duration);
+ msg = g_strdup_printf(_("Album: '%s', "
+ "artist: '%s', "
+ "length: '%s', "
+ "year: '%s', "
+ "genre: '%s', "
+ "ID: '%s'"),
+ cddbalbum->album ? cddbalbum->album : "",
+ cddbalbum->artist ? cddbalbum->artist : "",
+ duration_str,
+ cddbalbum->year ? cddbalbum->year : "",
+ cddbalbum->genre ? cddbalbum->genre : "",
+ cddbalbum->id ? cddbalbum->id : "");
+ gtk_statusbar_push(GTK_STATUSBAR(CddbStatusBar), CddbStatusBarContext, msg);
+ g_free(msg);
+ g_free(duration_str);
+}
+
+
+/*
+ * Select the corresponding file into the main file list
+ */
+void Cddb_Track_List_Row_Selected (GtkTreeSelection *selection, gpointer data)
+{
+ GList *selectedRows;
+ GtkTreeIter currentFile;
+ gchar *text_path;
+ ET_File **etfile;
+
+ // Exit if we don't have to select files in the main list
+ if (!CDDB_FOLLOW_FILE)
+ return;
+
+ selectedRows = gtk_tree_selection_get_selected_rows(selection, NULL);
+
+ // We might be called with no rows selected
+ if (g_list_length(selectedRows) == 0)
+ {
+ g_list_foreach(selectedRows, (GFunc) gtk_tree_path_free, NULL);
+ g_list_free(selectedRows);
+ return;
+ }
+
+ // Unselect files in the main list before re-selecting them...
+ Browser_List_Unselect_All_Files();
+
+ while (selectedRows)
+ {
+ gboolean found;
+
+ found = gtk_tree_model_get_iter(GTK_TREE_MODEL(CddbTrackListModel), &currentFile, (GtkTreePath*)selectedRows->data);
+ if (found)
+ {
+ if (CDDB_USE_DLM)
+ {
+ gtk_tree_model_get(GTK_TREE_MODEL(CddbTrackListModel), &currentFile,
+ CDDB_TRACK_LIST_NAME, &text_path,
+ CDDB_TRACK_LIST_ETFILE, &etfile, -1);
+ *etfile = Browser_List_Select_File_By_DLM((const gchar*) text_path, TRUE);
+ } else
+ {
+ text_path = gtk_tree_model_get_string_from_iter(GTK_TREE_MODEL(CddbTrackListModel), &currentFile);
+ Browser_List_Select_File_By_Iter_String((const gchar*) text_path, TRUE);
+ }
+ g_free(text_path);
+ }
+
+ if (!selectedRows->next) break;
+ selectedRows = selectedRows->next;
+ }
+
+ g_list_foreach(selectedRows, (GFunc) gtk_tree_path_free, NULL);
+ g_list_free(selectedRows);
+}
+
+/*
+ * Unselect all rows in the track list
+ */
+void Cddb_Track_List_Unselect_All ()
+{
+ GtkTreeSelection *selection;
+
+ if (!CddbTrackListView) return;
+
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(CddbTrackListView));
+ if (selection)
+ {
+ gtk_tree_selection_unselect_all(selection);
+ }
+}
+
+/*
+ * Select all rows in the track list
+ */
+void Cddb_Track_List_Select_All ()
+{
+ GtkTreeSelection *selection;
+
+ if (!CddbTrackListView) return;
+
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(CddbTrackListView));
+ if (selection)
+ {
+ gtk_tree_selection_select_all(selection);
+ }
+}
+
+/*
+ * Invert the selection of every row in the track list
+ */
+void Cddb_Track_List_Invert_Selection ()
+{
+ GtkTreeSelection *selection;
+ GtkTreeIter iter;
+ gboolean valid;
+
+ if (!CddbTrackListView) return;
+
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(CddbTrackListView));
+
+ if (selection)
+ {
+ /* Must block the select signal to avoid selecting all files (one by one) in the main list */
+ g_signal_handlers_block_by_func(G_OBJECT(selection), G_CALLBACK(Cddb_Track_List_Row_Selected), NULL);
+
+ valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(CddbTrackListModel), &iter);
+ while (valid)
+ {
+ if (gtk_tree_selection_iter_is_selected(selection, &iter))
+ {
+ gtk_tree_selection_unselect_iter(selection, &iter);
+ } else
+ {
+ gtk_tree_selection_select_iter(selection, &iter);
+ }
+ valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(CddbTrackListModel), &iter);
+ }
+ g_signal_handlers_unblock_by_func(G_OBJECT(selection), G_CALLBACK(Cddb_Track_List_Row_Selected), NULL);
+ g_signal_emit_by_name(G_OBJECT(gtk_tree_view_get_selection(GTK_TREE_VIEW(CddbTrackListView))), "changed");
+ }
+}
+
+gboolean Cddb_Track_List_Button_Press (GtkTreeView *treeView, GdkEventButton *event)
+{
+ if (!event)
+ return FALSE;
+
+ if (event->type==GDK_2BUTTON_PRESS && event->button==1)
+ {
+ /* Double left mouse click */
+ Cddb_Track_List_Select_All();
+ }
+ return FALSE;
+}
+
+
+/*
+ * Cddb_Popup_Menu_Handler : displays the corresponding menu
+ * Create_Browser_Tree_Popup_Menu: Create a popup menu for the tree browser
+ * Create_Browser_List_Popup_Menu: Create a popup menu for the list of files of browser
+ */
+gboolean Cddb_Popup_Menu_Handler (GtkMenu *menu, GdkEventButton *event)
+{
+ if (event && (event->type==GDK_BUTTON_PRESS) && (event->button==3))
+ {
+ gtk_menu_popup(menu,NULL,NULL,NULL,NULL,event->button,event->time);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+GtkWidget *Create_Cddb_Track_List_Popup_Menu(GtkWidget *list)
+{
+ GtkWidget *CddbPopupMenu;
+ GtkWidget *MenuItem;
+
+
+ CddbPopupMenu = gtk_menu_new();
+ g_signal_connect_swapped(G_OBJECT(list), "button_press_event",
+ G_CALLBACK(Cddb_Popup_Menu_Handler), G_OBJECT(CddbPopupMenu));
+
+ MenuItem = gtk_menu_item_new_with_label(_("Select all lines"));
+ gtk_menu_shell_append(GTK_MENU_SHELL(CddbPopupMenu), MenuItem);
+ g_signal_connect(G_OBJECT(MenuItem),"activate", G_CALLBACK(Cddb_Track_List_Select_All),NULL);
+
+ MenuItem = gtk_menu_item_new_with_label(_("Unselect all lines"));
+ gtk_menu_shell_append(GTK_MENU_SHELL(CddbPopupMenu), MenuItem);
+ g_signal_connect(G_OBJECT(MenuItem),"activate", G_CALLBACK(Cddb_Track_List_Unselect_All),NULL);
+
+ MenuItem = gtk_menu_item_new_with_label(_("Invert selection"));
+ gtk_menu_shell_append(GTK_MENU_SHELL(CddbPopupMenu), MenuItem);
+ g_signal_connect(G_OBJECT(MenuItem),"activate", G_CALLBACK(Cddb_Track_List_Invert_Selection),NULL);
+
+ MenuItem = gtk_menu_item_new();
+ gtk_menu_shell_append(GTK_MENU_SHELL(CddbPopupMenu), MenuItem);
+
+ MenuItem = gtk_menu_item_new_with_label(_("Sort by Track Number"));
+ gtk_menu_shell_append(GTK_MENU_SHELL(CddbPopupMenu),MenuItem);
+ g_signal_connect(G_OBJECT(MenuItem),"activate",G_CALLBACK(Cddb_Track_List_Sort_By_Ascending_Track_Number),NULL);
+
+ MenuItem = gtk_menu_item_new_with_label(_("Sort by Track Name"));
+ gtk_menu_shell_append(GTK_MENU_SHELL(CddbPopupMenu),MenuItem);
+ g_signal_connect(G_OBJECT(MenuItem),"activate",G_CALLBACK(Cddb_Track_List_Sort_By_Ascending_Track_Name),NULL);
+
+ gtk_widget_show_all(CddbPopupMenu);
+ return CddbPopupMenu;
+}
+
+/*
+ * To run an "automatic search" from a popup menu with the sélected files
+ */
+void Cddb_Popup_Menu_Search_Selected_File (void)
+{
+ Open_Cddb_Window();
+ Cddb_Search_Album_From_Selected_Files();
+}
+
+/*
+ * Sort the track list
+ */
+gint Cddb_Track_List_Sort_Func (GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer data)
+{
+ gint sortcol = GPOINTER_TO_INT(data);
+ gchar *text1, *text1cp;
+ gchar *text2, *text2cp;
+ gint num1;
+ gint num2;
+ gint ret = 0;
+
+ switch (sortcol)
+ {
+ case SORT_LIST_NUMBER:
+ gtk_tree_model_get(model, a, CDDB_TRACK_LIST_NUMBER, &num1, -1);
+ gtk_tree_model_get(model, b, CDDB_TRACK_LIST_NUMBER, &num2, -1);
+ if (num1 < num2)
+ return -1;
+ else if(num1 > num2)
+ return 1;
+ else
+ return 0;
+ break;
+
+ case SORT_LIST_NAME:
+ gtk_tree_model_get(model, a, CDDB_TRACK_LIST_NAME, &text1, -1);
+ gtk_tree_model_get(model, b, CDDB_TRACK_LIST_NAME, &text2, -1);
+ text1cp = g_utf8_collate_key_for_filename(text1, -1);
+ text2cp = g_utf8_collate_key_for_filename(text2, -1);
+ // Must be the same rules as "ET_Comp_Func_Sort_File_By_Ascending_Filename" to be
+ // able to sort in the same order files in cddb and in the file list.
+ ret = SORTING_FILE_CASE_SENSITIVE ? strcmp(text1cp,text2cp) : strcasecmp(text1cp,text2cp);
+
+ g_free(text1);
+ g_free(text2);
+ g_free(text1cp);
+ g_free(text2cp);
+ break;
+ }
+
+ return ret;
+}
+
+void Cddb_Track_List_Sort_By_Ascending_Track_Number (void)
+{
+ // Sort ascending by column '#'
+ gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(CddbTrackListModel), SORT_LIST_NUMBER, GTK_SORT_ASCENDING);
+}
+
+void Cddb_Track_List_Sort_By_Ascending_Track_Name (void)
+{
+ // Sort ascending by column 'Track Name'
+ gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(CddbTrackListModel), SORT_LIST_NAME, GTK_SORT_ASCENDING);
+}
+
+
+
+/*
+ * Open a connection to "server_name" and retun the socket_id
+ * On error, returns 0.
+ *
+ * Some help on : http://shoe.bocks.com/net/
+ * http://www.zone-h.org/files/4/socket.txt
+ */
+gint Cddb_Open_Connection (gchar *host, gint port)
+{
+ gint socket_id = 0;
+ struct hostent *hostent;
+ struct sockaddr_in sockaddr;
+ gint optval = 1;
+ gchar *msg;
+
+
+ if (!CddbWindow)
+ return 0;
+
+ if (!host || port <= 0)
+ return 0;
+
+ msg = g_strdup_printf(_("Resolving host '%s' ..."),host);
+ gtk_statusbar_push(GTK_STATUSBAR(CddbStatusBar),CddbStatusBarContext,msg);
+ g_free(msg);
+ while (gtk_events_pending())
+ gtk_main_iteration();
+
+ if ( (hostent=gethostbyname((const gchar*)host)) == NULL )
+ {
+ msg = g_strdup_printf(_("Can't resolve host '%s' (%s)!"),host,g_strerror(errno));
+ gtk_statusbar_push(GTK_STATUSBAR(CddbStatusBar),CddbStatusBarContext,msg);
+ Log_Print("%s",msg);
+ g_free(msg);
+ return 0;
+ }
+
+ memset((void *)&sockaddr,0,sizeof(sockaddr)); // Initialize with zero
+ memcpy(&sockaddr.sin_addr.s_addr,*(hostent->h_addr_list),sizeof(sockaddr.sin_addr.s_addr));
+ sockaddr.sin_family = AF_INET;
+ sockaddr.sin_port = htons(port);
+
+ // Create socket
+ if( (socket_id = socket(AF_INET,SOCK_STREAM,0)) < 0 )
+ {
+ msg = g_strdup_printf(_("Can't create a new socket (%s)!"),g_strerror(errno));
+ gtk_statusbar_push(GTK_STATUSBAR(CddbStatusBar),CddbStatusBarContext,msg);
+ Log_Print("%s",msg);
+ g_free(msg);
+ return 0;
+ }
+
+ // FIX ME : must catch SIGPIPE?
+ if ( setsockopt(socket_id,SOL_SOCKET,SO_KEEPALIVE,(gchar *)&optval,sizeof(optval)) < 0 )
+ {
+ Log_Print("Can't set option of the new created socket!");
+ }
+
+ // Open connection to the server
+ msg = g_strdup_printf(_("Connecting to host '%s', port '%d' ..."),host,port);
+ gtk_statusbar_push(GTK_STATUSBAR(CddbStatusBar),CddbStatusBarContext,msg);
+ g_free(msg);
+ while (gtk_events_pending())
+ gtk_main_iteration();
+ if ( connect(socket_id,(struct sockaddr *)&sockaddr,sizeof(struct sockaddr_in)) < 0 )
+ {
+ msg = g_strdup_printf(_("Can't connect to host '%s' (%s)!"),host,g_strerror(errno));
+ gtk_statusbar_push(GTK_STATUSBAR(CddbStatusBar),CddbStatusBarContext,msg);
+ Log_Print("%s",msg);
+ g_free(msg);
+ return 0;
+ }
+ msg = g_strdup_printf(_("Connected to host '%s'"),host);
+ gtk_statusbar_push(GTK_STATUSBAR(CddbStatusBar),CddbStatusBarContext,msg);
+ g_free(msg);
+ while (gtk_events_pending())
+ gtk_main_iteration();
+
+ return socket_id;
+}
+
+
+/*
+ * Close the connection correcponding to the socket_id
+ */
+void Cddb_Close_Connection (gint socket_id)
+{
+#ifndef WIN32
+ shutdown(socket_id,SHUT_RDWR);
+#endif
+ close(socket_id);
+
+ if (!CddbWindow)
+ return;
+
+ socket_id = 0;
+ CddbStopSearch = FALSE;
+}
+
+
+/*
+ * Read the result of the request and write it into a file.
+ * And return the number of bytes read.
+ * - bytes_read=0 => no more data.
+ * - bytes_read_total : use to add bytes of severals pages... must be initialized before
+ *
+ * Server answser is formated like this :
+ *
+ * HTTP/1.1 200 OK\r\n }
+ * Server: Apache/1.3.19 (Unix) PHP/4.0.4pl1\r\n } "Header"
+ * Connection: close\r\n }
+ * \r\n
+ * <html>\n }
+ * [...] } "Body"
+ */
+gint Cddb_Write_Result_To_File (gint socket_id, gulong *bytes_read_total)
+{
+ gchar *home_path = NULL;
+ gchar *file_path = NULL;
+ FILE *file;
+
+
+ /* The file to write */
+ if (!HOME_VARIABLE)
+ return FALSE;
+
+ home_path = g_strconcat(HOME_VARIABLE,
+ HOME_VARIABLE[strlen(HOME_VARIABLE)-1]!=G_DIR_SEPARATOR?G_DIR_SEPARATOR_S:"",
+ NULL);
+
+
+ file_path = g_strconcat(home_path,CDDB_RESULT_FILE,NULL);
+ if ( (file=fopen(file_path,"w+")) != NULL )
+ {
+ gchar cddb_out[MAX_STRING_LEN+1];
+ gint bytes_read = 0;
+
+ while ( CddbWindow && !CddbStopSearch
+ // Read data
+ && (bytes_read = recv(socket_id,(void *)&cddb_out,MAX_STRING_LEN,0)) > 0 )
+ {
+ gchar *size_str;
+ gchar *msg;
+
+
+ // Write to file
+ cddb_out[bytes_read] = 0;
+ fwrite(&cddb_out,bytes_read,1,file);
+
+ *bytes_read_total += bytes_read;
+
+ //g_print("\nLine : %lu : %s\n",bytes_read,cddb_out);
+
+ // Display message
+ size_str = Convert_Size_1(*bytes_read_total);
+ msg = g_strdup_printf(_("Receiving data (%s) ..."),size_str);
+ gtk_statusbar_push(GTK_STATUSBAR(CddbStatusBar),CddbStatusBarContext,msg);
+ g_free(msg);
+ g_free(size_str);
+ while (gtk_events_pending())
+ gtk_main_iteration();
+ }
+
+ if (bytes_read < 0)
+ {
+ Log_Print(_("Error when reading cddb response (%s)!"),g_strerror(errno));
+ return -1; // Error!
+ }
+
+ fclose(file);
+ } else
+ {
+ Log_Print(_("Can't create file '%s' (%s)"),file_path,g_strerror(errno));
+ }
+ g_free(file_path);
+ g_free(home_path);
+
+
+ return 0;
+}
+
+
+/*
+ * Read one line (of the connection) into cddb_out.
+ * return : -1 on error
+ * 0 if no more line to read (EOF)
+ * 1 if more lines to read
+ *
+ * Server answser is formated like this :
+ *
+ * HTTP/1.1 200 OK\r\n }
+ * Server: Apache/1.3.19 (Unix) PHP/4.0.4pl1\r\n } "Header"
+ * Connection: close\r\n }
+ * \r\n
+ * <html>\n }
+ * [...] } "Body"
+ */
+gint Cddb_Read_Line (FILE **file, gchar **cddb_out)
+{
+ gchar buffer[MAX_STRING_LEN];
+
+
+ if (*file == NULL)
+ {
+ gchar *home_path;
+ gchar *file_path;
+
+ home_path = g_strconcat(HOME_VARIABLE,
+ HOME_VARIABLE[strlen(HOME_VARIABLE)-1]!=G_DIR_SEPARATOR?G_DIR_SEPARATOR_S:"",
+ NULL);
+ file_path = g_strconcat(home_path,CDDB_RESULT_FILE,NULL);
+ g_free(home_path);
+
+ if ( (*file=fopen(file_path,"r"))==0 )
+ {
+ Log_Print(_("Can't open file '%s' (%s)"),file_path,g_strerror(errno));
+ g_free(file_path);
+ return -1; // Error!
+ }
+ g_free(file_path);
+ }
+
+ if (fgets(buffer,sizeof(buffer),*file))
+ {
+ if (buffer && strlen(buffer)>0 && buffer[strlen(buffer)-1]=='\n')
+ buffer[strlen(buffer)-1]='\0';
+ while (buffer && strlen(buffer)>0 && buffer[strlen(buffer)-1]=='\r') // Severals chars '\r' may be present
+ buffer[strlen(buffer)-1]='\0';
+ *cddb_out = g_strdup(buffer);
+ }else
+ {
+ // On error or EOF
+ fclose(*file);
+
+ //*cddb_out = NULL;
+ *cddb_out = g_strdup(""); // To avoid a crash
+
+ return 0;
+ }
+
+ //g_print("Line read: %s\n",*cddb_out);
+ return 1;
+}
+
+
+/*
+ * Read HTTP header data : from "HTTP/1.1 200 OK" to the blank line
+ */
+gint Cddb_Read_Http_Header (FILE **file, gchar **cddb_out)
+{
+
+ if ( Cddb_Read_Line(file,cddb_out) < 0 )
+ return -1; // Error!
+
+ // First line must be : "HTTP/1.1 200 OK"
+ if ( !*cddb_out || strncmp("HTTP",*cddb_out,4)!=0 || strstr(*cddb_out,"200 OK")==NULL )
+ return -1;
+
+ // Read until end of the http header up to the next blank line
+ while ( Cddb_Read_Line(file,cddb_out) > 0 && *cddb_out && strlen(*cddb_out) > 0 )
+ {
+ }
+
+ //g_print("Http Header : %s\n",*cddb_out);
+ return 1;
+}
+
+/*
+ * Read CDDB header data when requesting a file (cmd=cddb+read+<album genre>+<discid>)
+ * Must be read after the HTTP header :
+ *
+ * HTTP/1.1 200 OK
+ * Date: Sun, 26 Nov 2006 22:37:13 GMT
+ * Server: Apache/2.0.54 (Debian GNU/Linux) mod_python/3.1.3 Python/2.3.5 PHP/4.3.10-16 proxy_html/2.4 mod_perl/1.999.21 Perl/v5.8.4
+ * Expires: Sun Nov 26 23:37:14 2006
+ * Content-Length: 1013
+ * Connection: close
+ * Content-Type: text/plain; charset=UTF-8
+ *
+ * 210 newage 710ed208 CD database entry follows (until terminating `.')
+ *
+ * Cddb Header is the line like this :
+ * 210 newage 710ed208 CD database entry follows (until terminating `.')
+ */
+gint Cddb_Read_Cddb_Header (FILE **file, gchar **cddb_out)
+{
+ if ( Cddb_Read_Line(file,cddb_out) < 0 )
+ return -1; // Error!
+
+ // Some requests receive some strange data (arbitrary : less than 10 chars.)
+ // at the beginning (2 or 3 characters)... So we read one line more...
+ if ( !*cddb_out || strlen(*cddb_out) < 10 )
+ if ( Cddb_Read_Line(file,cddb_out) < 0 )
+ return -1; // Error!
+
+ //g_print("Cddb Header : %s\n",*cddb_out);
+
+ // Read the line
+ // 200 - exact match
+ // 210 - multiple exact matches
+ // 211 - inexact match
+ if ( *cddb_out == NULL
+ || (strncmp(*cddb_out,"200",3)!=0
+ && strncmp(*cddb_out,"210",3)!=0
+ && strncmp(*cddb_out,"211",3)!=0) )
+ return -1;
+
+ return 1;
+}
+
+
+
+/*
+ * Free the CddbAlbumList
+ */
+gboolean Cddb_Free_Album_List (void)
+{
+ if (!CddbAlbumList) return FALSE;
+
+ CddbAlbumList = g_list_last(CddbAlbumList);
+ while (CddbAlbumList)
+ {
+ CddbAlbum *cddbalbum = CddbAlbumList->data;
+
+ if (cddbalbum)
+ {
+ g_free(cddbalbum->server_name);
+ g_object_unref(cddbalbum->bitmap);
+
+ g_free(cddbalbum->artist_album);
+ g_free(cddbalbum->category);
+ g_free(cddbalbum->id);
+ Cddb_Free_Track_Album_List(cddbalbum->track_list);
+
+ g_free(cddbalbum->artist);
+ g_free(cddbalbum->album);
+ g_free(cddbalbum->genre);
+ g_free(cddbalbum->year);
+
+ g_free(cddbalbum);
+ cddbalbum = (CddbAlbum *)NULL;
+ }
+ if (!CddbAlbumList->prev) break;
+ CddbAlbumList = CddbAlbumList->prev;
+ }
+
+ g_list_free(CddbAlbumList);
+ CddbAlbumList = (GList *)NULL;
+ return TRUE;
+}
+
+gboolean Cddb_Free_Track_Album_List (GList *track_list)
+{
+ GList *CddbTrackAlbumList;
+
+ if (!track_list) return FALSE;
+
+ CddbTrackAlbumList = g_list_last(track_list);
+ while (CddbTrackAlbumList)
+ {
+ CddbTrackAlbum *cddbtrackalbum = CddbTrackAlbumList->data;
+ if (cddbtrackalbum)
+ {
+ g_free(cddbtrackalbum->track_name);
+ g_free(cddbtrackalbum);
+ cddbtrackalbum = (CddbTrackAlbum *)NULL;
+ }
+ if (!CddbTrackAlbumList->prev) break;
+ CddbTrackAlbumList = CddbTrackAlbumList->prev;
+ }
+ g_list_free(CddbTrackAlbumList);
+ CddbTrackAlbumList = (GList *)NULL;
+ return TRUE;
+}
+
+
+
+/*
+ * Load the CddbAlbumList into the corresponding List
+ */
+void Cddb_Load_Album_List (gboolean only_red_lines)
+{
+ if (CddbWindow && CddbAlbumList && CddbAlbumListView)
+ {
+ GtkTreeIter iter;
+ GList *cddbalbumlist;
+
+ GtkTreeSelection *selection;
+ GList *selectedRows = NULL;
+ GtkTreeIter currentIter;
+ CddbAlbum *cddbalbumSelected = NULL;
+
+ // Memorize the current selected item
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(CddbAlbumListView));
+ selectedRows = gtk_tree_selection_get_selected_rows(selection, NULL);
+ if (selectedRows)
+ {
+ if (gtk_tree_model_get_iter(GTK_TREE_MODEL(CddbAlbumListModel), &currentIter, (GtkTreePath*)selectedRows->data))
+ gtk_tree_model_get(GTK_TREE_MODEL(CddbAlbumListModel), &currentIter, CDDB_ALBUM_LIST_DATA, &cddbalbumSelected, -1);
+ }
+
+ // Remove lines
+ gtk_list_store_clear(CddbAlbumListModel);
+
+ // Reload list following parameter 'only_red_lines'
+ cddbalbumlist = g_list_first(CddbAlbumList);
+ while (cddbalbumlist)
+ {
+ CddbAlbum *cddbalbum = cddbalbumlist->data;
+
+ if ( (only_red_lines && cddbalbum->track_list) || !only_red_lines)
+ {
+ // Load the row in the list
+ gtk_list_store_append(CddbAlbumListModel, &iter);
+ gtk_list_store_set(CddbAlbumListModel, &iter,
+ CDDB_ALBUM_LIST_BITMAP, cddbalbum->bitmap,
+ CDDB_ALBUM_LIST_ALBUM, cddbalbum->artist_album,
+ CDDB_ALBUM_LIST_CATEGORY, cddbalbum->category,
+ CDDB_ALBUM_LIST_DATA, cddbalbum, -1);
+
+ Cddb_Album_List_Set_Row_Appearance(&iter);
+
+ // Select this item if it is the saved one...
+ if (cddbalbum == cddbalbumSelected)
+ gtk_tree_selection_select_iter(gtk_tree_view_get_selection(GTK_TREE_VIEW(CddbAlbumListView)), &iter);
+ }
+ cddbalbumlist = cddbalbumlist->next;
+ }
+ }
+}
+
+
+/*
+ * Load the CddbTrackList into the corresponding List
+ */
+void Cddb_Load_Track_Album_List (GList *track_list)
+{
+ GtkTreeIter iter;
+
+ if (CddbWindow && track_list && CddbTrackListView)
+ {
+ GList *tracklist = g_list_first(track_list);
+
+ // Must block the select signal of the target to avoid looping
+ gtk_list_store_clear(CddbTrackListModel);
+ while (tracklist)
+ {
+ gchar *row_text[1];
+ CddbTrackAlbum *cddbtrackalbum = tracklist->data;
+ ET_File **etfile;
+ etfile = g_malloc0(sizeof(ET_File *));
+
+ row_text[0] = Convert_Duration((gulong)cddbtrackalbum->duration);
+
+ // Load the row in the list
+ gtk_list_store_append(CddbTrackListModel, &iter);
+ gtk_list_store_set(CddbTrackListModel, &iter,
+ CDDB_TRACK_LIST_NUMBER, cddbtrackalbum->track_number,
+ CDDB_TRACK_LIST_NAME, cddbtrackalbum->track_name,
+ CDDB_TRACK_LIST_TIME, row_text[0],
+ CDDB_TRACK_LIST_DATA, cddbtrackalbum,
+ CDDB_TRACK_LIST_ETFILE, etfile,
+ -1);
+
+ tracklist = tracklist->next;
+ g_free(row_text[0]);
+ }
+ Cddb_Set_Apply_Button_Sensivity();
+ }
+}
+
+/*
+ * Fields : artist, title, track, rest
+ * CDDB Categories : blues, classical, country, data, folk, jazz, misc, newage, reggae, rock, soundtrack
+ */
+gchar *Cddb_Generate_Request_String_With_Fields_And_Categories_Options (void)
+{
+ gchar string[256];
+ gboolean cddbinallfields, cddbinartistfield, cddbintitlefield, cddbintracknamefield, cddbinotherfield;
+ gboolean cddbinallcategories, cddbinbluescategory, cddbinclassicalcategory, cddbincountrycategory,
+ cddbinfolkcategory, cddbinjazzcategory, cddbinmisccategory, cddbinnewagecategory,
+ cddbinreggaecategory, cddbinrockcategory, cddbinsoundtrackcategory;
+
+ // Init
+ string[0] = 0;
+
+ // Fields
+ cddbinallfields = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CddbSearchInAllFields));
+ cddbinartistfield = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CddbSearchInArtistField));
+ cddbintitlefield = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CddbSearchInTitleField));
+ cddbintracknamefield = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CddbSearchInTrackNameField));
+ cddbinotherfield = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CddbSearchInOtherField));
+
+ if (cddbinallfields) strncat(string,"&allfields=YES",14);
+ else strncat(string,"&allfields=NO",13);
+
+ if (cddbinartistfield) strncat(string,"&fields=artist",14);
+ if (cddbintitlefield) strncat(string,"&fields=title",13);
+ if (cddbintracknamefield) strncat(string,"&fields=track",13);
+ if (cddbinotherfield) strncat(string,"&fields=rest",12);
+
+
+ // Categories (warning : there is one other CDDB catogories not used here ("data"))
+ cddbinallcategories = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CddbSearchInAllCategories));
+ cddbinbluescategory = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CddbSearchInBluesCategory));
+ cddbinclassicalcategory = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CddbSearchInClassicalCategory));
+ cddbincountrycategory = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CddbSearchInCountryCategory));
+ cddbinfolkcategory = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CddbSearchInFolkCategory));
+ cddbinjazzcategory = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CddbSearchInJazzCategory));
+ cddbinmisccategory = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CddbSearchInMiscCategory));
+ cddbinnewagecategory = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CddbSearchInNewageCategory));
+ cddbinreggaecategory = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CddbSearchInReggaeCategory));
+ cddbinrockcategory = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CddbSearchInRockCategory));
+ cddbinsoundtrackcategory = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CddbSearchInSoundtrackCategory));
+
+ strncat(string,"&allcats=NO",11);
+ if (cddbinallcategories)
+ {
+ // All categories except "data"
+ strncat(string,"&cats=blues&cats=classical&cats=country&cats=folk&cats=jazz"
+ "&cats=misc&cats=newage&cats=reggae&cats=rock&cats=soundtrack",119);
+ }else
+ {
+ if (cddbinbluescategory) strncat(string,"&cats=blues",11);
+ if (cddbinclassicalcategory) strncat(string,"&cats=classical",15);
+ if (cddbincountrycategory) strncat(string,"&cats=country",13);
+ if (cddbinfolkcategory) strncat(string,"&cats=folk",10);
+ if (cddbinjazzcategory) strncat(string,"&cats=jazz",10);
+ if (cddbinmisccategory) strncat(string,"&cats=misc",10);
+ if (cddbinnewagecategory) strncat(string,"&cats=newage",12);
+ if (cddbinreggaecategory) strncat(string,"&cats=reggae",12);
+ if (cddbinrockcategory) strncat(string,"&cats=rock",10);
+ if (cddbinsoundtrackcategory) strncat(string,"&cats=soundtrack",16);
+ }
+
+ return g_strdup(string);
+}
+
+
+/*
+ * Select the function to use according the server adress for the manual search
+ * - freedb.freedb.org
+ * - gnudb.gnudb.org
+ */
+gboolean Cddb_Search_Album_List_From_String (void)
+{
+ if ( strstr(CDDB_SERVER_NAME_MANUAL_SEARCH,"freedb") != NULL )
+ return Cddb_Search_Album_List_From_String_Freedb();
+ else
+ return Cddb_Search_Album_List_From_String_Gnudb();
+}
+
+
+/*
+ * Site FREEDB.ORG - Manual Search
+ * Send request (using the HTML search page in freedb.org site) to the CD database
+ * to get the list of albums matching to a string.
+ */
+gboolean Cddb_Search_Album_List_From_String_Freedb (void)
+{
+ gint socket_id;
+ gchar *string = NULL;
+ gchar *tmp, *tmp1;
+ gchar *cddb_in; // For the request to send
+ gchar *cddb_out = NULL; // Answer received
+ gchar *cddb_out_tmp;
+ gchar *msg;
+ gchar *proxy_auth = NULL;
+ gchar *cddb_server_name;
+ gint cddb_server_port;
+ gchar *cddb_server_cgi_path;
+
+ gchar *ptr_cat, *cat_str, *id_str, *art_alb_str;
+ gchar *art_alb_tmp = NULL;
+ gboolean use_art_alb = FALSE;
+ gchar *end_str;
+ gchar *html_end_str;
+ gchar buffer[MAX_STRING_LEN+1];
+ gint bytes_written;
+ gulong bytes_read_total = 0;
+ FILE *file = NULL;
+ gboolean web_search_disabled = FALSE;
+
+ gtk_statusbar_push(GTK_STATUSBAR(CddbStatusBar),CddbStatusBarContext,"");
+
+ /* Get words to search... */
+ string = g_strdup(gtk_entry_get_text(GTK_ENTRY(GTK_BIN(CddbSearchStringCombo)->child)));
+ if (!string || g_utf8_strlen(string, -1) <= 0)
+ return FALSE;
+
+ /* Format the string of words */
+ Strip_String(string);
+ /* Remove the duplicated spaces */
+ while ((tmp=strstr(string," "))!=NULL) // Search 2 spaces
+ {
+ tmp1 = tmp + 1;
+ while (*tmp1)
+ *(tmp++) = *(tmp1++);
+ *tmp = '\0';
+ }
+
+ Add_String_To_Combo_List(CddbSearchStringModel, string);
+
+ /* Convert spaces to '+' */
+ while ( (tmp=strchr(string,' '))!=NULL )
+ *tmp = '+';
+
+ cddb_server_name = g_strdup(CDDB_SERVER_NAME_MANUAL_SEARCH); //"www.freedb.org");
+ cddb_server_port = CDDB_SERVER_PORT_MANUAL_SEARCH; //80;
+ cddb_server_cgi_path = g_strdup(CDDB_SERVER_CGI_PATH_MANUAL_SEARCH);//"/~cddb/cddb.cgi");
+
+ /* Connection to the server */
+ if ( (socket_id=Cddb_Open_Connection(CDDB_USE_PROXY?CDDB_PROXY_NAME:cddb_server_name,
+ CDDB_USE_PROXY?CDDB_PROXY_PORT:cddb_server_port)) <= 0 )
+ {
+ g_free(string);
+ g_free(cddb_server_name);
+ return FALSE;
+ }
+
+ /* Build request */
+ //cddb_in = g_strdup_printf("GET http://www.freedb.org/freedb_search.php?" // In this case, problem with squid cache...
+ cddb_in = g_strdup_printf("GET /freedb_search.php?"
+ "words=%s"
+ "%s"
+ "&grouping=none"
+ " HTTP/1.1\r\n"
+ "Host: %s:%d\r\n"
+ "User-Agent: %s %s\r\n"
+ "%s"
+ "Connection: close\r\n"
+ "\r\n",
+ string,
+ (tmp=Cddb_Generate_Request_String_With_Fields_And_Categories_Options()),
+ cddb_server_name,cddb_server_port,
+ APPNAME,VERSION,
+ (proxy_auth=Cddb_Format_Proxy_Authentification())
+ );
+
+ g_free(string);
+ g_free(tmp);
+ g_free(proxy_auth);
+ //g_print("Request : '%s'\n", cddb_in);
+
+ // Send the request
+ gtk_statusbar_push(GTK_STATUSBAR(CddbStatusBar),CddbStatusBarContext,_("Sending request ..."));
+ while (gtk_events_pending()) gtk_main_iteration();
+ if ( (bytes_written=send(socket_id,cddb_in,strlen(cddb_in)+1,0)) < 0)
+ {
+ Log_Print(_("Can't send the request (%s)!"),g_strerror(errno));
+ Cddb_Close_Connection(socket_id);
+ g_free(cddb_in);
+ g_free(string);
+ g_free(cddb_server_name);
+ g_free(cddb_server_cgi_path);
+ return FALSE;
+ }
+ g_free(cddb_in);
+
+
+ // Delete previous album list
+ gtk_list_store_clear(CddbAlbumListModel);
+ gtk_list_store_clear(CddbTrackListModel);
+ Cddb_Free_Album_List();
+ gtk_widget_set_sensitive(GTK_WIDGET(CddbStopSearchButton),TRUE);
+ gtk_widget_set_sensitive(GTK_WIDGET(CddbStopSearchAutoButton),TRUE);
+
+
+ /*
+ * Read the answer
+ */
+ gtk_statusbar_push(GTK_STATUSBAR(CddbStatusBar),CddbStatusBarContext,_("Receiving data ..."));
+ while (gtk_events_pending())
+ gtk_main_iteration();
+
+ // Write result in a file
+ if (Cddb_Write_Result_To_File(socket_id,&bytes_read_total) < 0)
+ {
+ msg = g_strdup(_("The server returned a wrong answer!"));
+ gtk_statusbar_push(GTK_STATUSBAR(CddbStatusBar),CddbStatusBarContext,msg);
+ Log_Print("%s",msg);
+ g_free(msg);
+ g_free(cddb_server_name);
+ g_free(cddb_server_cgi_path);
+ gtk_widget_set_sensitive(GTK_WIDGET(CddbStopSearchButton),FALSE);
+ gtk_widget_set_sensitive(GTK_WIDGET(CddbStopSearchAutoButton),FALSE);
+ return FALSE;
+ }
+
+ // Parse server answer : Check returned code in the first line
+ if (Cddb_Read_Http_Header(&file,&cddb_out) <= 0 || !cddb_out) // Order is important!
+ {
+ msg = g_strdup_printf(_("The server returned a wrong answer! (%s)"),cddb_out);
+ gtk_statusbar_push(GTK_STATUSBAR(CddbStatusBar),CddbStatusBarContext,msg);
+ Log_Print("%s",msg);
+ g_free(msg);
+ g_free(cddb_out);
+ g_free(cddb_server_name);
+ g_free(cddb_server_cgi_path);
+ gtk_widget_set_sensitive(GTK_WIDGET(CddbStopSearchButton),FALSE);
+ gtk_widget_set_sensitive(GTK_WIDGET(CddbStopSearchAutoButton),FALSE);
+ return FALSE;
+ }
+ g_free(cddb_out);
+
+ // Read other lines, and get list of matching albums
+ // Composition of a line :
+ // - freedb.org
+ // <a href="http://www.freedb.org/freedb_search_fmt.php?cat=rock&id=8c0f0a0b">Bob Dylan / MTV Unplugged</a><br>
+ cat_str = g_strdup("http://www.freedb.org/freedb_search_fmt.php?cat=");
+ id_str = g_strdup("&id=");
+ art_alb_str = g_strdup("\">");
+ end_str = g_strdup("</a>"); //"</a><br>");
+ html_end_str = g_strdup("</body>"); // To avoid the cddb lookups to hang
+ while ( CddbWindow && !CddbStopSearch
+ && Cddb_Read_Line(&file,&cddb_out) > 0 )
+ {
+ cddb_out_tmp = cddb_out;
+ //g_print("%s\n",cddb_out); // To print received data
+
+ // If the web search is disabled! (ex : http://www.freedb.org/modules.php?name=News&file=article&sid=246)
+ // The following string is displayed in the search page
+ if (cddb_out != NULL && strstr(cddb_out_tmp,"Sorry, The web-based search is currently down.") != NULL)
+ {
+ web_search_disabled = TRUE;
+ break;
+ }
+
+ // We may have severals album in the same line (other version of the same album?)
+ // Note : we test that the 'end' delimiter exists to avoid crashes
+ while ( cddb_out != NULL && (ptr_cat=strstr(cddb_out_tmp,cat_str)) != NULL && strstr(cddb_out_tmp,end_str) != NULL )
+ {
+ gchar *ptr_font, *ptr_font1;
+ gchar *ptr_id, *ptr_art_alb, *ptr_end;
+ gchar *copy;
+ CddbAlbum *cddbalbum;
+
+ cddbalbum = g_malloc0(sizeof(CddbAlbum));
+
+
+ // Parameters of the server used
+ cddbalbum->server_name = g_strdup(cddb_server_name);
+ cddbalbum->server_port = cddb_server_port;
+ cddbalbum->server_cgi_path = g_strdup(cddb_server_cgi_path);
+ cddbalbum->bitmap = Cddb_Get_Pixbuf_From_Server_Name(cddbalbum->server_name);
+
+ // Get album category
+ cddb_out_tmp = ptr_cat + strlen(cat_str);
+ strncpy(buffer,cddb_out_tmp,MAX_STRING_LEN);
+ if ( (ptr_id=strstr(buffer,id_str)) != NULL )
+ *ptr_id = 0;
+ cddbalbum->category = Try_To_Validate_Utf8_String(buffer);
+
+
+ // Get album ID
+ //cddb_out_tmp = strstr(cddb_out_tmp,id_str) + strlen(id_str);
+ cddb_out_tmp = ptr_cat + strlen(cat_str) + 2;
+ strncpy(buffer,cddb_out_tmp,MAX_STRING_LEN);
+ if ( (ptr_art_alb=strstr(buffer,art_alb_str)) != NULL )
+ *ptr_art_alb = 0;
+ cddbalbum->id = Try_To_Validate_Utf8_String(buffer);
+
+
+ // Get album and artist names.
+ // Note : some names can be like this "<font size=-1>2</font>" (for other version of the same album)
+ cddb_out_tmp = strstr(cddb_out_tmp,art_alb_str) + strlen(art_alb_str);
+ strncpy(buffer,cddb_out_tmp,MAX_STRING_LEN);
+ if ( (ptr_end=strstr(buffer,end_str)) != NULL )
+ *ptr_end = 0;
+ if ( (ptr_font=strstr(buffer,"</font>")) != NULL )
+ {
+ copy = NULL;
+ *ptr_font = 0;
+ if ( (ptr_font1=strstr(buffer,">")) != NULL )
+ {
+ copy = g_strdup_printf("%s -> %s",ptr_font1+1,art_alb_tmp);
+ cddbalbum->other_version = TRUE;
+ }else
+ {
+ copy = g_strdup(buffer);
+ }
+
+ }else
+ {
+ copy = g_strdup(buffer);
+ art_alb_tmp = cddbalbum->artist_album;
+ use_art_alb = TRUE;
+ }
+
+ cddbalbum->artist_album = Try_To_Validate_Utf8_String(copy);
+ g_free(copy);
+
+ if (use_art_alb)
+ {
+ art_alb_tmp = cddbalbum->artist_album;
+ use_art_alb = FALSE;
+ }
+
+
+ // New position the search the next string
+ cddb_out_tmp = strstr(cddb_out_tmp,end_str) + strlen(end_str);
+
+ CddbAlbumList = g_list_append(CddbAlbumList,cddbalbum);
+ }
+
+ // To avoid the cddb lookups to hang (Patch from Paul Giordano)
+ /* It appears that on some systems that cddb lookups continue to attempt
+ * to get data from the socket even though the other system has completed
+ * sending. Here we see if the actual end of data is in the last block read.
+ * In the case of the html scan, the </body> tag is used because there's
+ * no crlf followint the </html> tag.
+ */
+ if (strstr(cddb_out_tmp,html_end_str)!=NULL)
+ {
+ g_free(cddb_out);
+ break;
+ }
+ g_free(cddb_out);
+ }
+ g_free(cat_str); g_free(id_str); g_free(art_alb_str); g_free(end_str); g_free(html_end_str);
+ g_free(cddb_server_name);
+ g_free(cddb_server_cgi_path);
+
+ gtk_widget_set_sensitive(GTK_WIDGET(CddbStopSearchButton),FALSE);
+ gtk_widget_set_sensitive(GTK_WIDGET(CddbStopSearchAutoButton),FALSE);
+
+ // Close connection
+ Cddb_Close_Connection(socket_id);
+
+ if (web_search_disabled)
+ msg = g_strdup_printf(_("Sorry, the web-based search is currently down!"));
+ else
+ msg = g_strdup_printf(_("Found %d matching album(s)"),g_list_length(CddbAlbumList));
+ gtk_statusbar_push(GTK_STATUSBAR(CddbStatusBar),CddbStatusBarContext,msg);
+ g_free(msg);
+
+ // Initialize the button
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(CddbDisplayRedLinesButton),FALSE);
+
+ // Load the albums found in the list
+ Cddb_Load_Album_List(FALSE);
+
+ return TRUE;
+}
+
+
+/*
+ * Site GNUDB.ORG - Manual Search
+ * Send request (using the HTML search page in freedb.org site) to the CD database
+ * to get the list of albums matching to a string.
+ */
+gboolean Cddb_Search_Album_List_From_String_Gnudb (void)
+{
+ gint socket_id;
+ gchar *string = NULL;
+ gchar *tmp, *tmp1;
+ gchar *cddb_in; // For the request to send
+ gchar *cddb_out = NULL; // Answer received
+ gchar *cddb_out_tmp;
+ gchar *msg;
+ gchar *proxy_auth = NULL;
+ gchar *cddb_server_name;
+ gint cddb_server_port;
+ gchar *cddb_server_cgi_path;
+
+ gchar *ptr_cat, *cat_str, *art_alb_str;
+ gchar *end_str;
+ gchar *ptr_sraf, *sraf_str, *sraf_end_str;
+ gchar *html_end_str;
+ gchar buffer[MAX_STRING_LEN+1];
+ gint bytes_written;
+ gulong bytes_read_total = 0;
+ FILE *file;
+ gint num_albums = 0;
+ gint total_num_albums = 0;
+
+ gchar *next_page = NULL;
+ gint next_page_cpt = 0;
+ gboolean next_page_found;
+
+
+ gtk_statusbar_push(GTK_STATUSBAR(CddbStatusBar),CddbStatusBarContext,"");
+
+ /* Get words to search... */
+ string = g_strdup(gtk_entry_get_text(GTK_ENTRY(GTK_BIN(CddbSearchStringCombo)->child)));
+ if (!string || g_utf8_strlen(string, -1) <= 0)
+ return FALSE;
+
+ /* Format the string of words */
+ Strip_String(string);
+ /* Remove the duplicated spaces */
+ while ((tmp=strstr(string," "))!=NULL) // Search 2 spaces
+ {
+ tmp1 = tmp + 1;
+ while (*tmp1)
+ *(tmp++) = *(tmp1++);
+ *tmp = '\0';
+ }
+
+ Add_String_To_Combo_List(CddbSearchStringModel, string);
+
+ /* Convert spaces to '+' */
+ while ( (tmp=strchr(string,' '))!=NULL )
+ *tmp = '+';
+
+
+ // Delete previous album list
+ gtk_list_store_clear(CddbAlbumListModel);
+ gtk_list_store_clear(CddbTrackListModel);
+ Cddb_Free_Album_List();
+ gtk_widget_set_sensitive(GTK_WIDGET(CddbStopSearchButton),TRUE);
+ gtk_widget_set_sensitive(GTK_WIDGET(CddbStopSearchAutoButton),TRUE);
+
+
+ // Do a loop to load all the pages of results
+ do
+ {
+ cddb_server_name = g_strdup(CDDB_SERVER_NAME_MANUAL_SEARCH); //"www.gnudb.org");
+ cddb_server_port = CDDB_SERVER_PORT_MANUAL_SEARCH; //80;
+ cddb_server_cgi_path = g_strdup(CDDB_SERVER_CGI_PATH_MANUAL_SEARCH);//"/~cddb/cddb.cgi");
+
+ /* Connection to the server */
+ if ( (socket_id=Cddb_Open_Connection(CDDB_USE_PROXY?CDDB_PROXY_NAME:cddb_server_name,
+ CDDB_USE_PROXY?CDDB_PROXY_PORT:cddb_server_port)) <= 0 )
+ {
+ g_free(string);
+ g_free(cddb_server_name);
+ gtk_widget_set_sensitive(GTK_WIDGET(CddbStopSearchButton),FALSE);
+ gtk_widget_set_sensitive(GTK_WIDGET(CddbStopSearchAutoButton),FALSE);
+ return FALSE;
+ }
+
+
+ /* Build request */
+ cddb_in = g_strdup_printf("GET /search/"
+ "%s"
+ "?page=%d"
+ " HTTP/1.1\r\n"
+ "Host: %s:%d\r\n"
+ "User-Agent: %s %s\r\n"
+ "%s"
+ "Connection: close\r\n"
+ "\r\n",
+ string,
+ next_page_cpt,
+ cddb_server_name,cddb_server_port,
+ APPNAME,VERSION,
+ (proxy_auth=Cddb_Format_Proxy_Authentification())
+ );
+ next_page_found = FALSE;
+ g_free(proxy_auth);
+ //g_print("Request : '%s'\n", cddb_in);
+
+ // Send the request
+ gtk_statusbar_push(GTK_STATUSBAR(CddbStatusBar),CddbStatusBarContext,_("Sending request ..."));
+ while (gtk_events_pending()) gtk_main_iteration();
+ if ( (bytes_written=send(socket_id,cddb_in,strlen(cddb_in)+1,0)) < 0)
+ {
+ Log_Print(_("Can't send the request (%s)!"),g_strerror(errno));
+ Cddb_Close_Connection(socket_id);
+ g_free(cddb_in);
+ g_free(string);
+ g_free(cddb_server_name);
+ g_free(cddb_server_cgi_path);
+ gtk_widget_set_sensitive(GTK_WIDGET(CddbStopSearchButton),FALSE);
+ gtk_widget_set_sensitive(GTK_WIDGET(CddbStopSearchAutoButton),FALSE);
+ return FALSE;
+ }
+ g_free(cddb_in);
+
+
+ /*
+ * Read the answer
+ */
+ if (total_num_albums != 0)
+ msg = g_strdup_printf(_("Receiving data of page %d (album %d/%d)..."),next_page_cpt,num_albums,total_num_albums);
+ else
+ msg = g_strdup_printf(_("Receiving data of page %d ..."),next_page_cpt);
+
+ gtk_statusbar_push(GTK_STATUSBAR(CddbStatusBar),CddbStatusBarContext,msg);
+ g_free(msg);
+ while (gtk_events_pending())
+ gtk_main_iteration();
+
+ // Write result in a file
+ if (Cddb_Write_Result_To_File(socket_id,&bytes_read_total) < 0)
+ {
+ msg = g_strdup(_("The server returned a wrong answer!"));
+ gtk_statusbar_push(GTK_STATUSBAR(CddbStatusBar),CddbStatusBarContext,msg);
+ Log_Print("%s",msg);
+ g_free(msg);
+ g_free(string);
+ g_free(cddb_server_name);
+ g_free(cddb_server_cgi_path);
+ gtk_widget_set_sensitive(GTK_WIDGET(CddbStopSearchButton),FALSE);
+ gtk_widget_set_sensitive(GTK_WIDGET(CddbStopSearchAutoButton),FALSE);
+ return FALSE;
+ }
+
+ // Parse server answer : Check returned code in the first line
+ file = NULL;
+ if (Cddb_Read_Http_Header(&file,&cddb_out) <= 0 || !cddb_out) // Order is important!
+ {
+ msg = g_strdup_printf(_("The server returned a wrong answer! (%s)"),cddb_out);
+ gtk_statusbar_push(GTK_STATUSBAR(CddbStatusBar),CddbStatusBarContext,msg);
+ Log_Print("%s",msg);
+ g_free(msg);
+ g_free(cddb_out);
+ g_free(string);
+ g_free(cddb_server_name);
+ g_free(cddb_server_cgi_path);
+ gtk_widget_set_sensitive(GTK_WIDGET(CddbStopSearchButton),FALSE);
+ gtk_widget_set_sensitive(GTK_WIDGET(CddbStopSearchAutoButton),FALSE);
+ return FALSE;
+ }
+ g_free(cddb_out);
+
+ // The next page if exists will contains this url :
+ g_free(next_page);
+ next_page = g_strdup_printf("?page=%d",++next_page_cpt);
+
+ // Read other lines, and get list of matching albums
+ // Composition of a line :
+ // - gnudb.org
+ // <a href="http://www.gnudb.org/cd/ro21123813"><b>Indochine / Le Birthday Album</b></a><br>
+ cat_str = g_strdup("http://www.gnudb.org/cd/");
+ art_alb_str = g_strdup("\"><b>");
+ end_str = g_strdup("</b></a>"); //"</a><br>");
+ html_end_str = g_strdup("</body>"); // To avoid the cddb lookups to hang
+ // Composition of a line displaying the number of albums
+ // <h2>Search Results, 3486 albums found:</h2>
+ sraf_str = g_strdup("<h2>Search Results, ");
+ sraf_end_str = g_strdup(" albums found:</h2>");
+
+ while ( CddbWindow && !CddbStopSearch
+ && Cddb_Read_Line(&file,&cddb_out) > 0 )
+ {
+ cddb_out_tmp = cddb_out;
+ //g_print("%s\n",cddb_out); // To print received data
+
+ // Line that displays the number of total albums return by the search
+ if ( cddb_out != NULL
+ && total_num_albums == 0 // Do it only the first time
+ && (ptr_sraf=strstr(cddb_out_tmp,sraf_end_str)) != NULL
+ && strstr(cddb_out_tmp,sraf_str) != NULL )
+ {
+ // Get total number of albums
+ ptr_sraf = 0;
+ total_num_albums = atoi(cddb_out_tmp + strlen(sraf_str));
+ }
+
+ // For GNUDB.ORG : one album per line
+ if ( cddb_out != NULL
+ && (ptr_cat=strstr(cddb_out_tmp,cat_str)) != NULL
+ && strstr(cddb_out_tmp,end_str) != NULL )
+ {
+ gchar *ptr_art_alb, *ptr_end;
+ gchar *copy, *valid;
+ CddbAlbum *cddbalbum;
+
+ cddbalbum = g_malloc0(sizeof(CddbAlbum));
+
+ // Parameters of the server used
+ cddbalbum->server_name = g_strdup(cddb_server_name);
+ cddbalbum->server_port = cddb_server_port;
+ cddbalbum->server_cgi_path = g_strdup(cddb_server_cgi_path);
+ cddbalbum->bitmap = Cddb_Get_Pixbuf_From_Server_Name(cddbalbum->server_name);
+
+ num_albums++;
+
+ // Get album category
+ cddb_out_tmp = ptr_cat + strlen(cat_str);
+ strncpy(buffer,cddb_out_tmp,MAX_STRING_LEN);
+ *(buffer+2) = 0;
+
+ // Check only the 2 first characters to set the right category
+ if ( strncmp(buffer,"blues",2)==0 )
+ valid = g_strdup("blues");
+ else if ( strncmp(buffer,"classical",2)==0 )
+ valid = g_strdup("classical");
+ else if ( strncmp(buffer,"country",2)==0 )
+ valid = g_strdup("country");
+ else if ( strncmp(buffer,"data",2)==0 )
+ valid = g_strdup("data");
+ else if ( strncmp(buffer,"folk",2)==0 )
+ valid = g_strdup("folk");
+ else if ( strncmp(buffer,"jazz",2)==0 )
+ valid = g_strdup("jazz");
+ else if ( strncmp(buffer,"misc",2)==0 )
+ valid = g_strdup("misc");
+ else if ( strncmp(buffer,"newage",2)==0 )
+ valid = g_strdup("newage");
+ else if ( strncmp(buffer,"reggae",2)==0 )
+ valid = g_strdup("reggae");
+ else if ( strncmp(buffer,"rock",2)==0 )
+ valid = g_strdup("rock");
+ else //if ( strncmp(buffer,"soundtrack",2)==0 )
+ valid = g_strdup("soundtrack");
+
+ cddbalbum->category = valid; //Not useful -> Try_To_Validate_Utf8_String(valid);
+
+
+ // Get album ID
+ cddb_out_tmp = ptr_cat + strlen(cat_str) + 2;
+ strncpy(buffer,cddb_out_tmp,MAX_STRING_LEN);
+ if ( (ptr_art_alb=strstr(buffer,art_alb_str)) != NULL )
+ *ptr_art_alb = 0;
+ cddbalbum->id = Try_To_Validate_Utf8_String(buffer);
+
+
+ // Get album and artist names.
+ cddb_out_tmp = strstr(cddb_out_tmp,art_alb_str) + strlen(art_alb_str);
+ strncpy(buffer,cddb_out_tmp,MAX_STRING_LEN);
+ if ( (ptr_end=strstr(buffer,end_str)) != NULL )
+ *ptr_end = 0;
+ copy = g_strdup(buffer);
+ cddbalbum->artist_album = Try_To_Validate_Utf8_String(buffer);
+
+ CddbAlbumList = g_list_append(CddbAlbumList,cddbalbum);
+ }
+
+ // To avoid the cddb lookups to hang (Patch from Paul Giordano)
+ /* It appears that on some systems that cddb lookups continue to attempt
+ * to get data from the socket even though the other system has completed
+ * sending. Here we see if the actual end of data is in the last block read.
+ * In the case of the html scan, the </body> tag is used because there's
+ * no crlf followint the </html> tag.
+ */
+ /***if (strstr(cddb_out_tmp,html_end_str)!=NULL)
+ break;***/
+
+
+ // Check if the link to the next results exists to loop again with the next link
+ if (cddb_out != NULL && next_page != NULL
+ && (strstr(cddb_out_tmp,next_page) != NULL || next_page_cpt < 2) ) // BUG : "next_page_cpt < 2" to fix a bug in gnudb : the page 0 doesn't contain link to the page=1, so we force it...
+ {
+ next_page_found = TRUE;
+
+ if ( !(next_page_cpt < 2) ) // Don't display message in this case as it will be displayed each line of page 0 and 1
+ {
+ msg = g_strdup_printf(_("More results to load ..."));
+ gtk_statusbar_push(GTK_STATUSBAR(CddbStatusBar),CddbStatusBarContext,msg);
+ g_free(msg);
+
+ while (gtk_events_pending())
+ gtk_main_iteration();
+ }
+ }
+
+ g_free(cddb_out);
+ }
+ g_free(cat_str); g_free(art_alb_str); g_free(end_str); g_free(html_end_str);
+ g_free(sraf_str);g_free(sraf_end_str);
+ g_free(cddb_server_name);
+ g_free(cddb_server_cgi_path);
+
+ // Close connection
+ Cddb_Close_Connection(socket_id);
+
+ } while (next_page_found);
+ g_free(string);
+
+
+ gtk_widget_set_sensitive(GTK_WIDGET(CddbStopSearchButton),FALSE);
+ gtk_widget_set_sensitive(GTK_WIDGET(CddbStopSearchAutoButton),FALSE);
+
+ msg = g_strdup_printf(_("Found %d matching album(s)"),num_albums);
+ gtk_statusbar_push(GTK_STATUSBAR(CddbStatusBar),CddbStatusBarContext,msg);
+ g_free(msg);
+
+ // Initialize the button
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(CddbDisplayRedLinesButton),FALSE);
+
+ // Load the albums found in the list
+ Cddb_Load_Album_List(FALSE);
+
+ return TRUE;
+}
+
+
+/*
+ * Send cddb query using the CddbId generated from the selected files to get the
+ * list of albums matching with this cddbid.
+ */
+gboolean Cddb_Search_Album_From_Selected_Files (void)
+{
+ gint socket_id;
+ gint bytes_written;
+ gulong bytes_read_total = 0;
+ FILE *file = NULL;
+
+ gchar *cddb_in; /* For the request to send */
+ gchar *cddb_out = NULL; /* Answer received */
+ gchar *cddb_out_tmp;
+ gchar *msg;
+ gchar *proxy_auth;
+ gchar *cddb_server_name;
+ gint cddb_server_port;
+ gchar *cddb_server_cgi_path;
+ gint server_try = 0;
+ gchar *tmp, *valid;
+ gchar *query_string;
+ gchar *cddb_discid;
+ gchar *cddb_end_str;
+
+ guint total_frames = 150; /* First offset is (almost) always 150 */
+ guint disc_length = 2; /* and 2s elapsed before first track */
+
+ GtkTreeSelection *file_selection = NULL;
+ guint file_selectedcount = 0;
+ GtkTreeIter currentIter;
+ guint total_id;
+ guint num_tracks;
+
+ gpointer iterptr;
+
+ GtkListStore *fileListModel;
+ GtkTreeIter *fileIter;
+ GList *file_iterlist = NULL;
+
+
+ if (!BrowserList) return FALSE;
+
+ // Number of selected files
+ fileListModel = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(BrowserList)));
+ file_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(BrowserList));
+ file_selectedcount = gtk_tree_selection_count_selected_rows(file_selection);
+
+ // Create the list 'file_iterlist' of selected files (no selected files => all files selected)
+ if (file_selectedcount > 0)
+ {
+ GList* file_selectedrows = gtk_tree_selection_get_selected_rows(file_selection, NULL);
+
+ while (file_selectedrows)
+ {
+ iterptr = g_malloc0(sizeof(GtkTreeIter));
+ if (gtk_tree_model_get_iter(GTK_TREE_MODEL(fileListModel),
+ (GtkTreeIter*) iterptr,
+ (GtkTreePath*) file_selectedrows->data))
+ file_iterlist = g_list_append(file_iterlist, iterptr);
+
+ if (!file_selectedrows->next) break;
+ file_selectedrows = file_selectedrows->next;
+ }
+ g_list_foreach(file_selectedrows, (GFunc)gtk_tree_path_free, NULL);
+ g_list_free(file_selectedrows);
+
+ } else /* No rows selected, use the whole list */
+ {
+ gtk_tree_model_get_iter_first(GTK_TREE_MODEL(fileListModel), &currentIter);
+
+ do
+ {
+ iterptr = g_memdup(&currentIter, sizeof(GtkTreeIter));
+ file_iterlist = g_list_append(file_iterlist, iterptr);
+ } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(fileListModel), &currentIter));
+
+ file_selectedcount = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(fileListModel), NULL);
+ }
+
+ if (file_selectedcount == 0)
+ {
+ msg = g_strdup_printf(_("No file selected!"));
+ gtk_statusbar_push(GTK_STATUSBAR(CddbStatusBar),CddbStatusBarContext,msg);
+ g_free(msg);
+ return TRUE;
+ }else if (file_selectedcount > 99)
+ {
+ // The CD redbook standard defines the maximum number of tracks as 99, any
+ // queries with more than 99 tracks will never return a result.
+ msg = g_strdup_printf(_("More than 99 files selected! Can't send request!"));
+ gtk_statusbar_push(GTK_STATUSBAR(CddbStatusBar),CddbStatusBarContext,msg);
+ g_free(msg);
+ return FALSE;
+ }else
+ {
+ msg = g_strdup_printf(_("%d file(s) selected!"),file_selectedcount);
+ gtk_statusbar_push(GTK_STATUSBAR(CddbStatusBar),CddbStatusBarContext,msg);
+ g_free(msg);
+ }
+
+ // Generate query string and compute discid from the list 'file_iterlist'
+ total_id = 0;
+ num_tracks = file_selectedcount;
+ query_string = g_strdup("");
+ while (file_iterlist)
+ {
+ ET_File *etfile;
+ gulong secs = 0;
+
+ fileIter = (GtkTreeIter *)file_iterlist->data;
+ etfile = Browser_List_Get_ETFile_From_Iter(fileIter);
+
+ tmp = query_string;
+ if (strlen(query_string)>0)
+ query_string = g_strdup_printf("%s+%d", query_string, total_frames);
+ else
+ query_string = g_strdup_printf("%d", total_frames);
+ g_free(tmp);
+
+ secs = etfile->ETFileInfo->duration;
+ total_frames += secs * 75;
+ disc_length += secs;
+ while (secs > 0)
+ {
+ total_id = total_id + (secs % 10);
+ secs = secs / 10;
+ }
+ if (!file_iterlist->next) break;
+ file_iterlist = file_iterlist->next;
+ }
+ g_list_foreach(file_iterlist, (GFunc)g_free, NULL);
+ g_list_free(file_iterlist);
+
+ // Compute CddbId
+ cddb_discid = g_strdup_printf("%08x",(guint)(((total_id % 0xFF) << 24) |
+ (disc_length << 8) | num_tracks));
+
+
+ // Delete previous album list
+ gtk_list_store_clear(CddbAlbumListModel);
+ gtk_list_store_clear(CddbTrackListModel);
+ Cddb_Free_Album_List();
+ gtk_widget_set_sensitive(GTK_WIDGET(CddbStopSearchButton),TRUE);
+ gtk_widget_set_sensitive(GTK_WIDGET(CddbStopSearchAutoButton),TRUE);
+
+
+ CDDB_USE_LOCAL_ACCESS = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CddbUseLocalAccess));
+ if (CDDB_USE_LOCAL_ACCESS) // Remote or Local acces?
+ {
+ /*
+ * Local cddb acces
+ */
+ static const gchar *CddbDir[] = // Or use cddb_genre_vs_id3_genre[][2]?
+ {
+ "blues", "classical", "country", "data", "folk",
+ "jazz", "misc", "newage", "reggae", "rock",
+ "soundtrack"
+ };
+ static const gint CddbDirSize = sizeof(CddbDir)/sizeof(CddbDir[0]) - 1 ;
+ guint i;
+
+ // We check if the file corresponding to the discid exists in each directory
+ for (i=0; i<=CddbDirSize; i++)
+ {
+ FILE *file;
+ gchar *file_path;
+
+ if (!CDDB_LOCAL_PATH || strlen(CDDB_LOCAL_PATH)==0)
+ {
+ GtkWidget *msgbox;
+
+ msgbox = msg_box_new(_("Local CD search..."),_("The path for 'Local "
+ "CD Data Base' wasn't defined!\nFill it in the "
+ "preferences window."),GTK_STOCK_DIALOG_ERROR,BUTTON_YES,0);
+ msg_box_hide_check_button(MSG_BOX(msgbox));
+ msg_box_run(MSG_BOX(msgbox));
+ gtk_widget_destroy(msgbox);
+ break;
+ }
+ file_path = g_strconcat(CDDB_LOCAL_PATH,
+ CDDB_LOCAL_PATH[strlen(CDDB_LOCAL_PATH)-1]!=G_DIR_SEPARATOR ? G_DIR_SEPARATOR_S : "",
+ CddbDir[i],"/",cddb_discid,NULL);
+
+ if ( (file=fopen(file_path,"r"))!=0 )
+ {
+ // File found
+ CddbAlbum *cddbalbum;
+ gint rc = 0;
+
+ cddbalbum = g_malloc0(sizeof(CddbAlbum));
+
+ // Parameters of the server used (Local acces => specific!)
+ cddbalbum->server_name = NULL; // No server name
+ cddbalbum->server_port = 0; // No server port
+ cddbalbum->server_cgi_path = g_strdup(file_path); // File name
+ cddbalbum->bitmap = Cddb_Get_Pixbuf_From_Server_Name(file_path);
+
+ // Get album category
+ cddbalbum->category = Try_To_Validate_Utf8_String(CddbDir[i]);
+
+ // Get album ID
+ cddbalbum->id = Try_To_Validate_Utf8_String(cddb_discid);
+
+ while ( CddbWindow && !CddbStopSearch
+ && (rc = Cddb_Read_Line(&file,&cddb_out)) > 0 )
+ {
+ if (!cddb_out) // Empty line?
+ continue;
+ //g_print("%s\n",cddb_out);
+
+ // Get Album and Artist names
+ if ( strncmp(cddb_out,"DTITLE=",7)==0 )
+ {
+ // Note : disc title too long take severals lines. For example :
+ // DTITLE=Marilyn Manson / The Nobodies (2005 Against All Gods Mix - Korea Tour L
+ // DTITLE=imited Edition)
+ if (!cddbalbum->artist_album)
+ {
+ // It is the first time we find DTITLE...
+
+ // Artist and album
+ cddbalbum->artist_album = Try_To_Validate_Utf8_String(cddb_out+7); // '7' to skip 'DTITLE='
+ }else
+ {
+ // It is at least the second time we find DTITLE
+ // So we suppose that only the album was truncated
+
+ // Album
+ valid = Try_To_Validate_Utf8_String(cddb_out+7); // '7' to skip 'DTITLE='
+ tmp = cddbalbum->artist_album; // To free...
+ cddbalbum->artist_album = g_strconcat(cddbalbum->artist_album,valid,NULL);
+ g_free(tmp);
+
+ // Don't need to read more data to read in the file
+ break;
+ }
+ }
+
+ g_free(cddb_out);
+ }
+
+ CddbAlbumList = g_list_append(CddbAlbumList,cddbalbum);
+
+ if (rc != 0) // Need to close it, if not done in Cddb_Read_Line
+ fclose(file);
+ }
+ g_free(file_path);
+
+ }
+
+
+ }else
+ {
+
+ /*
+ * Remote cddb acces
+ *
+ * Request the two servers
+ * - 1) www.freedb.org
+ * - 2) MusicBrainz Gateway : freedb.musicbrainz.org (in Easytag < 2.1.1, it was: www.mb.inhouse.co.uk)
+ */
+ while (server_try < 2)
+ {
+ server_try++;
+ if (server_try == 1)
+ {
+ // 1rst try
+ cddb_server_name = g_strdup(CDDB_SERVER_NAME_AUTOMATIC_SEARCH);
+ cddb_server_port = CDDB_SERVER_PORT_AUTOMATIC_SEARCH;
+ cddb_server_cgi_path = g_strdup(CDDB_SERVER_CGI_PATH_AUTOMATIC_SEARCH);
+ }else
+ {
+ // 2sd try
+ cddb_server_name = g_strdup(CDDB_SERVER_NAME_AUTOMATIC_SEARCH2);
+ cddb_server_port = CDDB_SERVER_PORT_AUTOMATIC_SEARCH2;
+ cddb_server_cgi_path = g_strdup(CDDB_SERVER_CGI_PATH_AUTOMATIC_SEARCH2);
+ }
+
+ // Check values
+ if (!cddb_server_name || strcmp(cddb_server_name,"")==0)
+ continue;
+
+ // Connection to the server
+ if ( (socket_id=Cddb_Open_Connection(CDDB_USE_PROXY?CDDB_PROXY_NAME:cddb_server_name,
+ CDDB_USE_PROXY?CDDB_PROXY_PORT:cddb_server_port)) <= 0 )
+ {
+ return FALSE;
+ }
+
+ // CDDB Request (ex: GET /~cddb/cddb.cgi?cmd=cddb+query+0800ac01+1++150+172&hello=noname+localhost+EasyTAG+0.31&proto=1 HTTP/1.1\r\nHost: freedb.freedb.org:80\r\nConnection: close)
+ // Without proxy : "GET /~cddb/cddb.cgi?..." but doesn't work with a proxy.
+ // With proxy : "GET http://freedb.freedb.org/~cddb/cddb.cgi?..."
+ // proto=1 => ISO-8859-1 - proto=6 => UTF-8
+ cddb_in = g_strdup_printf("GET %s%s%s?cmd=cddb+query+"
+ "%s+"
+ "%d+%s+"
+ "%d"
+ "&hello=noname+localhost+%s+%s"
+ "&proto=6 HTTP/1.1\r\n"
+ "Host: %s:%d\r\n"
+ "%s"
+ "Connection: close\r\n\r\n",
+ CDDB_USE_PROXY?"http://":"",CDDB_USE_PROXY?cddb_server_name:"", cddb_server_cgi_path,
+ cddb_discid,
+ num_tracks, query_string,
+ disc_length,
+ APPNAME,VERSION,
+ cddb_server_name,cddb_server_port,
+ (proxy_auth=Cddb_Format_Proxy_Authentification())
+ );
+ g_free(proxy_auth);
+ //g_print("Request : '%s'\n", cddb_in);
+
+ msg = g_strdup_printf(_("Sending request (CddbId: %s, #tracks: %d, Disc length: %d) ..."),
+ cddb_discid,num_tracks,disc_length);
+ gtk_statusbar_push(GTK_STATUSBAR(CddbStatusBar),CddbStatusBarContext,msg);
+ g_free(msg);
+
+ while (gtk_events_pending())
+ gtk_main_iteration();
+
+ if ( (bytes_written=send(socket_id,cddb_in,strlen(cddb_in)+1,0)) < 0)
+ {
+ Log_Print(_("Can't send the request (%s)!"),g_strerror(errno));
+ Cddb_Close_Connection(socket_id);
+ g_free(cddb_in);
+ g_free(cddb_server_name);
+ g_free(cddb_server_cgi_path);
+ return FALSE;
+ }
+ g_free(cddb_in);
+
+
+ /*
+ * Read the answer
+ */
+ gtk_statusbar_push(GTK_STATUSBAR(CddbStatusBar),CddbStatusBarContext,_("Receiving data ..."));
+ while (gtk_events_pending())
+ gtk_main_iteration();
+
+ // Write result in a file
+ if (Cddb_Write_Result_To_File(socket_id,&bytes_read_total) < 0)
+ {
+ msg = g_strdup(_("The server returned a wrong answer!"));
+ gtk_statusbar_push(GTK_STATUSBAR(CddbStatusBar),CddbStatusBarContext,msg);
+ Log_Print("%s",msg);
+ g_free(msg);
+ g_free(cddb_server_name);
+ g_free(cddb_server_cgi_path);
+ gtk_widget_set_sensitive(GTK_WIDGET(CddbStopSearchButton),FALSE);
+ gtk_widget_set_sensitive(GTK_WIDGET(CddbStopSearchAutoButton),FALSE);
+ return FALSE;
+ }
+
+ // Parse server answer : Check returned code in the first line
+ file = NULL;
+ if (Cddb_Read_Http_Header(&file,&cddb_out) <= 0 || !cddb_out) // Order is important!
+ {
+ msg = g_strdup_printf(_("The server returned a wrong answer! (%s)"),cddb_out);
+ gtk_statusbar_push(GTK_STATUSBAR(CddbStatusBar),CddbStatusBarContext,msg);
+ Log_Print("%s",msg);
+ g_free(msg);
+ g_free(cddb_out);
+ g_free(cddb_server_name);
+ g_free(cddb_server_cgi_path);
+ gtk_widget_set_sensitive(GTK_WIDGET(CddbStopSearchButton),FALSE);
+ gtk_widget_set_sensitive(GTK_WIDGET(CddbStopSearchAutoButton),FALSE);
+ return FALSE;
+ }
+ g_free(cddb_out);
+
+ cddb_end_str = g_strdup(".");
+
+ /*
+ * Format :
+ * For Freedb, Gnudb, the lines to read are like :
+ * 211 Found inexact matches, list follows (until terminating `.')
+ * rock 8f0dc00b Archive / Noise
+ * rock 7b0dd80b Archive / Noise
+ * .
+ * For MusicBrainz Cddb Gateway (see http://wiki.musicbrainz.org/CddbGateway), the lines to read are like :
+ * 200 jazz 7e0a100a Pink Floyd / Dark Side of the Moon
+ */
+ while ( CddbWindow && !CddbStopSearch
+ && Cddb_Read_Line(&file,&cddb_out) > 0 )
+ {
+ cddb_out_tmp = cddb_out;
+ //g_print("%s\n",cddb_out);
+
+ // To avoid the cddb lookups to hang (Patch from Paul Giordano)
+ /* It appears that on some systems that cddb lookups continue to attempt
+ * to get data from the socket even though the other system has completed
+ * sending. The fix adds one check to the loops to see if the actual
+ * end of data is in the last block read. In this case, the last line
+ * will be a single '.'
+ */
+ if ( cddb_out_tmp && strlen(cddb_out_tmp)<=3 && strstr(cddb_out_tmp,cddb_end_str)!=NULL )
+ break;
+
+ // Compatibility for the MusicBrainz CddbGateway
+ if ( cddb_out_tmp && strlen(cddb_out_tmp)>3
+ && (strncmp(cddb_out_tmp,"200",3)==0
+ || strncmp(cddb_out_tmp,"210",3)==0
+ || strncmp(cddb_out_tmp,"211",3)==0) )
+ cddb_out_tmp = cddb_out_tmp + 4;
+
+ // Reading of lines with albums (skiping return code lines :
+ // "211 Found inexact matches, list follows (until terminating `.')" )
+ if (cddb_out != NULL && strstr(cddb_out_tmp,"/") != NULL)
+ {
+ gchar* ptr;
+ CddbAlbum *cddbalbum;
+
+ cddbalbum = g_malloc0(sizeof(CddbAlbum));
+
+ // Parameters of the server used
+ cddbalbum->server_name = g_strdup(cddb_server_name);
+ cddbalbum->server_port = cddb_server_port;
+ cddbalbum->server_cgi_path = g_strdup(cddb_server_cgi_path);
+ cddbalbum->bitmap = Cddb_Get_Pixbuf_From_Server_Name(cddbalbum->server_name);
+
+ // Get album category
+ if ( (ptr = strstr(cddb_out_tmp, " ")) != NULL )
+ {
+ *ptr = 0;
+ cddbalbum->category = Try_To_Validate_Utf8_String(cddb_out_tmp);
+ *ptr = ' ';
+ cddb_out_tmp = ptr + 1;
+ }
+
+ // Get album ID
+ if ( (ptr = strstr(cddb_out_tmp, " ")) != NULL )
+ {
+ *ptr = 0;
+ cddbalbum->id = Try_To_Validate_Utf8_String(cddb_out_tmp);
+ *ptr = ' ';
+ cddb_out_tmp = ptr + 1;
+ }
+
+ // Get album and artist names.
+ cddbalbum->artist_album = Try_To_Validate_Utf8_String(cddb_out_tmp);
+
+ CddbAlbumList = g_list_append(CddbAlbumList,cddbalbum);
+ }
+
+ g_free(cddb_out);
+ }
+ g_free(cddb_end_str);
+ g_free(cddb_server_name);
+ g_free(cddb_server_cgi_path);
+
+ // Close connection
+ Cddb_Close_Connection(socket_id);
+ }
+
+ }
+
+ msg = g_strdup_printf(_("Found %d matching album(s) for DiscID '%s'"), g_list_length(CddbAlbumList),cddb_discid);
+ gtk_statusbar_push(GTK_STATUSBAR(CddbStatusBar),CddbStatusBarContext,msg);
+ g_free(msg);
+
+ g_free(cddb_discid);
+ g_free(query_string);
+
+ gtk_widget_set_sensitive(GTK_WIDGET(CddbStopSearchButton),FALSE);
+ gtk_widget_set_sensitive(GTK_WIDGET(CddbStopSearchAutoButton),FALSE);
+
+ // Initialize the button
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(CddbDisplayRedLinesButton), FALSE);
+
+ // Load the albums found in the list
+ Cddb_Load_Album_List(FALSE);
+
+ return TRUE;
+}
+
+
+/*
+ * Callback when selecting a row in the Album List.
+ * We get the list of tracks of the selected album
+ */
+gboolean Cddb_Get_Album_Tracks_List_CB (GtkTreeSelection *selection, gpointer data)
+{
+ gint i;
+ gint i_max = 5;
+
+ /* As may be not opened the first time (The server returned a wrong answer!)
+ * me try to reconnect severals times */
+ for (i = 1; i <= i_max; i++)
+ {
+ if ( Cddb_Get_Album_Tracks_List(selection) == TRUE )
+ {
+ break;
+ }
+ }
+ if (i <= i_max)
+ {
+ return TRUE;
+ } else
+ {
+ return FALSE;
+ }
+}
+
+/*
+ * Look up a specific album in freedb, and save to a CddbAlbum structure
+ */
+gboolean Cddb_Get_Album_Tracks_List (GtkTreeSelection* selection)
+{
+ gint socket_id = 0;
+ CddbAlbum *cddbalbum = NULL;
+ GList *TrackOffsetList = NULL;
+ gchar *cddb_in, *cddb_out = NULL;
+ gchar *cddb_end_str, *msg, *copy, *valid;
+ gchar *proxy_auth;
+ gchar *cddb_server_name;
+ gint cddb_server_port;
+ gchar *cddb_server_cgi_path;
+ gint bytes_written;
+ gulong bytes_read_total = 0;
+ FILE *file;
+ gboolean read_track_offset = FALSE;
+ GtkTreeIter row;
+
+ if (!CddbWindow)
+ return FALSE;
+
+ gtk_list_store_clear(CddbTrackListModel);
+ Cddb_Set_Apply_Button_Sensivity();
+ if (gtk_tree_selection_get_selected(selection, NULL, &row))
+ {
+ gtk_tree_model_get(GTK_TREE_MODEL(CddbAlbumListModel), &row, CDDB_ALBUM_LIST_DATA, &cddbalbum, -1);
+ }
+ if (!cddbalbum)
+ return FALSE;
+
+ // We have already the track list
+ if (cddbalbum->track_list != NULL)
+ {
+ Cddb_Load_Track_Album_List(cddbalbum->track_list);
+ return TRUE;
+ }
+
+ // Parameters of the server used
+ cddb_server_name = cddbalbum->server_name;
+ cddb_server_port = cddbalbum->server_port;
+ cddb_server_cgi_path = cddbalbum->server_cgi_path;
+
+ if (!cddb_server_name)
+ {
+ // Local access
+ if ( (file=fopen(cddb_server_cgi_path,"r"))==0 )
+ {
+ Log_Print(_("Can't load file: '%s' (%s)!"),cddb_server_cgi_path,g_strerror(errno));
+ return FALSE;
+ }
+
+ }else
+ {
+ // Remote access
+
+ // Connection to the server
+ if ( (socket_id=Cddb_Open_Connection(CDDB_USE_PROXY?CDDB_PROXY_NAME:cddb_server_name,
+ CDDB_USE_PROXY?CDDB_PROXY_PORT:cddb_server_port)) <= 0 )
+ return FALSE;
+
+ // CDDB Request (ex: GET /~cddb/cddb.cgi?cmd=cddb+read+jazz+0200a401&hello=noname+localhost+EasyTAG+0.31&proto=1 HTTP/1.1\r\nHost: freedb.freedb.org:80\r\nConnection: close)
+ // Without proxy : "GET /~cddb/cddb.cgi?..." but doesn't work with a proxy.
+ // With proxy : "GET http://freedb.freedb.org/~cddb/cddb.cgi?..."
+ cddb_in = g_strdup_printf("GET %s%s%s?cmd=cddb+read+"
+ "%s+%s"
+ "&hello=noname+localhost+%s+%s"
+ "&proto=6 HTTP/1.1\r\n"
+ "Host: %s:%d\r\n"
+ "%s"
+ "Connection: close\r\n\r\n",
+ CDDB_USE_PROXY?"http://":"",CDDB_USE_PROXY?cddb_server_name:"", cddb_server_cgi_path,
+ cddbalbum->category,cddbalbum->id,
+ APPNAME,VERSION,
+ cddb_server_name,cddb_server_port,
+ (proxy_auth=Cddb_Format_Proxy_Authentification())
+ );
+ g_free(proxy_auth);
+ //g_print("Request : '%s'\n", cddb_in);
+
+ // Send the request
+ gtk_statusbar_push(GTK_STATUSBAR(CddbStatusBar),CddbStatusBarContext,_("Sending request ..."));
+ while (gtk_events_pending()) gtk_main_iteration();
+ if ( (bytes_written=send(socket_id,cddb_in,strlen(cddb_in)+1,0)) < 0)
+ {
+ Log_Print(_("Can't send the request (%s)!"),g_strerror(errno));
+ Cddb_Close_Connection(socket_id);
+ g_free(cddb_in);
+ return FALSE;
+ }
+ g_free(cddb_in);
+
+
+ // Read the answer
+ gtk_statusbar_push(GTK_STATUSBAR(CddbStatusBar),CddbStatusBarContext,_("Receiving data ..."));
+ while (gtk_events_pending())
+ gtk_main_iteration();
+
+ // Write result in a file
+ if (Cddb_Write_Result_To_File(socket_id,&bytes_read_total) < 0)
+ {
+ msg = g_strdup(_("The server returned a wrong answer!"));
+ gtk_statusbar_push(GTK_STATUSBAR(CddbStatusBar),CddbStatusBarContext,msg);
+ Log_Print("%s",msg);
+ g_free(msg);
+ gtk_widget_set_sensitive(GTK_WIDGET(CddbStopSearchButton),FALSE);
+ gtk_widget_set_sensitive(GTK_WIDGET(CddbStopSearchAutoButton),FALSE);
+ return FALSE;
+ }
+
+
+ // Parse server answer : Check HTTP Header and CDDB Header
+ file = NULL;
+ if ( Cddb_Read_Http_Header(&file,&cddb_out) <= 0
+ || Cddb_Read_Cddb_Header(&file,&cddb_out) <= 0 )
+ {
+ gchar *msg = g_strdup_printf(_("The server returned a wrong answer! (%s)"),cddb_out);
+ gtk_statusbar_push(GTK_STATUSBAR(CddbStatusBar),CddbStatusBarContext,msg);
+ Log_Print("%s",msg);
+ g_free(msg);
+ g_free(cddb_out);
+ return FALSE;
+ }
+ g_free(cddb_out);
+
+ }
+ cddb_end_str = g_strdup(".");
+
+ while ( CddbWindow && !CddbStopSearch
+ && Cddb_Read_Line(&file,&cddb_out) > 0 )
+ {
+ if (!cddb_out) // Empty line?
+ continue;
+ //g_print("%s\n",cddb_out);
+
+ // To avoid the cddb lookups to hang (Patch from Paul Giordano)
+ /* It appears that on some systems that cddb lookups continue to attempt
+ * to get data from the socket even though the other system has completed
+ * sending. The fix adds one check to the loops to see if the actual
+ * end of data is in the last block read. In this case, the last line
+ * will be a single '.'
+ */
+ if (strlen(cddb_out)<=3 && strstr(cddb_out,cddb_end_str)!=NULL)
+ break;
+
+ if ( strstr(cddb_out,"Track frame offsets")!=NULL ) // We read the Track frame offset
+ {
+ read_track_offset = TRUE; // The next reads are for the tracks offset
+ continue;
+
+ }else if (read_track_offset) // We are reading a track offset? (generates TrackOffsetList)
+ {
+ if ( strtoul(cddb_out+1,NULL,10)>0 )
+ {
+ CddbTrackFrameOffset *cddbtrackframeoffset = g_malloc0(sizeof(CddbTrackFrameOffset));
+ cddbtrackframeoffset->offset = strtoul(cddb_out+1,NULL,10);
+ TrackOffsetList = g_list_append(TrackOffsetList,cddbtrackframeoffset);
+ }else
+ {
+ read_track_offset = FALSE; // No more track offset
+ }
+ continue;
+
+ }else if ( strstr(cddb_out,"Disc length: ")!=NULL ) // Length of album (in second)
+ {
+ cddbalbum->duration = atoi(strchr(cddb_out,':')+1);
+ if (TrackOffsetList) // As it must be the last item, do nothing if no previous data
+ {
+ CddbTrackFrameOffset *cddbtrackframeoffset = g_malloc0(sizeof(CddbTrackFrameOffset));
+ cddbtrackframeoffset->offset = cddbalbum->duration * 75; // It's the last offset
+ TrackOffsetList = g_list_append(TrackOffsetList,cddbtrackframeoffset);
+ }
+ continue;
+
+ }else if ( strncmp(cddb_out,"DTITLE=",7)==0 ) // "Artist / Album" names
+ {
+ // Note : disc title too long take severals lines. For example :
+ // DTITLE=Marilyn Manson / The Nobodies (2005 Against All Gods Mix - Korea Tour L
+ // DTITLE=imited Edition)
+ if (!cddbalbum->album)
+ {
+ // It is the first time we find DTITLE...
+
+ gchar *alb_ptr = strstr(cddb_out," / ");
+ // Album
+ if (alb_ptr && alb_ptr+3)
+ {
+ cddbalbum->album = Try_To_Validate_Utf8_String(alb_ptr+3);
+ *alb_ptr = 0;
+ }
+
+ // Artist
+ cddbalbum->artist = Try_To_Validate_Utf8_String(cddb_out+7); // '7' to skip 'DTITLE='
+ }else
+ {
+ // It is at least the second time we find DTITLE
+ // So we suppose that only the album was truncated
+
+ // Album
+ valid = Try_To_Validate_Utf8_String(cddb_out+7); // '7' to skip 'DTITLE='
+ copy = cddbalbum->album; // To free...
+ cddbalbum->album = g_strconcat(cddbalbum->album,valid,NULL);
+ g_free(copy);
+ }
+ continue;
+
+ }else if ( strncmp(cddb_out,"DYEAR=",6)==0 ) // Year
+ {
+ valid = Try_To_Validate_Utf8_String(cddb_out+6); // '6' to skip 'DYEAR='
+ if (g_utf8_strlen(valid, -1))
+ cddbalbum->year = valid;
+ continue;
+
+ }else if ( strncmp(cddb_out,"DGENRE=",7)==0 ) // Genre
+ {
+ valid = Try_To_Validate_Utf8_String(cddb_out+7); // '7' to skip 'DGENRE='
+ if (g_utf8_strlen(valid, -1))
+ cddbalbum->genre = valid;
+ continue;
+
+ }else if ( strncmp(cddb_out,"TTITLE",6)==0 ) // Track title (for exemple : TTITLE10=xxxx)
+ {
+ CddbTrackAlbum *cddbtrackalbum_last = NULL;
+
+ CddbTrackAlbum *cddbtrackalbum = g_malloc0(sizeof(CddbTrackAlbum));
+ cddbtrackalbum->cddbalbum = cddbalbum; // To find the CddbAlbum father quickly
+
+ // Here is a fix when TTITLExx doesn't contain an "=", we skip the line
+ if ( (copy = g_utf8_strchr(cddb_out,-1,'=')) != NULL )
+ {
+ cddbtrackalbum->track_name = Try_To_Validate_Utf8_String(copy+1);
+ }else
+ {
+ continue;
+ }
+
+ *g_utf8_strchr(cddb_out,-1,'=') = 0;
+ cddbtrackalbum->track_number = atoi(cddb_out+6)+1;
+
+ // Note : titles too long take severals lines. For example :
+ // TTITLE15=Bob Marley vs. Funkstar De Luxe Remix - Sun Is Shining (Radio De Lu
+ // TTITLE15=xe Edit)
+ // So to check it, we compare current track number with the previous one...
+ if (cddbalbum->track_list)
+ cddbtrackalbum_last = g_list_last(cddbalbum->track_list)->data;
+ if (cddbtrackalbum_last && cddbtrackalbum_last->track_number == cddbtrackalbum->track_number)
+ {
+ gchar *track_name = g_strconcat(cddbtrackalbum_last->track_name,cddbtrackalbum->track_name,NULL);
+ g_free(cddbtrackalbum_last->track_name);
+
+ cddbtrackalbum_last->track_name = Try_To_Validate_Utf8_String(track_name);
+
+ // Frees useless allocated data previously
+ g_free(cddbtrackalbum->track_name);
+ g_free(cddbtrackalbum);
+ }else
+ {
+ if (TrackOffsetList && TrackOffsetList->next)
+ {
+ cddbtrackalbum->duration = ( ((CddbTrackFrameOffset *)TrackOffsetList->next->data)->offset - ((CddbTrackFrameOffset *)TrackOffsetList->data)->offset ) / 75; // Calculate time in seconds
+ TrackOffsetList = TrackOffsetList->next;
+ }
+ cddbalbum->track_list = g_list_append(cddbalbum->track_list,cddbtrackalbum);
+ }
+ continue;
+
+ }else if ( strncmp(cddb_out,"EXTD=",5)==0 ) // Extended album data
+ {
+ gchar *genre_ptr = strstr(cddb_out,"ID3G:");
+ gchar *year_ptr = strstr(cddb_out,"YEAR:");
+ // May contains severals EXTD field it too long
+ // EXTD=Techno
+ // EXTD= YEAR: 1997 ID3G: 18
+ // EXTD= ID3G: 17
+ if (year_ptr && cddbalbum->year)
+ cddbalbum->year = g_strdup_printf("%d",atoi(year_ptr+5));
+ if (genre_ptr && cddbalbum->genre)
+ cddbalbum->genre = g_strdup(Id3tag_Genre_To_String(atoi(genre_ptr+5)));
+ continue;
+ }
+
+ g_free(cddb_out);
+ }
+ g_free(cddb_end_str);
+
+ if (cddb_server_name)
+ {
+ // Remote access
+
+ /* Close connection */
+ Cddb_Close_Connection(socket_id);
+ }
+
+ /* Set color of the selected row (without reloading the whole list) */
+ Cddb_Album_List_Set_Row_Appearance(&row);
+
+ /* Load the track list of the album */
+ gtk_statusbar_push(GTK_STATUSBAR(CddbStatusBar),CddbStatusBarContext,_("Loading album track list ..."));
+ while (gtk_events_pending()) gtk_main_iteration();
+ Cddb_Load_Track_Album_List(cddbalbum->track_list);
+
+ Cddb_Show_Album_Info(gtk_tree_view_get_selection(GTK_TREE_VIEW(CddbAlbumListView)),NULL);
+
+ // Frees 'TrackOffsetList'
+ TrackOffsetList = g_list_last(TrackOffsetList);
+ while (TrackOffsetList)
+ {
+ g_free(TrackOffsetList->data);
+ if (!TrackOffsetList->prev) break;
+ TrackOffsetList = TrackOffsetList->prev;
+ }
+ g_list_free(TrackOffsetList);
+ TrackOffsetList = (GList *)NULL;
+ return TRUE;
+}
+
+/*
+ * Set the row apperance depending if we have cached info or not
+ * Bold/Red = Info is cached
+ * Italic/Light Red = Duplicate CDDB entry
+ */
+void Cddb_Album_List_Set_Row_Appearance (GtkTreeIter *row)
+{
+ CddbAlbum *cddbalbum = NULL;
+
+ gtk_tree_model_get(GTK_TREE_MODEL(CddbAlbumListModel), row, CDDB_ALBUM_LIST_DATA, &cddbalbum, -1);
+
+ if (cddbalbum->track_list != NULL)
+ {
+ if (CHANGED_FILES_DISPLAYED_TO_BOLD)
+ {
+ gtk_list_store_set(CddbAlbumListModel, row, CDDB_ALBUM_LIST_FONT_WEIGHT, PANGO_WEIGHT_BOLD, -1);
+ } else
+ {
+ if(cddbalbum->other_version == TRUE)
+ {
+ gtk_list_store_set(CddbAlbumListModel, row, CDDB_ALBUM_LIST_FOREGROUND_COLOR, &LIGHT_RED, -1);
+ } else
+ {
+ gtk_list_store_set(CddbAlbumListModel, row, CDDB_ALBUM_LIST_FOREGROUND_COLOR, &RED, -1);
+ }
+ }
+ } else
+ {
+ if(cddbalbum->other_version == TRUE)
+ {
+ if (CHANGED_FILES_DISPLAYED_TO_BOLD)
+ {
+ gtk_list_store_set(CddbAlbumListModel, row, CDDB_ALBUM_LIST_FONT_STYLE, PANGO_STYLE_ITALIC, -1);
+ } else
+ {
+ gtk_list_store_set(CddbAlbumListModel, row, CDDB_ALBUM_LIST_FOREGROUND_COLOR, &GREY, -1);
+ }
+ }
+ }
+}
+
+
+/*
+ * Set CDDB data (from tracks list) into tags of the main file list
+ */
+gboolean Cddb_Set_Track_Infos_To_File_List (void)
+{
+ guint row;
+ guint list_length;
+ guint rows_to_loop = 0;
+ guint selectedcount;
+ guint file_selectedcount;
+ guint counter = 0;
+ GList *file_iterlist = NULL;
+ GList *file_selectedrows;
+ GList *selectedrows = NULL;
+ gchar buffer[256];
+ gboolean CddbTrackList_Line_Selected;
+ gboolean cddbsettoallfields, cddbsettotitle, cddbsettoartist, cddbsettoalbum, cddbsettoyear,
+ cddbsettotrack, cddbsettotracktotal, cddbsettogenre, cddbsettofilename;
+ CddbTrackAlbum *cddbtrackalbum = NULL;
+ GtkTreeSelection *selection = NULL;
+ GtkTreeSelection *file_selection = NULL;
+ GtkListStore *fileListModel;
+ GtkTreePath *currentPath = NULL;
+ GtkTreeIter currentIter;
+ GtkTreeIter *fileIter;
+ gpointer iterptr;
+
+ if (!CddbWindow || !BrowserList || !ETCore->ETFileDisplayedList)
+ return FALSE;
+
+ // Save the current displayed data
+ ET_Save_File_Data_From_UI(ETCore->ETFileDisplayed);
+
+ cddbsettoallfields = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CddbSetToAllFields));
+ cddbsettotitle = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CddbSetToTitle));
+ cddbsettoartist = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CddbSetToArtist));
+ cddbsettoalbum = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CddbSetToAlbum));
+ cddbsettoyear = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CddbSetToYear));
+ cddbsettotrack = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CddbSetToTrack));
+ cddbsettotracktotal = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CddbSetToTrackTotal));
+ cddbsettogenre = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CddbSetToGenre));
+ cddbsettofilename = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CddbSetToFileName));
+
+ fileListModel = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(BrowserList)));
+ list_length = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(CddbTrackListModel), NULL);
+
+ // Take the selected files in the cddb track list, else the full list
+ // Note : Just used to calculate "cddb_track_list_length" because
+ // "GPOINTER_TO_INT(cddb_track_list->data)" doesn't return the number of the
+ // line when "cddb_track_list = g_list_first(GTK_CLIST(CddbTrackCList)->row_list)"
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(CddbTrackListView));
+ selectedcount = gtk_tree_selection_count_selected_rows(selection);
+
+ /* Check if at least one line was selected. No line selected is equal to all lines selected. */
+ CddbTrackList_Line_Selected = FALSE;
+
+ if (selectedcount > 0)
+ {
+ /* Loop through selected rows only */
+ CddbTrackList_Line_Selected = TRUE;
+ rows_to_loop = selectedcount;
+ selectedrows = gtk_tree_selection_get_selected_rows(selection, NULL);
+ } else
+ {
+ /* Loop through all rows */
+ CddbTrackList_Line_Selected = FALSE;
+ rows_to_loop = list_length;
+ }
+
+ file_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(BrowserList));
+ file_selectedcount = gtk_tree_selection_count_selected_rows(file_selection);
+
+ if (file_selectedcount > 0)
+ {
+ /* Rows are selected in the file list, apply tags to them only */
+ file_selectedrows = gtk_tree_selection_get_selected_rows(file_selection, NULL);
+
+ while (file_selectedrows)
+ {
+ counter++;
+ iterptr = g_malloc0(sizeof(GtkTreeIter));
+ if (gtk_tree_model_get_iter(GTK_TREE_MODEL(fileListModel),
+ (GtkTreeIter *)iterptr,
+ (GtkTreePath *)file_selectedrows->data))
+ file_iterlist = g_list_append(file_iterlist, iterptr);
+
+ if(!file_selectedrows->next || counter == rows_to_loop) break;
+ file_selectedrows = file_selectedrows->next;
+ }
+
+ /* Free the useless bit */
+ g_list_foreach(file_selectedrows, (GFunc)gtk_tree_path_free, NULL);
+ g_list_free(file_selectedrows);
+
+ } else /* No rows selected, use the first x items in the list */
+ {
+ gtk_tree_model_get_iter_first(GTK_TREE_MODEL(fileListModel), &currentIter);
+
+ do
+ {
+ counter++;
+ iterptr = g_memdup(&currentIter, sizeof(GtkTreeIter));
+ file_iterlist = g_list_append(file_iterlist, iterptr);
+ } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(fileListModel), &currentIter));
+
+ file_selectedcount = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(fileListModel), NULL);
+ }
+
+ if (file_selectedcount != rows_to_loop)
+ {
+ GtkWidget *msgbox;
+ gchar *msg;
+ gint button;
+
+ msg = g_strdup_printf(_("Be careful, you are applying %d lines of the CDDB "
+ "results to %d lines in the list of files!\n\nDo you want to continue ?"),
+ rows_to_loop,file_selectedcount);
+ msgbox = msg_box_new(_("Write Tag from CDDB..."),msg,GTK_STOCK_DIALOG_QUESTION,BUTTON_NO,BUTTON_YES,0);
+ msg_box_hide_check_button(MSG_BOX(msgbox));
+ button = msg_box_run(MSG_BOX(msgbox));
+ gtk_widget_destroy(msgbox);
+
+ if (button != BUTTON_YES)
+ {
+ g_list_foreach(file_iterlist, (GFunc)g_free, NULL);
+ g_list_free(file_iterlist);
+ return FALSE;
+ }
+ }
+
+ //ET_Debug_Print_File_List (NULL, __FILE__, __LINE__, __FUNCTION__);
+
+ for (row=0; row < rows_to_loop; row++)
+ {
+ if (CddbTrackList_Line_Selected == FALSE)
+ {
+ if(row == 0)
+ currentPath = gtk_tree_path_new_first();
+ else
+ gtk_tree_path_next(currentPath);
+ } else /* (e.g.: if CddbTrackList_Line_Selected == TRUE) */
+ {
+ if(row == 0)
+ {
+ selectedrows = g_list_first(selectedrows);
+ currentPath = (GtkTreePath *)selectedrows->data;
+ } else
+ {
+ selectedrows = g_list_next(selectedrows);
+ currentPath = (GtkTreePath *)selectedrows->data;
+ }
+ }
+
+ if (gtk_tree_model_get_iter(GTK_TREE_MODEL(CddbTrackListModel), &currentIter, currentPath))
+ gtk_tree_model_get(GTK_TREE_MODEL(CddbTrackListModel), &currentIter, CDDB_TRACK_LIST_DATA, &cddbtrackalbum, -1);
+
+ // Set values in the ETFile
+ if (CDDB_USE_DLM)
+ {
+ // RQ : this part is ~ equal to code for '!CDDB_USE_DLM', but uses '*etfile' instead of 'etfile'
+ ET_File **etfile = NULL;
+ File_Name *FileName = NULL;
+ File_Tag *FileTag = NULL;
+
+ gtk_tree_model_get(GTK_TREE_MODEL(CddbTrackListModel), &currentIter,
+ CDDB_TRACK_LIST_ETFILE, &etfile, -1);
+
+ /*
+ * Tag fields
+ */
+ if (cddbsettoallfields || cddbsettotitle || cddbsettotitle
+ || cddbsettoartist || cddbsettoalbum || cddbsettoyear
+ || cddbsettotrack || cddbsettotracktotal || cddbsettogenre)
+ {
+ // Allocation of a new FileTag
+ FileTag = ET_File_Tag_Item_New();
+ ET_Copy_File_Tag_Item(*etfile,FileTag);
+
+ if (cddbsettoallfields || cddbsettotitle)
+ ET_Set_Field_File_Tag_Item(&FileTag->title,cddbtrackalbum->track_name);
+
+ if ( (cddbsettoallfields || cddbsettoartist) && cddbtrackalbum->cddbalbum->artist)
+ ET_Set_Field_File_Tag_Item(&FileTag->artist,cddbtrackalbum->cddbalbum->artist);
+
+ if ( (cddbsettoallfields || cddbsettoalbum) && cddbtrackalbum->cddbalbum->album)
+ ET_Set_Field_File_Tag_Item(&FileTag->album, cddbtrackalbum->cddbalbum->album);
+
+ if ( (cddbsettoallfields || cddbsettoyear) && cddbtrackalbum->cddbalbum->year)
+ ET_Set_Field_File_Tag_Item(&FileTag->year, cddbtrackalbum->cddbalbum->year);
+
+ if (cddbsettoallfields || cddbsettotrack)
+ {
+ if (NUMBER_TRACK_FORMATED) snprintf(buffer,sizeof(buffer),"%.*d",NUMBER_TRACK_FORMATED_SPIN_BUTTON,cddbtrackalbum->track_number);
+ else snprintf(buffer,sizeof(buffer),"%d", cddbtrackalbum->track_number);
+ ET_Set_Field_File_Tag_Item(&FileTag->track,buffer);
+ }
+
+ if (cddbsettoallfields || cddbsettotracktotal)
+ {
+ if (NUMBER_TRACK_FORMATED) snprintf(buffer,sizeof(buffer),"%.*d",NUMBER_TRACK_FORMATED_SPIN_BUTTON,list_length);
+ else snprintf(buffer,sizeof(buffer),"%d", list_length);
+ ET_Set_Field_File_Tag_Item(&FileTag->track_total,buffer);
+ }
+
+ if ( (cddbsettoallfields || cddbsettogenre) && (cddbtrackalbum->cddbalbum->genre || cddbtrackalbum->cddbalbum->category) )
+ {
+ if (cddbtrackalbum->cddbalbum->genre && g_utf8_strlen(cddbtrackalbum->cddbalbum->genre, -1)>0)
+ ET_Set_Field_File_Tag_Item(&FileTag->genre,Cddb_Get_Id3_Genre_From_Cddb_Genre(cddbtrackalbum->cddbalbum->genre));
+ else
+ ET_Set_Field_File_Tag_Item(&FileTag->genre,Cddb_Get_Id3_Genre_From_Cddb_Genre(cddbtrackalbum->cddbalbum->category));
+ }
+ }
+
+ /*
+ * Filename field
+ */
+ if ( (cddbsettoallfields || cddbsettofilename) )
+ {
+ gchar *filename_generated_utf8;
+ gchar *filename_new_utf8;
+
+ // Allocation of a new FileName
+ FileName = ET_File_Name_Item_New();
+
+ // Build the filename with the path
+ if (NUMBER_TRACK_FORMATED) snprintf(buffer,sizeof(buffer),"%.*d",NUMBER_TRACK_FORMATED_SPIN_BUTTON,cddbtrackalbum->track_number);
+ else snprintf(buffer,sizeof(buffer),"%d", cddbtrackalbum->track_number);
+
+ filename_generated_utf8 = g_strconcat(buffer," - ",cddbtrackalbum->track_name,NULL);
+ ET_File_Name_Convert_Character(filename_generated_utf8); // Replace invalid characters
+ filename_new_utf8 = ET_File_Name_Generate(*etfile,filename_generated_utf8);
+
+ ET_Set_Filename_File_Name_Item(FileName,filename_new_utf8,NULL);
+
+ g_free(filename_generated_utf8);
+ g_free(filename_new_utf8);
+ }
+
+ ET_Manage_Changes_Of_File_Data(*etfile,FileName,FileTag);
+
+ // Then run current scanner if asked...
+ if (ScannerWindow && gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CddbRunScanner)) )
+ Scan_Select_Mode_And_Run_Scanner(*etfile);
+
+ } else if (cddbtrackalbum && file_iterlist && file_iterlist->data)
+ {
+ ET_File *etfile;
+ File_Name *FileName = NULL;
+ File_Tag *FileTag = NULL;
+
+ fileIter = (GtkTreeIter*) file_iterlist->data;
+ etfile = Browser_List_Get_ETFile_From_Iter(fileIter);
+
+ /*
+ * Tag fields
+ */
+ if (cddbsettoallfields || cddbsettotitle || cddbsettotitle
+ || cddbsettoartist || cddbsettoalbum || cddbsettoyear
+ || cddbsettotrack || cddbsettotracktotal || cddbsettogenre)
+ {
+ // Allocation of a new FileTag
+ FileTag = ET_File_Tag_Item_New();
+ ET_Copy_File_Tag_Item(etfile,FileTag);
+
+ if (cddbsettoallfields || cddbsettotitle)
+ ET_Set_Field_File_Tag_Item(&FileTag->title,cddbtrackalbum->track_name);
+
+ if ( (cddbsettoallfields || cddbsettoartist) && cddbtrackalbum->cddbalbum->artist)
+ ET_Set_Field_File_Tag_Item(&FileTag->artist,cddbtrackalbum->cddbalbum->artist);
+
+ if ( (cddbsettoallfields || cddbsettoalbum) && cddbtrackalbum->cddbalbum->album)
+ ET_Set_Field_File_Tag_Item(&FileTag->album, cddbtrackalbum->cddbalbum->album);
+
+ if ( (cddbsettoallfields || cddbsettoyear) && cddbtrackalbum->cddbalbum->year)
+ ET_Set_Field_File_Tag_Item(&FileTag->year, cddbtrackalbum->cddbalbum->year);
+
+ if (cddbsettoallfields || cddbsettotrack)
+ {
+ if (NUMBER_TRACK_FORMATED) snprintf(buffer,sizeof(buffer),"%.*d",NUMBER_TRACK_FORMATED_SPIN_BUTTON,cddbtrackalbum->track_number);
+ else snprintf(buffer,sizeof(buffer),"%d", cddbtrackalbum->track_number);
+ ET_Set_Field_File_Tag_Item(&FileTag->track,buffer);
+ }
+
+ if (cddbsettoallfields || cddbsettotracktotal)
+ {
+ if (NUMBER_TRACK_FORMATED) snprintf(buffer,sizeof(buffer),"%.*d",NUMBER_TRACK_FORMATED_SPIN_BUTTON,list_length);
+ else snprintf(buffer,sizeof(buffer),"%d", list_length);
+ ET_Set_Field_File_Tag_Item(&FileTag->track_total,buffer);
+ }
+
+ if ( (cddbsettoallfields || cddbsettogenre) && (cddbtrackalbum->cddbalbum->genre || cddbtrackalbum->cddbalbum->category) )
+ {
+ if (cddbtrackalbum->cddbalbum->genre && g_utf8_strlen(cddbtrackalbum->cddbalbum->genre, -1)>0)
+ ET_Set_Field_File_Tag_Item(&FileTag->genre,Cddb_Get_Id3_Genre_From_Cddb_Genre(cddbtrackalbum->cddbalbum->genre));
+ else
+ ET_Set_Field_File_Tag_Item(&FileTag->genre,Cddb_Get_Id3_Genre_From_Cddb_Genre(cddbtrackalbum->cddbalbum->category));
+ }
+ }
+
+ /*
+ * Filename field
+ */
+ if ( (cddbsettoallfields || cddbsettofilename) )
+ {
+ gchar *filename_generated_utf8;
+ gchar *filename_new_utf8;
+
+ // Allocation of a new FileName
+ FileName = ET_File_Name_Item_New();
+
+ // Build the filename with the path
+ if (NUMBER_TRACK_FORMATED) snprintf(buffer,sizeof(buffer),"%.*d",NUMBER_TRACK_FORMATED_SPIN_BUTTON,cddbtrackalbum->track_number);
+ else snprintf(buffer,sizeof(buffer),"%d", cddbtrackalbum->track_number);
+
+ filename_generated_utf8 = g_strconcat(buffer," - ",cddbtrackalbum->track_name,NULL);
+ ET_File_Name_Convert_Character(filename_generated_utf8); // Replace invalid characters
+ filename_new_utf8 = ET_File_Name_Generate(etfile,filename_generated_utf8);
+
+ ET_Set_Filename_File_Name_Item(FileName,filename_new_utf8,NULL);
+
+ g_free(filename_generated_utf8);
+ g_free(filename_new_utf8);
+ }
+
+ ET_Manage_Changes_Of_File_Data(etfile,FileName,FileTag);
+
+ // Then run current scanner if asked...
+ if (ScannerWindow && gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CddbRunScanner)) )
+ Scan_Select_Mode_And_Run_Scanner(etfile);
+ }
+
+ if(!file_iterlist->next) break;
+ file_iterlist = file_iterlist->next;
+ }
+
+ g_list_foreach(file_iterlist, (GFunc)g_free, NULL);
+ g_list_free(file_iterlist);
+
+ Browser_List_Refresh_Whole_List();
+ ET_Display_File_Data_To_UI(ETCore->ETFileDisplayed);
+
+ return TRUE;
+}
+
+
+void Cddb_Display_Red_Lines_In_Result (void)
+{
+ if (!CddbDisplayRedLinesButton) return;
+
+ if ( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CddbDisplayRedLinesButton)) )
+ {
+ // Show only red lines
+ Cddb_Load_Album_List(TRUE);
+ }else
+ {
+ // Show all lines
+ Cddb_Load_Album_List(FALSE);
+ }
+}
+
+
+/*
+ * Returns the corresponding ID3 genre (the name, not the value)
+ */
+gchar *Cddb_Get_Id3_Genre_From_Cddb_Genre (gchar *cddb_genre)
+{
+ guint i;
+
+ if (!cddb_genre) return "";
+
+ for (i=0; i<=CDDB_GENRE_MAX; i++)
+ if (strcasecmp(cddb_genre,cddb_genre_vs_id3_genre[i][0])==0)
+ return cddb_genre_vs_id3_genre[i][1];
+ return cddb_genre;
+}
+
+/* Pixmaps */
+#include "../pixmaps/freedb.xpm"
+#include "../pixmaps/gnudb.xpm"
+#include "../pixmaps/musicbrainz.xpm"
+//#include "../pixmaps/closed_folder.xpm"
+
+/*
+ * Returns the pixmap to display following the server name
+ */
+GdkPixbuf *Cddb_Get_Pixbuf_From_Server_Name (gchar *server_name)
+{
+ if (!server_name)
+ return NULL;
+ else if (strstr((const gchar *)server_name,"freedb.org"))
+ return gdk_pixbuf_new_from_xpm_data(freedb_xpm);
+ else if (strstr((const gchar *)server_name,"gnudb.org"))
+ return gdk_pixbuf_new_from_xpm_data(gnudb_xpm);
+ else if (strstr((const gchar *)server_name,"musicbrainz.org"))
+ return gdk_pixbuf_new_from_xpm_data(musicbrainz_xpm);
+ else if (strstr((const gchar *)server_name,"/"))
+ //return gdk_pixbuf_new_from_xpm_data(closed_folder_xpm);
+ return NULL;
+ else
+ return NULL;
+}
+
+
+/*
+ * Function taken from gFTP.
+ * The standard to Base64 encoding can be found in RFC2045
+ */
+char *base64_encode (char *str)
+{
+ char *newstr, *newpos, *fillpos, *pos;
+ unsigned char table[64], encode[3];
+ int i, num;
+
+ for (i = 0; i < 26; i++)
+ {
+ table[i] = 'A' + i;
+ table[i + 26] = 'a' + i;
+ }
+
+ for (i = 0; i < 10; i++)
+ table[i + 52] = '0' + i;
+
+ table[62] = '+';
+ table[63] = '/';
+
+ num = strlen (str) / 3;
+ if (strlen (str) % 3 > 0)
+ num++;
+ newstr = g_malloc (num * 4 + 1);
+ newstr[num * 4] = '\0';
+ newpos = newstr;
+
+ pos = str;
+ while (*pos != '\0')
+ {
+ memset (encode, 0, sizeof (encode));
+ for (i = 0; i < 3 && *pos != '\0'; i++)
+ encode[i] = *pos++;
+
+ fillpos = newpos;
+ *newpos++ = table[encode[0] >> 2];
+ *newpos++ = table[(encode[0] & 3) << 4 | encode[1] >> 4];
+ *newpos++ = table[(encode[1] & 0xF) << 2 | encode[2] >> 6];
+ *newpos++ = table[encode[2] & 0x3F];
+ while (i < 3)
+ fillpos[++i] = '=';
+ }
+ return (newstr);
+}
+
+gchar *Cddb_Format_Proxy_Authentification (void)
+{
+ gchar *tempstr;
+ gchar *str;
+ gchar *ret;
+
+ if (CDDB_USE_PROXY && CDDB_PROXY_USER_NAME != NULL && *CDDB_PROXY_USER_NAME != '\0')
+ {
+ tempstr = g_strconcat(CDDB_PROXY_USER_NAME, ":", CDDB_PROXY_USER_PASSWORD, NULL);
+ str = base64_encode(tempstr);
+
+ ret = g_strdup_printf("Proxy-authorization: Basic %s\r\n", str);
+ g_free (str);
+ }else
+ {
+ ret = g_strdup("");
+ }
+ return ret;
+}
diff --git a/src/cddb.h b/src/cddb.h
new file mode 100644
index 0000000..9bcfd69
--- /dev/null
+++ b/src/cddb.h
@@ -0,0 +1,130 @@
+/* cddb.h - 2002/09/15 */
+/*
+ * EasyTAG - Tag editor for MP3 and Ogg Vorbis files
+ * Copyright (C) 2000-2003 Jerome Couderc <easytag@gmail.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef __CDDB_H__
+#define __CDDB_H__
+
+
+/****************
+ * Declarations *
+ ****************/
+
+GtkWidget *CddbWindow;
+GtkWidget *CddbWindowHPaned;
+
+GtkWidget *CddbSearchInAllFields;
+GtkWidget *CddbSearchInArtistField;
+GtkWidget *CddbSearchInTitleField;
+GtkWidget *CddbSearchInTrackNameField;
+GtkWidget *CddbSearchInOtherField;
+
+GtkWidget *CddbSeparatorH;
+GtkWidget *CddbSeparatorV;
+GtkWidget *CddbShowCategoriesButton;
+
+GtkWidget *CddbSearchInAllCategories;
+GtkWidget *CddbSearchInBluesCategory;
+GtkWidget *CddbSearchInClassicalCategory;
+GtkWidget *CddbSearchInCountryCategory;
+GtkWidget *CddbSearchInFolkCategory;
+GtkWidget *CddbSearchInJazzCategory;
+GtkWidget *CddbSearchInMiscCategory;
+GtkWidget *CddbSearchInNewageCategory;
+GtkWidget *CddbSearchInReggaeCategory;
+GtkWidget *CddbSearchInRockCategory;
+GtkWidget *CddbSearchInSoundtrackCategory;
+
+GtkWidget *CddbSetToAllFields;
+GtkWidget *CddbSetToTitle;
+GtkWidget *CddbSetToArtist;
+GtkWidget *CddbSetToAlbum;
+GtkWidget *CddbSetToYear;
+GtkWidget *CddbSetToTrack;
+GtkWidget *CddbSetToTrackTotal;
+GtkWidget *CddbSetToGenre;
+GtkWidget *CddbSetToFileName;
+
+GtkWidget *CddbRunScanner;
+GtkWidget *CddbUseDLM2; // '2' as also used in the prefs.c
+GtkWidget *CddbUseLocalAccess;
+
+
+
+/*
+ * Structure used for each item of the album list. Aslo attached to each row of the album list
+ */
+typedef struct _CddbAlbum CddbAlbum;
+struct _CddbAlbum
+{
+ gchar *server_name; /* Remote access : Param of server name used for the connection - Local access : NULL */
+ gint server_port; /* Remote access : Param of server port used for the connection - Local access : 0 */
+ gchar *server_cgi_path; /* Remote access : Param of server cgi path used for the connection - Local access : discid file path */
+
+ GdkPixbuf *bitmap; /* Pixmap corresponding to the server */
+
+ gchar *artist_album; /* CDDB artist+album (allocated) */
+ gchar *category; /* CDDB genre (allocated) */
+ gchar *id; /* example : 8d0de30c (allocated) */
+ GList *track_list; /* List of tracks name of the album (list of CddbTrackAlbum items) */
+ gboolean other_version; /* To TRUE if this album is an other version of the previous one */
+
+ // We fill these data when loading the track list
+ gchar *artist; /* (allocated) */
+ gchar *album; /* (allocated) */
+ gchar *genre; /* (allocated) */
+ gchar *year; /* (allocated) */
+ guint duration;
+};
+
+
+/*
+ * Structure used for each item of the track_list of the CddbAlbum structure.
+ */
+typedef struct _CddbTrackAlbum CddbTrackAlbum;
+struct _CddbTrackAlbum
+{
+ guint track_number;
+ gchar *track_name; /* (allocated) */
+ guint duration;
+ CddbAlbum *cddbalbum; /* Pointer to the parent CddbAlbum structure (to access quickly to album properties) */
+};
+
+
+typedef struct _CddbTrackFrameOffset CddbTrackFrameOffset;
+struct _CddbTrackFrameOffset
+{
+ gulong offset;
+};
+
+
+
+/**************
+ * Prototypes *
+ **************/
+
+void Init_CddbWindow (void);
+void Open_Cddb_Window (void);
+void Cddb_Popup_Menu_Search_Selected_File (void);
+void Cddb_Window_Apply_Changes (void);
+
+
+
+#endif /* __CDDB_H__ */
diff --git a/src/charset.c b/src/charset.c
new file mode 100755
index 0000000..74ecd9d
--- /dev/null
+++ b/src/charset.c
@@ -0,0 +1,824 @@
+/*
+ * Main part of code, written by:
+ *
+ * Copyright (C) 1999-2001 Håvard Kvålen <havardk@xmms.org>
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ */
+
+#include <config.h>
+#include <stdlib.h>
+#include <glib.h>
+#include <string.h>
+#include <errno.h>
+#include <glib/gi18n-lib.h>
+
+#ifdef HAVE_LANGINFO_CODESET
+#include <langinfo.h>
+#endif
+
+#include "charset.h"
+#include "setting.h"
+#include "log.h"
+
+#ifdef WIN32
+ #include "win32/win32dep.h"
+#endif
+
+
+/****************
+ * Declarations *
+ ****************/
+
+#define CHARSET_TRANS_ARRAY_LEN ( sizeof(charset_trans_array) / sizeof((charset_trans_array)[0]) )
+const CharsetInfo charset_trans_array[] = {
+ {N_("Arabic (IBM-864)"), "IBM864" },
+ {N_("Arabic (ISO-8859-6)"), "ISO-8859-6" },
+ {N_("Arabic (Windows-1256)"), "windows-1256" },
+ {N_("Baltic (ISO-8859-13)"), "ISO-8859-13" },
+ {N_("Baltic (ISO-8859-4)"), "ISO-8859-4" },
+ {N_("Baltic (Windows-1257)"), "windows-1257" },
+ {N_("Celtic (ISO-8859-14)"), "ISO-8859-14" },
+ {N_("Central European (IBM-852)"), "IBM852" },
+ {N_("Central European (ISO-8859-2)"), "ISO-8859-2" },
+ {N_("Central European (Windows-1250)"), "windows-1250" },
+ {N_("Chinese Simplified (GB18030)"), "gb18030" },
+ {N_("Chinese Simplified (GB2312)"), "GB2312" },
+ {N_("Chinese Traditional (Big5)"), "Big5" },
+ {N_("Chinese Traditional (Big5-HKSCS)"), "Big5-HKSCS" },
+ {N_("Cyrillic (IBM-855)"), "IBM855" },
+ {N_("Cyrillic (ISO-8859-5)"), "ISO-8859-5" },
+ {N_("Cyrillic (ISO-IR-111)"), "ISO-IR-111" },
+ {N_("Cyrillic (KOI8-R)"), "KOI8-R" },
+ {N_("Cyrillic (Windows-1251)"), "windows-1251" },
+ {N_("Cyrillic/Russian (CP-866)"), "IBM866" },
+ {N_("Cyrillic/Ukrainian (KOI8-U)"), "KOI8-U" },
+ {N_("English (US-ASCII)"), "us-ascii" },
+ {N_("Greek (ISO-8859-7)"), "ISO-8859-7" },
+ {N_("Greek (Windows-1253)"), "windows-1253" },
+ {N_("Hebrew (IBM-862)"), "IBM862" },
+ {N_("Hebrew (Windows-1255)"), "windows-1255" },
+ {N_("Japanese (EUC-JP)"), "EUC-JP" },
+ {N_("Japanese (ISO-2022-JP)"), "ISO-2022-JP" },
+ {N_("Japanese (Shift_JIS)"), "Shift_JIS" },
+ {N_("Korean (EUC-KR)"), "EUC-KR" },
+ {N_("Nordic (ISO-8859-10)"), "ISO-8859-10" },
+ {N_("South European (ISO-8859-3)"), "ISO-8859-3" },
+ {N_("Thai (TIS-620)"), "TIS-620" },
+ {N_("Turkish (IBM-857)"), "IBM857" },
+ {N_("Turkish (ISO-8859-9)"), "ISO-8859-9" },
+ {N_("Turkish (Windows-1254)"), "windows-1254" },
+ //{N_("Unicode (UTF-7)"), "UTF-7" },
+ {N_("Unicode (UTF-8)"), "UTF-8" },
+
+ //{N_("Unicode (UTF-16BE)"), "UTF-16BE" },
+ //{N_("Unicode (UTF-16LE)"), "UTF-16LE" },
+ //{N_("Unicode (UTF-32BE)"), "UTF-32BE" },
+ //{N_("Unicode (UTF-32LE)"), "UTF-32LE" },
+
+ {N_("Vietnamese (VISCII)"), "VISCII" },
+ {N_("Vietnamese (Windows-1258)"), "windows-1258" },
+ {N_("Visual Hebrew (ISO-8859-8)"), "ISO-8859-8" },
+ {N_("Western (IBM-850)"), "IBM850" },
+ {N_("Western (ISO-8859-1)"), "ISO-8859-1" },
+ {N_("Western (ISO-8859-15)"), "ISO-8859-15" },
+ {N_("Western (Windows-1252)"), "windows-1252" }
+
+ /*
+ * From this point, character sets aren't supported by iconv
+ */
+/* {N_("Arabic (IBM-864-I)"), "IBM864i" },
+ {N_("Arabic (ISO-8859-6-E)"), "ISO-8859-6-E" },
+ {N_("Arabic (ISO-8859-6-I)"), "ISO-8859-6-I" },
+ {N_("Arabic (MacArabic)"), "x-mac-arabic" },
+ {N_("Armenian (ARMSCII-8)"), "armscii-8" },
+ {N_("Central European (MacCE)"), "x-mac-ce" },
+ {N_("Chinese Simplified (GBK)"), "x-gbk" },
+ {N_("Chinese Simplified (HZ)"), "HZ-GB-2312" },
+ {N_("Chinese Traditional (EUC-TW)"), "x-euc-tw" },
+ {N_("Croatian (MacCroatian)"), "x-mac-croatian" },
+ {N_("Cyrillic (MacCyrillic)"), "x-mac-cyrillic" },
+ {N_("Cyrillic/Ukrainian (MacUkrainian)"), "x-mac-ukrainian" },
+ {N_("Farsi (MacFarsi)"), "x-mac-farsi"},
+ {N_("Greek (MacGreek)"), "x-mac-greek" },
+ {N_("Gujarati (MacGujarati)"), "x-mac-gujarati" },
+ {N_("Gurmukhi (MacGurmukhi)"), "x-mac-gurmukhi" },
+ {N_("Hebrew (ISO-8859-8-E)"), "ISO-8859-8-E" },
+ {N_("Hebrew (ISO-8859-8-I)"), "ISO-8859-8-I" },
+ {N_("Hebrew (MacHebrew)"), "x-mac-hebrew" },
+ {N_("Hindi (MacDevanagari)"), "x-mac-devanagari" },
+ {N_("Icelandic (MacIcelandic)"), "x-mac-icelandic" },
+ {N_("Korean (JOHAB)"), "x-johab" },
+ {N_("Korean (UHC)"), "x-windows-949" },
+ {N_("Romanian (MacRomanian)"), "x-mac-romanian" },
+ {N_("Turkish (MacTurkish)"), "x-mac-turkish" },
+ {N_("User Defined"), "x-user-defined" },
+ {N_("Vietnamese (TCVN)"), "x-viet-tcvn5712" },
+ {N_("Vietnamese (VPS)"), "x-viet-vps" },
+ {N_("Western (MacRoman)"), "x-mac-roman" },
+ // charsets whithout possibly translatable names
+ {"T61.8bit", "T61.8bit" },
+ {"x-imap4-modified-utf7", "x-imap4-modified-utf7"},
+ {"x-u-escaped", "x-u-escaped" },
+ {"windows-936", "windows-936" }
+*/
+};
+
+static GHashTable *encodings;
+
+
+
+/*************
+ * Functions *
+ *************/
+
+
+/* stolen from gnome-desktop-item.c */
+static gboolean
+check_locale (const char *locale)
+{
+ GIConv cd = g_iconv_open ("UTF-8", locale);
+ if ((GIConv)-1 == cd)
+ return FALSE;
+ g_iconv_close (cd);
+ return TRUE;
+}
+
+/* stolen from gnome-desktop-item.c */
+static void
+insert_locales (GHashTable *encodings, char *enc, ...)
+{
+ va_list args;
+ char *s;
+
+ va_start (args, enc);
+ for (;;) {
+ s = va_arg (args, char *);
+ if (s == NULL)
+ break;
+ g_hash_table_insert (encodings, s, enc);
+ }
+ va_end (args);
+}
+
+/* stolen from gnome-desktop-item.c */
+/* make a standard conversion table from the desktop standard spec */
+void
+Charset_Insert_Locales_Init (void)
+{
+ encodings = g_hash_table_new (g_str_hash, g_str_equal);
+
+ /* "C" is plain ascii */
+ insert_locales (encodings, "ASCII", "C", NULL);
+#if WIN32
+ insert_locales (encodings, "windows-1256", "ar", NULL); // 2006.12.31 - For testing with Arabic
+#else
+ insert_locales (encodings, "ISO-8859-6", "ar", NULL);
+#endif
+ insert_locales (encodings, "ARMSCII-8", "by", NULL);
+ insert_locales (encodings, "BIG5", "zh_TW", NULL);
+ insert_locales (encodings, "CP1251", "be", "bg", NULL);
+ if (check_locale ("EUC-CN")) {
+ insert_locales (encodings, "EUC-CN", "zh_CN", NULL);
+ } else {
+ insert_locales (encodings, "GB2312", "zh_CN", NULL);
+ }
+ insert_locales (encodings, "EUC-JP", "ja", NULL);
+ insert_locales (encodings, "EUC-KR", "ko", NULL);
+ /*insert_locales (encodings, "GEORGIAN-ACADEMY", NULL);*/
+ insert_locales (encodings, "GEORGIAN-PS", "ka", NULL);
+ insert_locales (encodings, "ISO-8859-1", "br", "ca", "da", "de", "en", "es", "eu", "fi", "fr", "gl", "it", "nl", "wa", "no", "pt", "pt", "sv", NULL);
+#if WIN32
+ insert_locales (encodings, "windows-1250", "cs", "hr", "hu", "pl", "ro", "sk", "sl", "sq", "sr", NULL);
+#else
+ insert_locales (encodings, "ISO-8859-2", "cs", "hr", "hu", "pl", "ro", "sk", "sl", "sq", "sr", NULL);
+#endif
+ insert_locales (encodings, "ISO-8859-3", "eo", NULL);
+ insert_locales (encodings, "ISO-8859-5", "mk", "sp", NULL);
+#if WIN32
+ insert_locales (encodings, "windows-1253", "el", NULL);
+#else
+ insert_locales (encodings, "ISO-8859-7", "el", NULL);
+#endif
+#if WIN32
+ insert_locales (encodings, "windows-1254", "tr", NULL);
+#else
+ insert_locales (encodings, "ISO-8859-9", "tr", NULL);
+#endif
+ insert_locales (encodings, "ISO-8859-13", "lt", "lv", "mi", NULL);
+ insert_locales (encodings, "ISO-8859-14", "ga", "cy", NULL);
+ insert_locales (encodings, "ISO-8859-15", "et", NULL);
+#if WIN32
+ insert_locales (encodings, "windows-1251", "ru", NULL);
+#else
+ insert_locales (encodings, "KOI8-R", "ru", NULL);
+#endif
+ insert_locales (encodings, "KOI8-U", "uk", NULL);
+ if (check_locale ("TCVN-5712")) {
+ insert_locales (encodings, "TCVN-5712", "vi", NULL);
+ } else {
+ insert_locales (encodings, "TCVN", "vi", NULL);
+ }
+ insert_locales (encodings, "TIS-620", "th", NULL);
+#if WIN32
+ insert_locales (encodings, "windows-1255", "he", NULL);
+#endif
+ /*insert_locales (encodings, "VISCII", NULL);*/
+}
+
+void
+Charset_Insert_Locales_Destroy (void)
+{
+ g_hash_table_destroy (encodings);
+}
+
+/* stolen from gnome-desktop-item.c */
+const char *
+get_encoding_from_locale (const char *locale)
+{
+ char lang[3];
+ const char *encoding;
+
+ if (locale == NULL)
+ return NULL;
+
+ /* if locale includes encoding, use it *//*
+ encoding = strchr (locale, '.');
+ if (encoding != NULL) {
+ return encoding+1;
+ }*/
+ /* if locale includes encoding (that isn't UTF-8), use it */
+ encoding = strchr (locale, '.');
+ if (encoding != NULL && strncmp (encoding, ".UTF-8", 6)) {
+ return encoding+1;
+ }
+
+ /* first try the entire locale (at this point ll_CC) */
+ encoding = g_hash_table_lookup (encodings, locale);
+ if (encoding != NULL)
+ return encoding;
+
+ /* Try just the language */
+ strncpy (lang, locale, 2);
+ lang[2] = '\0';
+ return g_hash_table_lookup (encodings, lang);
+}
+
+
+/*
+ * Return the locale from LANG if exists, else from LC_ALL
+ *
+ * http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap08.html#tag_08_02
+ *
+ * LANG
+ * This variable shall determine the locale category for native language,
+ * local customs, and coded character set in the absence of the LC_ALL and
+ * other LC_* ( LC_COLLATE , LC_CTYPE , LC_MESSAGES , LC_MONETARY , LC_NUMERIC ,
+ * LC_TIME ) environment variables. This can be used by applications to
+ * determine the language to use for error messages and instructions, collating
+ * sequences, date formats, and so on.
+ * LC_ALL
+ * This variable shall determine the values for all locale categories. The
+ * value of the LC_ALL environment variable has precedence over any of the
+ * other environment variables starting with LC_ ( LC_COLLATE , LC_CTYPE ,
+ * LC_MESSAGES , LC_MONETARY , LC_NUMERIC , LC_TIME ) and the LANG environment
+ * variable.
+ * LC_COLLATE
+ * This variable shall determine the locale category for character collation.
+ * It determines collation information for regular expressions and sorting,
+ * including equivalence classes and multi-character collating elements, in
+ * various utilities and the strcoll() and strxfrm() functions. Additional
+ * semantics of this variable, if any, are implementation-defined.
+ * LC_CTYPE
+ * This variable shall determine the locale category for character handling
+ * functions, such as tolower(), toupper(), and isalpha(). This environment
+ * variable determines the interpretation of sequences of bytes of text data
+ * as characters (for example, single as opposed to multi-byte characters),
+ * the classification of characters (for example, alpha, digit, graph), and
+ * the behavior of character classes. Additional semantics of this variable,
+ * if any, are implementation-defined.
+ * LC_MESSAGES
+ * This variable shall determine the locale category for processing affirmative
+ * and negative responses and the language and cultural conventions in which
+ * messages should be written. [XSI] [Option Start] It also affects the behavior
+ * of the catopen() function in determining the message catalog. [Option End]
+ * Additional semantics of this variable, if any, are implementation-defined.
+ * The language and cultural conventions of diagnostic and informative messages
+ * whose format is unspecified by IEEE Std 1003.1-2001 should be affected by
+ * the setting of LC_MESSAGES .
+ * LC_MONETARY
+ * This variable shall determine the locale category for monetary-related
+ * numeric formatting information. Additional semantics of this variable, if
+ * any, are implementation-defined.
+ * LC_NUMERIC
+ * This variable shall determine the locale category for numeric formatting
+ * (for example, thousands separator and radix character) information in
+ * various utilities as well as the formatted I/O operations in printf() and
+ * scanf() and the string conversion functions in strtod(). Additional semantics
+ * of this variable, if any, are implementation-defined.
+ * LC_TIME
+ * This variable shall determine the locale category for date and time formatting
+ * information. It affects the behavior of the time functions in strftime().
+ * Additional semantics of this variable, if any, are implementation-defined.
+ *
+ *
+ * The values of locale categories shall be determined by a precedence order; the
+ * first condition met below determines the value:
+ *
+ * 1. If the LC_ALL environment variable is defined and is not null, the value
+ * of LC_ALL shall be used.
+ * 2. If the LC_* environment variable ( LC_COLLATE , LC_CTYPE , LC_MESSAGES ,
+ * LC_MONETARY , LC_NUMERIC , LC_TIME ) is defined and is not null, the value
+ * of the environment variable shall be used to initialize the category that
+ * corresponds to the environment variable.
+ * 3. If the LANG environment variable is defined and is not null, the value of
+ * the LANG environment variable shall be used.
+ * 4. If the LANG environment variable is not set or is set to the empty string,
+ * the implementation-defined default locale shall be used.
+ *
+ */
+const gchar *get_locale (void)
+{
+ if (g_getenv("LC_ALL"))
+ return g_getenv("LC_ALL");
+
+ else if (g_getenv("LC_CTYPE"))
+ return g_getenv("LC_CTYPE");
+
+ else if (g_getenv("LANG"))
+ return g_getenv("LANG");
+
+ else
+ return NULL;
+}
+
+
+
+
+/*
+ * convert_string : (don't use with UTF-16 strings)
+ * - display_error : if TRUE, may return an escaped string and display an error
+ * message (if conversion fails).
+ */
+gchar *convert_string (const gchar *string, const gchar *from_codeset,
+ const gchar *to_codeset, const gboolean display_error)
+{
+ return convert_string_1(string, -1, from_codeset, to_codeset, display_error);
+}
+/* Length must be passed, as the string might be Unicode, in which case we can't
+ * count zeroes (see strlen call below). */
+gchar *convert_string_1 (const gchar *string, gssize length, const gchar *from_codeset,
+ const gchar *to_codeset, const gboolean display_error)
+{
+ gchar *output;
+ GError *error = NULL;
+ gsize bytes_written;
+
+ if (!string)
+ return NULL;
+
+ output = g_convert(string, length, to_codeset, from_codeset, NULL, &bytes_written, &error);
+ //output = g_convert_with_fallback(string, length, to_codeset, from_codeset, "?", NULL, &bytes_written, &error);
+
+ if (output == NULL)
+ {
+ gchar *escaped_str = g_strescape(string, NULL);
+ if (display_error)
+ {
+ Log_Print("convert_string(): Failed conversion from charset '%s' to '%s'. "
+ "String '%s'. Errcode %d (%s).\n",
+ from_codeset, to_codeset, escaped_str, error->code, error->message);
+ }
+ g_free(escaped_str);
+ g_error_free(error);
+ // Return the input string without converting it. If the string is
+ // displayed in the UI, it must be in UTF-8!
+ if ( (g_ascii_strcasecmp(to_codeset, "UTF-8"))
+ || (g_utf8_validate(string, -1, NULL)) )
+ {
+ return g_strdup(string);
+ }
+ }else
+ {
+ // Patch from Alexey Illarionov:
+ // g_convert returns null-terminated string only with one \0 at the
+ // end. It can cause some garbage at the end of a string for UTF-16.
+ // The second \0 should be set manually.
+ output = g_realloc(output, bytes_written + 2);
+ if (output != NULL)
+ output[bytes_written] = output[bytes_written + 1] = 0;
+ }
+
+ //g_print("from %s => len: %d, string: '%s'\n (%x %x %x %x %x %x %x %x)\n",from_codeset,length,string,string[0],string[1],string[2],string[3],string[4],string[5],string[6],string[7]);
+ //g_print("to %s => len: %d, output: '%s'\n (%x %x %x %x %x %x %x %x)\n\n",to_codeset,bytes_written+2,output,output[0],output[1],output[2],output[3],output[4],output[5],output[6],output[7]);
+
+ return output;
+}
+
+
+/*
+ * Conversion with UTF-8 for Ogg Vorbis and FLAC tags (current_charset <===> UTF-8)
+ */
+gchar *convert_to_utf8 (const gchar *string)
+{
+ gchar *output;
+ GError *error = NULL;
+
+ if (!string)
+ return NULL;
+
+ output = g_locale_to_utf8(string, -1, NULL, NULL, &error);
+
+ if (output == NULL)
+ {
+ const gchar *usercharset;
+ gchar *escaped_str = g_strescape(string, NULL);
+ g_get_charset(&usercharset);
+ Log_Print("convert_to_utf8(): Failed conversion from charset '%s'. "
+ "String '%s'. Errcode %d (%s).\n",
+ usercharset, escaped_str, error->code, error->message);
+ g_free(escaped_str);
+
+ if (g_utf8_validate(string, -1, NULL))
+ Log_Print("convert_to_utf8(): String was valid UTF-8.\n");
+ else
+ Log_Print("convert_to_utf8(): String was INVALID UTF-8.\n");
+
+ g_error_free(error);
+ return g_strdup(string);
+ }
+
+ return output;
+}
+
+gchar *convert_from_utf8 (const char *string)
+{
+ gchar *output;
+ GError *error = NULL;
+
+ if (!string)
+ return NULL;
+
+ output = g_locale_from_utf8(string, -1, NULL, NULL, &error);
+
+ if (output == NULL)
+ {
+ const gchar *usercharset;
+ gchar *escaped_str = g_strescape(string, NULL);
+ g_get_charset(&usercharset);
+ Log_Print("convert_from_utf8(): Failed conversion to charset '%s'. "
+ "String '%s'. Errcode %d (%s).\n",
+ usercharset, escaped_str, error->code, error->message);
+ g_free(escaped_str);
+
+ if (g_utf8_validate(string, -1, NULL))
+ Log_Print("convert_from_utf8(): String was valid UTF-8.\n");
+ else
+ Log_Print("convert_from_utf8(): String was INVALID UTF-8.\n");
+
+ g_error_free(error);
+ return g_strdup(string);
+ }
+
+ return output;
+}
+
+
+
+/*
+ * Convert a string from the filename system encoding to UTF-8.
+ * - conversion OK : returns the UTF-8 string (new allocated)
+ * - conversion KO : tries others encodings else returns an 'escaped' string
+ */
+gchar *filename_to_display (const gchar *string)
+{
+ gchar *ret = NULL;
+ GError *error = NULL;
+
+ if (!string)
+ return NULL;
+
+ if (g_utf8_validate(string, -1, NULL))
+ {
+ // String already in UTF-8
+ ret = g_strdup(string);
+ }else
+ {
+ const gchar *char_encoding;
+
+ // Get encoding associated to the locale without using UTF-8 (ex , if LANG=fr_FR.UTF-8 it will return ISO-8859-1)
+ char_encoding = get_encoding_from_locale(get_locale());
+ if (char_encoding)
+ {
+ //g_print("> char_encoding: %s\n",char_encoding);
+ error = NULL;
+ ret = g_convert(string, -1, "UTF-8", char_encoding, NULL, NULL, &error);
+ }
+
+ if (!ret)
+ {
+ // Failing that, try ISO-8859-1
+ error = NULL;
+ ret = g_convert(string, -1, "UTF-8", "ISO-8859-1", NULL, NULL, &error);
+ }
+
+ if (!ret)
+ {
+ gchar *escaped_str = g_strescape(string, NULL);
+ Log_Print(_("The filename '%s' couldn't be converted into UTF-8 (%s).\n"),
+ escaped_str, error && error->message ? error->message : _("Invalid UTF-8"));
+ g_clear_error(&error);
+
+ ret = escaped_str;
+ }
+ }
+
+#ifdef WIN32
+ ET_Win32_Path_Remove_Trailing_Slash(ret);
+ ET_Win32_Path_Replace_Slashes(ret);
+#endif
+
+ return ret;
+}
+
+/*
+ * Convert a string from UTF-8 to the filename system encoding.
+ * - conversion OK : returns the string in filename system encoding (new allocated)
+ * - conversion KO : display error message + returns nothing!
+ */
+gchar *filename_from_display (const gchar *string)
+{
+ GError *error = NULL;
+ gchar *ret = NULL;
+ const gchar *char_encoding = NULL;
+ //const gchar *filename_encoding = NULL;
+
+ if (!string) return NULL;
+
+ // Get system encoding from LANG if found (ex : fr_FR.UTF-8 => UTF-8)
+ if (get_locale())
+ char_encoding = strchr(get_locale(), '.');
+
+ if (char_encoding)
+ char_encoding = char_encoding+1; // Skip the '.'
+ if (char_encoding)
+ {
+ error = NULL;
+
+ if (FILENAME_CHARACTER_SET_OTHER)
+ {
+ ret = g_convert(string, -1, char_encoding, "UTF-8", NULL, NULL, &error);
+
+ }else if (FILENAME_CHARACTER_SET_APPROXIMATE)
+ {
+ // iconv_open (3):
+ // When the string "//TRANSLIT" is appended to tocode, transliteration
+ // is activated. This means that when a character cannot be represented
+ // in the target character set, it can be approximated through one or
+ // several similarly looking characters.
+ gchar *enc = g_strconcat(char_encoding, "//TRANSLIT", NULL);
+ ret = g_convert(string, -1, enc, "UTF-8", NULL, NULL, &error);
+ g_free(enc);
+
+ }else if (FILENAME_CHARACTER_SET_DISCARD)
+ {
+ // iconv_open (3):
+ // When the string "//IGNORE" is appended to tocode, characters that
+ // cannot be represented in the target character set will be silently
+ // discarded.
+ gchar *enc = g_strconcat(char_encoding, "//IGNORE", NULL);
+ ret = g_convert(string, -1, enc, "UTF-8", NULL, NULL, &error);
+ g_free(enc);
+ }
+ }
+
+ if (!ret)
+ {
+ // Get system encoding from locale in LANG if found (ex : fr_FR.UTF-8 => fr_FR => ISO-8859-1)
+ char_encoding = get_encoding_from_locale(get_locale());
+ if (char_encoding)
+ {
+ //g_print("> char_encoding: %s\n",char_encoding);
+ error = NULL;
+ ret = g_convert(string, -1, char_encoding, "UTF-8", NULL, NULL, &error);
+ }
+ }
+
+ if (!ret)
+ {
+ // Failing that, try ISO-8859-1
+ error = NULL;
+ ret = g_convert(string, -1, "ISO-8859-1", "UTF-8", NULL, NULL, &error);
+ }
+
+ if (!ret)
+ {
+ if (g_utf8_validate(string, -1, NULL))
+ {
+ // String already in UTF-8
+ ret = g_strdup(string);
+ }
+ }
+
+ if (!ret)
+ {
+ // Conversion KO!
+ gchar *escaped_str = g_strescape(string, NULL);
+ Log_Print(_("The UTF-8 string '%s' couldn't be converted into filename encoding (%s)\n"),
+ escaped_str, error && error->message ? error->message : _("Invalid UTF-8"));
+ g_clear_error(&error);
+
+ ret = escaped_str;
+ }
+
+#ifdef WIN32
+ //ET_Win32_Path_Replace_Backslashes(ret);
+#endif
+
+ return ret; // We need to catch errors (e.g. temp=NULL) in the real code
+}
+
+
+
+/*
+ * Function used when reading tags : we check if the string is valid UTF-8 (else
+ * it may cause problem in EasyTAG)
+ *
+ * Examples :
+ * - some Ogg Vorbis tags contain ISO-8859-1 characters instead of UTF-8).
+ * - some Flac tags may be probably encoded to ISO-8859-15 (by using for example
+ * "metaflac --no-utf8-convert ...") so we convert it from ISO-8859-1 to UTF-8.
+ *
+ * If not valid UTF-8, we try some conversion to try to get the correct string
+ * - conversion OK : returns the UTF-8 string (new allocated)
+ * - conversion KO : tries others encodings else returns an 'escaped' string
+ */
+gchar *Try_To_Validate_Utf8_String (const gchar *string)
+{
+ gchar *ret = NULL;
+ GError *error = NULL;
+
+ if (!string)
+ return NULL;
+
+ if (g_utf8_validate(string, -1, NULL))
+ {
+ // String already in UTF-8
+ ret = g_strdup(string);
+ }else
+ {
+ const gchar *char_encoding;
+
+ // Get encoding associated to the locale without using UTF-8 (ex , if LANG=fr_FR.UTF-8 it will return ISO-8859-1)
+ char_encoding = get_encoding_from_locale(get_locale());
+ if (char_encoding)
+ {
+ //g_print("> char_encoding: %s\n",char_encoding);
+ error = NULL;
+ ret = g_convert(string, -1, "UTF-8", char_encoding, NULL, NULL, &error);
+ }
+
+ if (!ret)
+ {
+ // Failing that, try ISO-8859-1
+ error = NULL;
+ ret = g_convert(string, -1, "UTF-8", "ISO-8859-1", NULL, NULL, &error);
+ }
+
+ if (!ret)
+ {
+ gchar *escaped_str = g_strescape(string, NULL);
+ Log_Print(_("The string '%s' couldn't be converted into UTF-8 (%s).\n"),
+ escaped_str, error && error->message ? error->message : _("Invalid UTF-8"));
+ g_clear_error(&error);
+
+ ret = escaped_str;
+ }
+ }
+
+ return ret;
+}
+
+
+
+void Charset_Populate_Combobox (GtkComboBox *combo, gchar *select_charset)
+{
+ guint i;
+
+ for (i=0; i<CHARSET_TRANS_ARRAY_LEN; i++)
+ {
+ gtk_combo_box_append_text(combo, _(charset_trans_array[i].charset_title));
+
+ if (select_charset && strcmp(charset_trans_array[i].charset_name, select_charset) == 0)
+ gtk_combo_box_set_active(combo, i);
+ }
+}
+
+
+/*
+ * Return charset_name from charset_title
+ */
+gchar *Charset_Get_Name_From_Title (const gchar *charset_title)
+{
+ guint i;
+
+ if (charset_title)
+ for (i=0; i<CHARSET_TRANS_ARRAY_LEN; i++)
+ if ( strcasecmp(_(charset_title),_(charset_trans_array[i].charset_title)) == 0 )
+ return charset_trans_array[i].charset_name;
+ return "";
+}
+
+
+/*
+ * Return charset_title from charset_name
+ */
+gchar *Charset_Get_Title_From_Name (gchar *charset_name)
+{
+ guint i;
+
+ if (charset_name)
+ for (i=0; i<CHARSET_TRANS_ARRAY_LEN; i++)
+ if ( strcasecmp(charset_name,charset_trans_array[i].charset_name) == 0 )
+ return _(charset_trans_array[i].charset_title);
+ return "";
+}
+
+
+
+/*
+ * Test if the conversion is supported between two character sets ('from' and 'to)
+ * (function called in the preferences window).
+ * Note : for UTF-16 (2 byte for each character) we make a special test...
+ */
+gboolean test_conversion_charset (const gchar *from, const gchar *to)
+{
+ gchar *temp;
+
+ if (!from || !to)
+ return FALSE;
+
+ // Do a quick test conversion and examine error output
+ if ( strcmp(from,"UTF-16BE") == 0 )
+ {
+ temp = convert_string_1("F\0O\0O\0\0\0", 6, from, to, FALSE);
+ }else if ( strcmp(from,"UTF-16LE") == 0 )
+ {
+ temp = convert_string_1("\0F\0O\0O\0\0", 6, from, to, FALSE);
+ }else
+ {
+ temp = convert_string("FOO", from, to, FALSE);
+ }
+
+ if (!temp)
+ {
+ /*// Error in conversion
+ if (error && error->code == G_CONVERT_ERROR_NO_CONVERSION)
+ {
+ Log_Print("Conversion error from '%s' to '%s' (G_CONVERT_ERROR_NO_CONVERSION)",from,to);
+ } else if (error && error->code == G_CONVERT_ERROR_ILLEGAL_SEQUENCE)
+ {
+ Log_Print("Conversion error from '%s' to '%s' (G_CONVERT_ERROR_ILLEGAL_SEQUENCE)",from,to);
+ } else if (error && error->code == G_CONVERT_ERROR_FAILED)
+ {
+ Log_Print("Conversion error from '%s' to '%s' (G_CONVERT_ERROR_FAILED)",from,to);
+ } else if (error && error->code == G_CONVERT_ERROR_PARTIAL_INPUT)
+ {
+ Log_Print("Conversion error from '%s' to '%s' (G_CONVERT_ERROR_PARTIAL_INPUT)",from,to);
+ } else if (error && error->code == G_CONVERT_ERROR_BAD_URI)
+ {
+ Log_Print("Conversion error from '%s' to '%s' (G_CONVERT_ERROR_BAD_URI)",from,to);
+ } else if (error && error->code == G_CONVERT_ERROR_NOT_ABSOLUTE_PATH)
+ {
+ Log_Print("Conversion error from '%s' to '%s' (G_CONVERT_ERROR_NOT_ABSOLUTE_PATH)",from,to);
+ } else
+ {
+ Log_Print("Conversion error from '%s' to '%s' (unknown : %d)",from,to,error->code);
+ }
+
+ if (error)
+ g_error_free(error);*/
+ return FALSE;
+ } else
+ {
+ /*// No error
+ if (error)
+ g_error_free(error);*/
+ g_free(temp);
+ return TRUE;
+ }
+}
diff --git a/src/charset.h b/src/charset.h
new file mode 100755
index 0000000..7613628
--- /dev/null
+++ b/src/charset.h
@@ -0,0 +1,72 @@
+/* charset.h - 2001/12/04 */
+/*
+ * EasyTAG - Tag editor for MP3 and Ogg Vorbis files
+ * Copyright (C) 2000-2003 Jerome Couderc <easytag@gmail.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef __CHARSET_H__
+#define __CHARSET_H__
+
+#include <gtk/gtk.h>
+
+/***************
+ * Declaration *
+ ***************/
+
+typedef struct
+{
+ gchar *charset_title;
+ gchar *charset_name;
+} CharsetInfo;
+
+/* translated charset titles */
+extern const CharsetInfo charset_trans_array[];
+
+
+
+/**************
+ * Prototypes *
+ **************/
+
+const char *get_encoding_from_locale (const char *locale);
+const gchar *get_locale (void);
+
+
+gchar *convert_string (const gchar *string, const gchar *from_codeset, const gchar *to_codeset, const gboolean display_error);
+gchar *convert_string_1 (const gchar *string, gssize length, const gchar *from_codeset, const gchar *to_codeset, const gboolean display_error);
+
+/* Used for Ogg Vorbis and FLAC tags */
+gchar *convert_to_utf8 (const gchar *string);
+gchar *convert_from_utf8 (const gchar *string);
+
+gchar *filename_to_display (const gchar *string);
+gchar *filename_from_display (const gchar *string);
+
+gchar *Try_To_Validate_Utf8_String (const gchar *string);
+
+void Charset_Populate_Combobox (GtkComboBox *combo, gchar *select_charset);
+gchar *Charset_Get_Name_From_Title (const gchar *charset_title);
+gchar *Charset_Get_Title_From_Name (gchar *charset_name);
+
+gboolean test_conversion_charset (const gchar *from, const gchar *to);
+
+void Charset_Insert_Locales_Init (void);
+void Charset_Insert_Locales_Destroy (void);
+
+
+#endif /* __CHARSET_H__ */
diff --git a/src/crc32.c b/src/crc32.c
new file mode 100755
index 0000000..bd68609
--- /dev/null
+++ b/src/crc32.c
@@ -0,0 +1,130 @@
+/* crc32.c - the crc check algorithm for cksfv modified by oliver for easytag
+
+ Copyright (C) 2000 Bryan Call <bc@fodder.org>
+ Copyright (C) 2003 Oliver Schinagl <oliver@are-b.org>
+
+ 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., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdio.h>
+#include "crc32.h"
+
+/*
+ * Initial crc32 function
+ * This function was renamed from crc32(...) to crc32_easytag(...) to avoid a
+ * strange problem with some gtk theme that may use the same name of function.
+ */
+int crc32_easytag(register int fd, unsigned long *main_val)
+{
+ char buf[BUFFERSIZE], *p;
+ int nr;
+ unsigned long crc = ~0, crc32_total = ~0;
+
+ while ((nr = read(fd, &buf, sizeof(buf))) > 0)
+ for (p = buf; nr--; ++p)
+ {
+ crc = (crc >> 8) ^ crctable[(crc ^ *p) & 0xff];
+ crc32_total = (crc >> 8) ^ crctable[(crc32_total ^ *p) & 0xff];
+ }
+ if (nr < 0)
+ return 1;
+
+ *main_val = ~crc;
+
+ return 0;
+}
+
+
+
+/*
+ * Calculate the CRC-32 value of audio data (doesn't read the ID3v2 and ID3v1 tags).
+ * Return 0 if OK
+ */
+int crc32_file_with_ID3_tag(char *filename, unsigned long *main_val)
+{
+ char buf[BUFFERSIZE], *p;
+ int nr;
+ unsigned long crc = ~0, crc32_total = ~0;
+ FILE *fd;
+ unsigned char tmp_id3[4];
+ int id3v2size = 0;
+ struct stat statbuf;
+ unsigned long size = 0;
+ int has_id3v1 = 0;
+
+
+ if (!filename)
+ return 1;
+
+ stat(filename,&statbuf);
+ size = statbuf.st_size;
+
+ if ( (fd = fopen(filename,"r"))==NULL )
+ return 1;
+
+ // Check if there is an ID3v1 tag...
+ fseek(fd, -128, SEEK_END);
+ if (fread(tmp_id3, 1, 3, fd) != 3)
+ return 1;
+ if (tmp_id3[0] == 'T' && tmp_id3[1] == 'A' && tmp_id3[2] == 'G')
+ has_id3v1 = 1;
+
+
+ // Check if there is an ID3v2 tag...
+ fseek(fd, 0L, SEEK_SET);
+ if (fread(tmp_id3, 1, 4, fd) != 4)
+ return 1;
+ // Calculate ID3v2 length
+ if (tmp_id3[0] == 'I' && tmp_id3[1] == 'D' && tmp_id3[2] == '3' && tmp_id3[3] < 0xFF)
+ {
+ // id3v2 tag skipeer $49 44 33 yy yy xx zz zz zz zz [zz size]
+ fseek(fd, 2, SEEK_CUR); // Size is 6-9 position
+ if (fread(tmp_id3, 1, 4, fd) != 4)
+ return 1;
+ id3v2size = 10 + ( (long)(tmp_id3[3]) | ((long)(tmp_id3[2]) << 7)
+ | ((long)(tmp_id3[1]) << 14) | ((long)(tmp_id3[0]) << 21) );
+
+ fseek(fd, id3v2size, SEEK_SET);
+ size = size - id3v2size;
+ }else
+ {
+ fseek(fd, 0L, SEEK_SET);
+ }
+
+ while ((nr = fread(buf, sizeof(char), sizeof(buf), fd)) > 0)
+ {
+ if (has_id3v1 && nr <= 128) // We are reading end of ID3v1 tag
+ break;
+ if ( has_id3v1 && ((size=size-nr) < 128) ) // ID3v1 tag is in the current buf
+ nr = nr - 128 + size;
+
+ for (p = buf; nr--; ++p)
+ {
+ crc = (crc >> 8) ^ crctable[(crc ^ *p) & 0xff];
+ crc32_total = (crc >> 8) ^ crctable[(crc32_total ^ *p) & 0xff];
+ }
+ }
+
+ fclose(fd);
+
+ if (nr < 0)
+ return 1;
+
+ *main_val = ~crc;
+
+ return 0;
+}
diff --git a/src/crc32.h b/src/crc32.h
new file mode 100755
index 0000000..7f29a70
--- /dev/null
+++ b/src/crc32.h
@@ -0,0 +1,98 @@
+/* crc32.h - the crc check algorithm for cksfv modified by oliver for easytag
+
+ Copyright (C) 2000 Bryan Call <bc@fodder.org>
+ Copyright (C) 2003 Oliver Schinagl <oliver@are-b.org>
+
+ 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., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+#ifndef __CRC32_H__
+#define __CRC32_H__
+
+
+#define BUFFERSIZE 16384 /* (16k) buffer size for reading from the file */
+
+static const unsigned long crctable[256] = {
+ 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
+ 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
+ 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
+ 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
+ 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
+ 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
+ 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
+ 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
+ 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
+ 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
+ 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
+ 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
+ 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
+ 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
+ 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
+ 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
+ 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
+ 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
+ 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
+ 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
+ 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
+ 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
+ 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
+ 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
+ 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
+ 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
+ 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
+ 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
+ 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
+ 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+ 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
+ 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
+ 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
+ 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
+ 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
+ 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
+ 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
+ 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
+ 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
+ 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
+ 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
+ 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
+ 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
+ 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
+ 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
+ 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
+ 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
+ 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
+ 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
+ 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
+ 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
+ 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
+ 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
+ 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
+ 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
+ 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
+ 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
+ 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
+ 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
+ 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+ 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
+ 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
+ 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
+ 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
+};
+
+int crc32_easytag(register int fd, unsigned long *main_val);
+int crc32_file_with_ID3_tag(char *filename, unsigned long *main_val);
+
+
+#endif /* __CRC32_H__ */
diff --git a/src/dlm.c b/src/dlm.c
new file mode 100755
index 0000000..19c14e3
--- /dev/null
+++ b/src/dlm.c
@@ -0,0 +1,90 @@
+/* dlm.c - 2004/07/04 - Santtu Lakkala */
+
+#include <glib.h>
+#include <string.h>
+
+int dlm_cost (const gchar, const gchar);
+int dlm_minimum (int a, int b, int c, int d);
+
+/*
+ * Compute the Damerau-Levenshtein Distance between utf-8 strings ds and dt.
+ */
+int dlm (const gchar *ds, const gchar *dt)
+{
+ int i, j, n, m, metric;
+ int *d;
+
+ /* Casefold for better matching of the strings. */
+ gchar *s = g_utf8_casefold(ds, -1);
+ gchar *t = g_utf8_casefold(dt, -1);
+
+ n = strlen(s);
+ m = strlen(t);
+
+ if (n && m)
+ {
+ n++;
+ m++;
+
+ d = (int *)g_malloc0(sizeof(int) * m * n);
+
+ for (i = 0; i < m; i++)
+ d[i * n] = i;
+
+ for (i = 1; i < n; i++)
+ {
+ d[i] = i;
+ for (j = 1; j < m; j++)
+ {
+ d[j * n + i] = dlm_minimum(
+ d[(j - 1) * n + i] + 1,
+ d[j * n + i - 1] + 1,
+ d[(j - 1) * n + i - 1] + dlm_cost(s[i - 1], t[j - 1]),
+ (j>1 && i>1 ?
+ d[(j - 2) * n + i - 2] + dlm_cost(s[i - 1], t[j])
+ + dlm_cost(s[i], t[j - 1])
+ : 0x7fff)
+ );
+ }
+ }
+ metric = d[n * m - 1];
+ g_free(d);
+
+ /* Count a "similarity value" */
+ metric = 1000 - (1000 * (metric * 2)) / (m + n);
+
+ g_free(t);
+ g_free(s);
+ return metric;
+ }
+ g_free(t);
+ g_free(s);
+
+ /* Return value of -1 indicates an error */
+ return -1;
+}
+
+/* "Cost" of changing from a to b. */
+int dlm_cost (const char a, const char b)
+{
+ return a == b ? 0 : 1;
+}
+
+/* Return the smallest of four integers. */
+int dlm_minimum (int a, int b, int c, int d)
+{
+ int min = a;
+ if (b < min)
+ {
+ min = b;
+ }
+ if (c < min)
+ {
+ min = c;
+ }
+ if (d < min)
+ {
+ min = d;
+ }
+ return min;
+}
diff --git a/src/dlm.h b/src/dlm.h
new file mode 100755
index 0000000..1ef23fd
--- /dev/null
+++ b/src/dlm.h
@@ -0,0 +1,8 @@
+/* dlm.h - 2004/07/04 */
+
+#ifndef __DLM_H__
+#define __DLM_H__
+
+int dlm (const gchar *s, const gchar *t);
+
+#endif /* __DLM_H__ */
diff --git a/src/easytag.c b/src/easytag.c
new file mode 100755
index 0000000..7ae6d35
--- /dev/null
+++ b/src/easytag.c
@@ -0,0 +1,4612 @@
+/* easytag.c - 2000/04/28 */
+/*
+ * EasyTAG - Tag editor for MP3 and Ogg Vorbis files
+ * Copyright (C) 2000-2003 Jerome Couderc <easytag@gmail.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+#include <config.h> // For definition of ENABLE_OGG
+#include <gtk/gtk.h>
+#include <stdio.h>
+#include <string.h>
+#include <gdk/gdkkeysyms.h>
+#include <glib/gi18n-lib.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+#include <signal.h>
+#ifdef ENABLE_MP3
+# include <id3tag.h>
+#endif
+#if defined ENABLE_MP3 && defined ENABLE_ID3LIB
+# include <id3.h>
+#endif
+#include <sys/types.h>
+#include <utime.h>
+
+#include "easytag.h"
+#include "browser.h"
+#include "log.h"
+#include "misc.h"
+#include "bar.h"
+#include "prefs.h"
+#include "setting.h"
+#include "scan.h"
+#include "mpeg_header.h"
+#include "id3_tag.h"
+#include "ogg_tag.h"
+#include "msgbox.h"
+#include "et_core.h"
+#include "cddb.h"
+#include "picture.h"
+#include "charset.h"
+
+#ifdef WIN32
+# include "win32/win32dep.h"
+#endif
+
+#include "../pixmaps/EasyTAG.xpm"
+
+
+/****************
+ * Declarations *
+ ****************/
+guint idle_handler_id;
+guint progressbar_index; /* An integer to set the value of progress bar into the recurse fonction */
+
+GtkWidget *QuitRecursionWindow = NULL;
+
+/* Used to force to hide the msgbox when saving tag */
+gboolean SF_HideMsgbox_Write_Tag;
+/* To remember which button was pressed when saving tag */
+gint SF_ButtonPressed_Write_Tag;
+/* Used to force to hide the msgbox when renaming file */
+gboolean SF_HideMsgbox_Rename_File;
+/* To remember which button was pressed when renaming file */
+gint SF_ButtonPressed_Rename_File;
+/* Used to force to hide the msgbox when deleting file */
+gboolean SF_HideMsgbox_Delete_File;
+/* To remember which button was pressed when deleting file */
+gint SF_ButtonPressed_Delete_File;
+
+
+
+/**************
+ * Prototypes *
+ **************/
+void Handle_Crash (gint signal_id);
+gchar *signal_to_string (gint signal);
+
+GtkWidget *Create_Browser_Area (void);
+GtkWidget *Create_File_Area (void);
+GtkWidget *Create_Tag_Area (void);
+
+void Menu_Mini_Button_Clicked (GtkEntry *entry);
+void Mini_Button_Clicked (GObject *object);
+void Disable_Command_Buttons (void);
+void Clear_Tag_Entry_Fields (void);
+void Clear_File_Entry_Field (void);
+void Clear_Header_Fields (void);
+
+gint Make_Dir (const gchar *dirname_old, const gchar *dirname_new);
+gint Remove_Dir (const gchar *dirname_old, const gchar *dirname_new);
+void Write_File_Tag (ET_File *ETFile);
+void Rename_File (ET_File *ETFile);
+gint Save_File (ET_File *ETFile, gboolean multiple_files, gboolean force_saving_files);
+gint Delete_File (ET_File *ETFile, gboolean multiple_files);
+gint Save_All_Files_With_Answer (gboolean force_saving_files);
+gint Save_Selected_Files_With_Answer (gboolean force_saving_files);
+gint Save_List_Of_Files (GList *etfilelist, gboolean force_saving_files);
+gint Delete_Selected_Files_With_Answer (void);
+gint Copy_File (gchar const *fileold, gchar const *filenew);
+
+void Display_Usage (void);
+
+void Init_Load_Default_Dir (void);
+void EasyTAG_Exit (void);
+void Quit_MainWindow_Ok_Button (void);
+
+GList *Read_Directory_Recursively (GList *file_list, gchar *path, gint recurse);
+void Open_Quit_Recursion_Function_Window (void);
+void Destroy_Quit_Recursion_Function_Window (void);
+void Quit_Recursion_Function_Button_Pressed (void);
+void Quit_Recursion_Window_Key_Press (GtkWidget *window, GdkEvent *event);
+
+
+
+/********
+ * Main *
+ ********/
+#ifdef WIN32
+int easytag_main (struct HINSTANCE__ *hInstance, int argc, char *argv[]) /* entry point of DLL */
+#else
+int main (int argc, char *argv[])
+#endif
+{
+ GtkWidget *MainVBox;
+ GtkWidget *HBox, *VBox;
+ GdkPixmap *pixmap;
+ GdkBitmap *mask;
+ gboolean created_settings;
+ struct stat statbuf;
+
+
+#ifdef WIN32
+ ET_Win32_Init(hInstance);
+#else
+ /* Signal handling to display a message(SIGSEGV, ...) */
+ signal(SIGBUS,Handle_Crash);
+ signal(SIGFPE,Handle_Crash);
+ signal(SIGSEGV,Handle_Crash);
+ // Must handle this signal to avoid zombie of applications executed (ex: xmms)
+ signal(SIGCHLD,SIG_IGN); // Fix me! : can't run nautilus 1.0.6 with "Browse Directory With"
+#endif
+
+#ifdef ENABLE_NLS
+ bindtextdomain(GETTEXT_PACKAGE, LOCALE);
+ bind_textdomain_codeset(PACKAGE, "UTF-8");
+ textdomain(GETTEXT_PACKAGE);
+ /* Initialize i18n support */
+ //gtk_set_locale();
+#endif
+ Charset_Insert_Locales_Init();
+
+ /* Initialize GTK */
+ gtk_init(&argc, &argv);
+
+ /* Get home variable */
+ HOME_VARIABLE = (gchar *)g_getenv("HOME");
+ INIT_DIRECTORY = NULL;
+
+ /* Starting messages */
+ Log_Print(_("Starting EasyTAG %s (PId: %d) ..."),VERSION,getpid());
+#ifdef ENABLE_MP3
+ Log_Print(_("Currently using libid3tag version %s ..."), ID3_VERSION);
+#endif
+#if defined ENABLE_MP3 && defined ENABLE_ID3LIB
+ Log_Print(_("Currently using id3lib version %d.%d.%d ..."),ID3LIB_MAJOR_VERSION,
+ ID3LIB_MINOR_VERSION,
+ ID3LIB_PATCH_VERSION);
+#endif
+
+#ifdef WIN32
+ if (g_getenv("EASYTAGLANG"))
+ Log_Print(_("Variable EASYTAGLANG defined. Setting locale : '%s'"),g_getenv("EASYTAGLANG"));
+ else
+ Log_Print(_("Setting locale : '%s'"),g_getenv("LANG"));
+#endif
+
+ if (get_locale())
+ Log_Print(_("Currently using locale '%s' (and eventually '%s')..."),
+ get_locale(),get_encoding_from_locale(get_locale()));
+
+
+ /* Create all config files */
+ created_settings = Setting_Create_Files();
+ /* Load Config */
+ Init_Config_Variables();
+ Read_Config();
+ /* Display_Config(); // <- for debugging */
+
+ /* Check given arguments */
+ if (argc>1)
+ {
+ if ( (strcmp(argv[1],"--version")==0) || (strcmp(argv[1],"-v")==0) ) // Query version
+ {
+ g_print(_("%s %s by %s (compiled %s, %s)\n"),APPNAME,VERSION,AUTHOR,__TIME__,__DATE__);
+ g_print(_("E-mail: %s"),EMAIL"\n");
+ g_print(_("Web Page: %s"),WEBPAGE"\n");
+ exit (0);
+ }else if ( (strcmp(argv[1],"--help")==0) || (strcmp(argv[1],"-h")==0) ) // Query help
+ {
+ Display_Usage();
+ }else
+ {
+ gchar *path2check = NULL, *path2check_tmp = NULL;
+ gint resultstat;
+ gchar **pathsplit;
+ gint ps_index = 0;
+
+ // Check if relative or absolute path
+ if (g_path_is_absolute(argv[1])) // Passed an absolute path
+ {
+ path2check = g_strdup(argv[1]);
+ }else // Passed a relative path
+ {
+ gchar *curdir = g_get_current_dir();
+ path2check = g_strconcat(g_get_current_dir(),G_DIR_SEPARATOR_S,argv[1],NULL);
+ g_free(curdir);
+ }
+
+#ifdef WIN32
+ ET_Win32_Path_Replace_Slashes(path2check);
+#endif
+ // Check if contains hidden directories
+ pathsplit = g_strsplit(path2check,G_DIR_SEPARATOR_S,0);
+ g_free(path2check);
+ path2check = NULL;
+
+ // Browse the list to build again the path
+ //FIX ME : Should manage directory ".." in path
+ while (pathsplit[ps_index])
+ {
+ // Activate hidden directories in browser if path contains a dir like ".hidden_dir"
+ if ( (g_ascii_strcasecmp (pathsplit[ps_index],"..") != 0)
+ && (g_ascii_strncasecmp(pathsplit[ps_index],".", 1) == 0)
+ && (strlen(pathsplit[ps_index]) > 1) )
+ BROWSE_HIDDEN_DIR = 1; // If user saves the config for this session, this value will be saved to 1
+
+ if (pathsplit[ps_index]
+ && g_ascii_strcasecmp(pathsplit[ps_index],".") != 0
+ && g_ascii_strcasecmp(pathsplit[ps_index],"") != 0)
+ {
+ if (path2check)
+ {
+ path2check_tmp = g_strconcat(path2check,G_DIR_SEPARATOR_S,pathsplit[ps_index],NULL);
+ }else
+ {
+#ifdef WIN32
+ // Build a path starting with the drive letter
+ path2check_tmp = g_strdup(pathsplit[ps_index]);
+#else
+ path2check_tmp = g_strconcat(G_DIR_SEPARATOR_S,pathsplit[ps_index],NULL);
+#endif
+ }
+
+ path2check = g_strdup(path2check_tmp);
+ g_free(path2check_tmp);
+ }
+ ps_index++;
+ }
+
+ // Check if file or directory
+ resultstat = stat(path2check,&statbuf);
+ if (resultstat==0 && S_ISDIR(statbuf.st_mode)) // Directory
+ {
+ INIT_DIRECTORY = g_strdup(path2check);
+ }else if (resultstat==0 && S_ISREG(statbuf.st_mode)) // File
+ {
+ // When passing a file, we load only the directory
+ INIT_DIRECTORY = g_path_get_dirname(path2check);
+ }else
+ {
+ g_print(_("Unknown parameter or path '%s'\n"),argv[1]);
+ Display_Usage();
+ }
+ g_free(path2check);
+ }
+ }
+
+
+ /* Initialization */
+ ET_Core_Create();
+ Main_Stop_Button_Pressed = 0;
+ Init_Custom_Icons();
+ Init_Mouse_Cursor();
+ Init_OptionsWindow();
+ Init_ScannerWindow();
+ Init_CddbWindow();
+ BrowserEntryModel = NULL;
+ TrackEntryComboModel = NULL;
+ GenreComboModel = NULL;
+
+ /* The main window */
+ MainWindow = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title(GTK_WINDOW(MainWindow),APPNAME" "VERSION);
+ // This part is needed to set correctly the position of handle panes
+ gtk_window_set_default_size(GTK_WINDOW(MainWindow),MAIN_WINDOW_WIDTH,MAIN_WINDOW_HEIGHT);
+
+ g_signal_connect(G_OBJECT(MainWindow),"delete_event",G_CALLBACK(Quit_MainWindow),NULL);
+ g_signal_connect(G_OBJECT(MainWindow),"destroy",G_CALLBACK(Quit_MainWindow),NULL);
+
+ /* Minimised window icon */
+ gtk_widget_realize(MainWindow);
+ pixmap = gdk_pixmap_create_from_xpm_d(MainWindow->window,&mask,NULL,EasyTAG_xpm);
+ gdk_window_set_icon(MainWindow->window,(GdkWindow *)NULL,pixmap,mask);
+
+
+ /* MainVBox for Menu bar + Tool bar + "Browser Area & FileArea & TagArea" + Log Area + "Status bar & Progress bar" */
+ MainVBox = gtk_vbox_new(FALSE,0);
+ gtk_container_add (GTK_CONTAINER(MainWindow),MainVBox);
+ gtk_widget_show(MainVBox);
+
+ /* Menu bar and tool bar */
+ Create_UI(&MenuArea, &ToolArea);
+ gtk_box_pack_start(GTK_BOX(MainVBox),MenuArea,FALSE,FALSE,0);
+ gtk_box_pack_start(GTK_BOX(MainVBox),ToolArea,FALSE,FALSE,0);
+
+
+ /* The two panes: BrowserArea on the left, FileArea+TagArea on the right */
+ MainWindowHPaned = gtk_hpaned_new();
+ //gtk_box_pack_start(GTK_BOX(MainVBox),MainWindowHPaned,TRUE,TRUE,0);
+ gtk_paned_set_position(GTK_PANED(MainWindowHPaned),PANE_HANDLE_POSITION1);
+ gtk_widget_show(MainWindowHPaned);
+
+ /* Browser (Tree + File list + Entry) */
+ BrowseArea = Create_Browser_Area();
+ gtk_paned_pack1(GTK_PANED(MainWindowHPaned),BrowseArea,TRUE,TRUE);
+
+ /* Vertical box for FileArea + TagArea */
+ VBox = gtk_vbox_new(FALSE,0);
+ gtk_paned_pack2(GTK_PANED(MainWindowHPaned),VBox,FALSE,FALSE);
+ gtk_widget_show(VBox);
+
+ /* File */
+ FileArea = Create_File_Area();
+ gtk_box_pack_start(GTK_BOX(VBox),FileArea,FALSE,FALSE,0);
+
+ /* Tag */
+ TagArea = Create_Tag_Area();
+ gtk_box_pack_start(GTK_BOX(VBox),TagArea,FALSE,FALSE,0);
+
+ /* Vertical pane for Browser Area + FileArea + TagArea */
+ MainWindowVPaned = gtk_vpaned_new();
+ gtk_box_pack_start(GTK_BOX(MainVBox),MainWindowVPaned,TRUE,TRUE,0);
+ gtk_paned_pack1(GTK_PANED(MainWindowVPaned),MainWindowHPaned,FALSE,FALSE);
+ gtk_paned_set_position(GTK_PANED(MainWindowVPaned),PANE_HANDLE_POSITION4);
+ gtk_widget_show(MainWindowVPaned);
+
+
+ /* Log */
+ LogArea = Create_Log_Area();
+ gtk_paned_pack2(GTK_PANED(MainWindowVPaned),LogArea,TRUE,TRUE);
+
+ /* Horizontal box for Status bar + Progress bar */
+ HBox = gtk_hbox_new(FALSE,0);
+ gtk_box_pack_start(GTK_BOX(MainVBox),HBox,FALSE,FALSE,0);
+ gtk_widget_show(HBox);
+
+ /* Status bar */
+ StatusArea = Create_Status_Bar();
+ gtk_box_pack_start(GTK_BOX(HBox),StatusArea,TRUE,TRUE,0);
+
+ /* Progress bar */
+ ProgressArea = Create_Progress_Bar();
+ gtk_box_pack_end(GTK_BOX(HBox),ProgressArea,FALSE,FALSE,0);
+
+ gtk_widget_show(MainWindow);
+
+ if (SET_MAIN_WINDOW_POSITION)
+ gdk_window_move(MainWindow->window, MAIN_WINDOW_X, MAIN_WINDOW_Y);
+
+ /* Load the default dir when the UI is created and displayed
+ * to the screen and open also the scanner window */
+ idle_handler_id = g_idle_add((GtkFunction)Init_Load_Default_Dir,NULL);
+
+ /* Enter the event loop */
+ gtk_main ();
+ return 0;
+}
+
+
+GtkWidget *Create_Browser_Area (void)
+{
+ GtkWidget *Frame;
+ GtkWidget *Tree;
+
+ Frame = gtk_frame_new(_("Browser"));
+ gtk_container_set_border_width(GTK_CONTAINER(Frame), 2);
+
+ Tree = Create_Browser_Items(MainWindow);
+ gtk_container_add(GTK_CONTAINER(Frame),Tree);
+
+ /* Don't load init dir here because Tag area hasn't been yet created!.
+ * It will be load at the end of the main function */
+ //Browser_Tree_Select_Dir(DEFAULT_PATH_TO_MP3);
+
+ gtk_widget_show(Frame);
+ return Frame;
+}
+
+
+GtkWidget *Create_File_Area (void)
+{
+ GtkWidget *VBox, *HBox;
+ GtkWidget *Separator;
+ GtkTooltips *Tips;
+
+
+ FileFrame = gtk_frame_new(_("File"));
+ gtk_container_set_border_width(GTK_CONTAINER(FileFrame),2);
+
+ VBox = gtk_vbox_new(FALSE,0);
+ gtk_container_add(GTK_CONTAINER(FileFrame),VBox);
+ gtk_container_set_border_width(GTK_CONTAINER(VBox),2);
+
+ /* Tips */
+ Tips = gtk_tooltips_new();
+
+ /* HBox for FileEntry and IconBox */
+ HBox = gtk_hbox_new(FALSE,2);
+ gtk_box_pack_start(GTK_BOX(VBox),HBox,TRUE,TRUE,0);
+
+ /* File index (position in list + list length) */
+ FileIndex = gtk_label_new("0/0:");
+ gtk_box_pack_start(GTK_BOX(HBox),FileIndex,FALSE,FALSE,0);
+
+ /* File name */
+ FileEntry = gtk_entry_new();
+ gtk_editable_set_editable(GTK_EDITABLE(FileEntry), TRUE);
+ gtk_box_pack_start(GTK_BOX(HBox),FileEntry,TRUE,TRUE,2);
+
+ /* Access status icon */
+ ReadOnlyStatusIconBox = Create_Pixmap_Icon_With_Event_Box("easytag-read-only");
+ gtk_box_pack_start(GTK_BOX(HBox),ReadOnlyStatusIconBox,FALSE,FALSE,0);
+ gtk_tooltips_set_tip(Tips,ReadOnlyStatusIconBox,_("Read Only File"),NULL);
+ BrokenStatusIconBox = Create_Pixmap_Icon_With_Event_Box(GTK_STOCK_MISSING_IMAGE);
+ gtk_box_pack_start(GTK_BOX(HBox),BrokenStatusIconBox,FALSE,FALSE,0);
+ gtk_tooltips_set_tip(Tips,BrokenStatusIconBox,_("File Link Broken"),NULL);
+
+ Attach_Popup_Menu_To_Tag_Entries(GTK_ENTRY(FileEntry));
+
+
+ /*
+ * File Infos
+ */
+ HeaderInfosTable = gtk_table_new(3,5,FALSE);
+ gtk_container_add(GTK_CONTAINER(VBox),HeaderInfosTable);
+ gtk_container_set_border_width(GTK_CONTAINER(HeaderInfosTable),2);
+ gtk_table_set_row_spacings(GTK_TABLE(HeaderInfosTable),1);
+ gtk_table_set_col_spacings(GTK_TABLE(HeaderInfosTable),2);
+
+ VersionLabel = gtk_label_new(_("MPEG"));
+ gtk_table_attach_defaults(GTK_TABLE(HeaderInfosTable),VersionLabel,0,1,0,1);
+ VersionValueLabel = gtk_label_new(_("?, Layer ?"));
+ gtk_table_attach_defaults(GTK_TABLE(HeaderInfosTable),VersionValueLabel,1,2,0,1);
+ gtk_misc_set_alignment(GTK_MISC(VersionLabel),1,0.5);
+ gtk_misc_set_alignment(GTK_MISC(VersionValueLabel),0,0.5);
+
+ BitrateLabel = gtk_label_new(_("Bitrate:"));
+ gtk_table_attach_defaults(GTK_TABLE(HeaderInfosTable),BitrateLabel,0,1,1,2);
+ BitrateValueLabel = gtk_label_new(_("? kb/s"));
+ gtk_table_attach_defaults(GTK_TABLE(HeaderInfosTable),BitrateValueLabel,1,2,1,2);
+ gtk_misc_set_alignment(GTK_MISC(BitrateLabel),1,0.5);
+ gtk_misc_set_alignment(GTK_MISC(BitrateValueLabel),0,0.5);
+
+ SampleRateLabel = gtk_label_new(_("Freq:"));
+ gtk_table_attach_defaults(GTK_TABLE(HeaderInfosTable),SampleRateLabel,0,1,2,3);
+ SampleRateValueLabel = gtk_label_new(_("? Hz"));
+ gtk_table_attach_defaults(GTK_TABLE(HeaderInfosTable),SampleRateValueLabel,1,2,2,3);
+ gtk_misc_set_alignment(GTK_MISC(SampleRateLabel),1,0.5);
+ gtk_misc_set_alignment(GTK_MISC(SampleRateValueLabel),0,0.5);
+
+ Separator = gtk_vseparator_new();
+ gtk_table_attach(GTK_TABLE(HeaderInfosTable),Separator,2,3,0,4,GTK_FILL,GTK_FILL,0,0);
+
+ ModeLabel = gtk_label_new(_("Mode:"));
+ gtk_table_attach_defaults(GTK_TABLE(HeaderInfosTable),ModeLabel,3,4,0,1);
+ ModeValueLabel = gtk_label_new(_("?"));
+ gtk_table_attach_defaults(GTK_TABLE(HeaderInfosTable),ModeValueLabel,4,5,0,1);
+ gtk_misc_set_alignment(GTK_MISC(ModeLabel),1,0.5);
+ gtk_misc_set_alignment(GTK_MISC(ModeValueLabel),0,0.5);
+
+ SizeLabel = gtk_label_new(_("Size:"));
+ gtk_table_attach_defaults(GTK_TABLE(HeaderInfosTable),SizeLabel,3,4,1,2);
+ SizeValueLabel = gtk_label_new(_("? kb"));
+ gtk_table_attach_defaults(GTK_TABLE(HeaderInfosTable),SizeValueLabel,4,5,1,2);
+ gtk_misc_set_alignment(GTK_MISC(SizeLabel),1,0.5);
+ gtk_misc_set_alignment(GTK_MISC(SizeValueLabel),0,0.5);
+
+ DurationLabel = gtk_label_new(_("Time:"));
+ gtk_table_attach_defaults(GTK_TABLE(HeaderInfosTable),DurationLabel,3,4,2,3);
+ DurationValueLabel = gtk_label_new(_("?"));
+ gtk_table_attach_defaults(GTK_TABLE(HeaderInfosTable),DurationValueLabel,4,5,2,3);
+ gtk_misc_set_alignment(GTK_MISC(DurationLabel),1,0.5);
+ gtk_misc_set_alignment(GTK_MISC(DurationValueLabel),0,0.5);
+
+ gtk_widget_show(FileFrame);
+ gtk_widget_show(VBox);
+ gtk_widget_show(HBox);
+ gtk_widget_show(FileIndex);
+ gtk_widget_show(FileEntry);
+ if (SHOW_HEADER_INFO)
+ gtk_widget_show_all(HeaderInfosTable);
+ return FileFrame;
+}
+
+#include "../pixmaps/sequence_track.xpm"
+GtkWidget *Create_Tag_Area (void)
+{
+ GtkWidget *Separator;
+ GtkWidget *Frame;
+ GtkWidget *Table;
+ GtkWidget *Label;
+ GtkWidget *Icon;
+ GtkWidget *VBox;
+ GtkWidget *hbox;
+ GList *focusable_widgets_list = NULL;
+ //GtkWidget *ScrollWindow;
+ //GtkTextBuffer *TextBuffer;
+ GtkEntryCompletion *completion;
+ GtkTooltips *Tips;
+ gint MButtonSize = 13;
+ gint TablePadding = 2;
+
+ // For Picture
+ static const GtkTargetEntry drops[] = { { "text/uri-list", 0, TARGET_URI_LIST } };
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+ GtkTreeSelection *selection;
+
+
+ /* Tips */
+ Tips = gtk_tooltips_new();
+
+ /* Main Frame */
+ TagFrame = gtk_frame_new(_("Tag"));
+ gtk_container_set_border_width(GTK_CONTAINER(TagFrame),2);
+
+ /* Box for the notebook (only for setting a border) */
+ VBox = gtk_vbox_new(FALSE,0);
+ gtk_container_add(GTK_CONTAINER(TagFrame),VBox);
+ gtk_container_set_border_width(GTK_CONTAINER(VBox),2);
+
+ /*
+ * Note book
+ */
+ TagNoteBook = gtk_notebook_new();
+ gtk_notebook_popup_enable(GTK_NOTEBOOK(TagNoteBook));
+ //gtk_container_add(GTK_CONTAINER(TagFrame),TagNoteBook);
+ gtk_box_pack_start(GTK_BOX(VBox),TagNoteBook,TRUE,TRUE,0);
+ gtk_notebook_set_tab_pos(GTK_NOTEBOOK(TagNoteBook),GTK_POS_TOP);
+ gtk_notebook_set_show_border(GTK_NOTEBOOK(TagNoteBook),FALSE);
+ gtk_notebook_popup_enable(GTK_NOTEBOOK(TagNoteBook));
+
+ /*
+ * 1 - Page for common tag fields
+ */
+ Label = gtk_label_new(_("Common"));
+ Frame = gtk_frame_new(NULL);
+ gtk_notebook_append_page(GTK_NOTEBOOK(TagNoteBook),Frame,Label);
+ gtk_container_set_border_width(GTK_CONTAINER(Frame), 0);
+ gtk_frame_set_shadow_type(GTK_FRAME(Frame),GTK_SHADOW_NONE); // Hide the Frame
+
+
+ Table = gtk_table_new(11,11,FALSE);
+ gtk_container_add(GTK_CONTAINER(Frame),Table);
+ gtk_container_set_border_width(GTK_CONTAINER(Table),2);
+
+ /* Title */
+ TitleLabel = gtk_label_new(_("Title:"));
+ gtk_table_attach(GTK_TABLE(Table),TitleLabel,0,1,0,1,GTK_FILL,GTK_FILL,TablePadding,TablePadding);
+ gtk_misc_set_alignment(GTK_MISC(TitleLabel),1,0.5);
+
+ TitleEntry = gtk_entry_new();
+ gtk_table_attach(GTK_TABLE(Table),TitleEntry,1,10,0,1,
+ GTK_EXPAND|GTK_FILL,GTK_EXPAND|GTK_FILL,TablePadding,TablePadding);
+ gtk_widget_set_size_request(TitleEntry, 150, -1);
+
+ TitleMButton = gtk_button_new();
+ gtk_widget_set_size_request(TitleMButton,MButtonSize,MButtonSize);
+ gtk_table_attach(GTK_TABLE(Table),TitleMButton,10,11,0,1,0,0,TablePadding,TablePadding);
+ g_signal_connect(G_OBJECT(TitleMButton),"clicked", G_CALLBACK(Mini_Button_Clicked),NULL);
+ gtk_tooltips_set_tip(Tips,TitleMButton,_("Tag selected files with this title"),NULL);
+
+ Attach_Popup_Menu_To_Tag_Entries(GTK_ENTRY(TitleEntry));
+ g_object_set_data(G_OBJECT(TitleEntry),"MButtonName",TitleMButton);
+
+
+ /* Artist */
+ ArtistLabel = gtk_label_new(_("Artist:"));
+ gtk_table_attach(GTK_TABLE(Table),ArtistLabel,0,1,1,2,GTK_FILL,GTK_FILL,TablePadding,TablePadding);
+ gtk_misc_set_alignment(GTK_MISC(ArtistLabel),1,0.5);
+
+ ArtistEntry = gtk_entry_new();
+ gtk_table_attach(GTK_TABLE(Table),ArtistEntry,1,10,1,2,
+ GTK_EXPAND|GTK_FILL,GTK_EXPAND|GTK_FILL,TablePadding,TablePadding);
+
+ ArtistMButton = gtk_button_new();
+ gtk_widget_set_size_request(ArtistMButton,MButtonSize,MButtonSize);
+ gtk_table_attach(GTK_TABLE(Table),ArtistMButton,10,11,1,2,0,0,TablePadding,TablePadding);
+ g_signal_connect(G_OBJECT(ArtistMButton),"clicked", G_CALLBACK(Mini_Button_Clicked),NULL);
+ gtk_tooltips_set_tip(Tips,ArtistMButton,_("Tag selected files with this artist"),NULL);
+
+ Attach_Popup_Menu_To_Tag_Entries(GTK_ENTRY(ArtistEntry));
+ g_object_set_data(G_OBJECT(ArtistEntry),"MButtonName",ArtistMButton);
+
+ /* Album */
+ AlbumLabel = gtk_label_new(_("Album:"));
+ gtk_table_attach(GTK_TABLE(Table),AlbumLabel,0,1,2,3,GTK_FILL,GTK_FILL,TablePadding,TablePadding);
+ gtk_misc_set_alignment(GTK_MISC(AlbumLabel),1,0.5);
+
+ AlbumEntry = gtk_entry_new();
+ gtk_table_attach(GTK_TABLE(Table),AlbumEntry,1,7,2,3,
+ GTK_EXPAND|GTK_FILL,GTK_EXPAND|GTK_FILL,TablePadding,TablePadding);
+
+ AlbumMButton = gtk_button_new();
+ //gtk_widget_set_size_request(AlbumMButton, 10, 10);
+ gtk_widget_set_size_request(AlbumMButton,MButtonSize,MButtonSize);
+ gtk_table_attach(GTK_TABLE(Table),AlbumMButton,7,8,2,3,0,0,TablePadding,TablePadding);
+ g_signal_connect(G_OBJECT(AlbumMButton),"clicked",G_CALLBACK(Mini_Button_Clicked),NULL);
+ gtk_tooltips_set_tip(Tips,AlbumMButton,_("Tag selected files with this album name"),NULL);
+
+ Attach_Popup_Menu_To_Tag_Entries(GTK_ENTRY(AlbumEntry));
+ g_object_set_data(G_OBJECT(AlbumEntry),"MButtonName",AlbumMButton);
+
+ /* Disc Number */
+ DiscNumberLabel = gtk_label_new(_("CD:"));
+ gtk_table_attach(GTK_TABLE(Table),DiscNumberLabel,8,9,2,3,GTK_FILL,GTK_FILL,TablePadding,TablePadding);
+ gtk_misc_set_alignment(GTK_MISC(DiscNumberLabel),1,0.5);
+
+ DiscNumberEntry = gtk_entry_new();
+ gtk_table_attach(GTK_TABLE(Table),DiscNumberEntry,9,10,2,3,
+ GTK_EXPAND|GTK_FILL,GTK_EXPAND|GTK_FILL,TablePadding,TablePadding);
+ gtk_widget_set_size_request(DiscNumberEntry,30,-1);
+ // FIX ME should allow to type only something like : 1/3
+ //g_signal_connect(G_OBJECT(GTK_ENTRY(DiscNumberEntry)),"insert_text",G_CALLBACK(Insert_Only_Digit),NULL);
+
+ DiscNumberMButton = gtk_button_new();
+ //gtk_widget_set_size_request(DiscNumberMButton, 10, 10);
+ gtk_widget_set_size_request(DiscNumberMButton,MButtonSize,MButtonSize);
+ gtk_table_attach(GTK_TABLE(Table),DiscNumberMButton,10,11,2,3,0,0,TablePadding,TablePadding);
+ g_signal_connect(G_OBJECT(DiscNumberMButton),"clicked",G_CALLBACK(Mini_Button_Clicked),NULL);
+ gtk_tooltips_set_tip(Tips,DiscNumberMButton,_("Tag selected files with this disc number"),NULL);
+
+ Attach_Popup_Menu_To_Tag_Entries(GTK_ENTRY(DiscNumberEntry));
+ g_object_set_data(G_OBJECT(DiscNumberEntry),"MButtonName",DiscNumberMButton);
+
+ /* Year */
+ YearLabel = gtk_label_new(_("Year:"));
+ gtk_table_attach(GTK_TABLE(Table),YearLabel,0,1,3,4,GTK_FILL,GTK_FILL,TablePadding,TablePadding);
+ gtk_misc_set_alignment(GTK_MISC(YearLabel),1,0.5);
+
+ YearEntry = gtk_entry_new();
+ gtk_entry_set_max_length(GTK_ENTRY(YearEntry), 4);
+ gtk_table_attach(GTK_TABLE(Table),YearEntry,1,2,3,4,
+ GTK_EXPAND|GTK_FILL,GTK_EXPAND|GTK_FILL,TablePadding,TablePadding);
+ gtk_widget_set_size_request(YearEntry,37,-1);
+ g_signal_connect(G_OBJECT(YearEntry),"insert_text",G_CALLBACK(Insert_Only_Digit),NULL);
+ g_signal_connect(G_OBJECT(YearEntry),"activate",G_CALLBACK(Parse_Date),NULL);
+ g_signal_connect(G_OBJECT(YearEntry),"focus-out-event",G_CALLBACK(Parse_Date),NULL);
+
+ YearMButton = gtk_button_new();
+ gtk_widget_set_size_request(YearMButton,MButtonSize,MButtonSize);
+ gtk_table_attach(GTK_TABLE(Table),YearMButton,2,3,3,4,0,0,TablePadding,TablePadding);
+ g_signal_connect(G_OBJECT(YearMButton),"clicked",G_CALLBACK(Mini_Button_Clicked),NULL);
+ gtk_tooltips_set_tip(Tips,YearMButton,_("Tag selected files with this year"),NULL);
+
+ /* Small vertical separator */
+ Separator = gtk_vseparator_new();
+ gtk_table_attach(GTK_TABLE(Table),Separator,3,4,3,4,GTK_FILL,GTK_FILL,TablePadding,TablePadding);
+
+
+ /* Track and Track total */
+ TrackMButtonSequence = gtk_button_new();
+ gtk_widget_set_size_request(TrackMButtonSequence,MButtonSize,MButtonSize);
+ gtk_table_attach(GTK_TABLE(Table),TrackMButtonSequence,4,5,3,4,0,0,TablePadding,TablePadding);
+ g_signal_connect(G_OBJECT(TrackMButtonSequence),"clicked",G_CALLBACK(Mini_Button_Clicked),NULL);
+ gtk_tooltips_set_tip(Tips,TrackMButtonSequence,_("Number selected tracks sequentially. "
+ "Starts at 01 in each subdirectory."), NULL);
+ // Pixmap into TrackMButtonSequence button
+ //Icon = gtk_image_new_from_stock("easytag-sequence-track", GTK_ICON_SIZE_BUTTON); // FIX ME : it doesn't display the good size of the '#'
+ Icon = Create_Xpm_Image((const char **)sequence_track_xpm);
+ gtk_container_add(GTK_CONTAINER(TrackMButtonSequence),Icon);
+ GTK_WIDGET_UNSET_FLAGS(TrackMButtonSequence,GTK_CAN_DEFAULT); // To have enought space to display the icon
+ GTK_WIDGET_UNSET_FLAGS(TrackMButtonSequence,GTK_CAN_FOCUS); // To have enought space to display the icon
+
+ TrackLabel = gtk_label_new(_("Track #:"));
+ gtk_table_attach(GTK_TABLE(Table),TrackLabel,5,6,3,4,GTK_FILL,GTK_FILL,TablePadding,TablePadding);
+ gtk_misc_set_alignment(GTK_MISC(TrackLabel),1,0.5);
+
+ if (TrackEntryComboModel != NULL)
+ gtk_list_store_clear(TrackEntryComboModel);
+ else
+ TrackEntryComboModel = gtk_list_store_new(MISC_COMBO_COUNT, G_TYPE_STRING);
+
+ TrackEntryCombo = gtk_combo_box_entry_new_with_model(GTK_TREE_MODEL(TrackEntryComboModel), MISC_COMBO_TEXT);
+ gtk_table_attach(GTK_TABLE(Table),TrackEntryCombo,6,7,3,4,
+ GTK_EXPAND|GTK_FILL,GTK_EXPAND|GTK_FILL,TablePadding,TablePadding);
+ gtk_combo_box_set_wrap_width(GTK_COMBO_BOX(TrackEntryCombo),3); // Three columns to display track numbers list
+
+ gtk_widget_set_size_request(TrackEntryCombo,50,-1);
+ g_signal_connect(G_OBJECT(GTK_ENTRY(GTK_BIN(TrackEntryCombo)->child)),"insert_text",
+ G_CALLBACK(Insert_Only_Digit),NULL);
+
+ Label = gtk_label_new("/");
+ gtk_table_attach(GTK_TABLE(Table),Label,7,8,3,4,GTK_FILL,GTK_FILL,TablePadding,TablePadding);
+ gtk_misc_set_alignment(GTK_MISC(Label),0.5,0.5);
+
+ TrackMButtonNbrFiles = gtk_button_new();
+ gtk_widget_set_size_request(TrackMButtonNbrFiles,MButtonSize,MButtonSize);
+ gtk_table_attach(GTK_TABLE(Table),TrackMButtonNbrFiles,8,9,3,4,0,0,TablePadding,TablePadding);
+ g_signal_connect(G_OBJECT(TrackMButtonNbrFiles),"clicked",G_CALLBACK(Mini_Button_Clicked),NULL);
+ gtk_tooltips_set_tip(Tips,TrackMButtonNbrFiles,_("Set the number of files, in the same directory of the displayed file, to the selected tracks."), NULL);
+ // Pixmap into TrackMButtonNbrFiles button
+ //Icon = gtk_image_new_from_stock("easytag-sequence-track", GTK_ICON_SIZE_BUTTON);
+ Icon = Create_Xpm_Image((const char **)sequence_track_xpm);
+ gtk_container_add(GTK_CONTAINER(TrackMButtonNbrFiles),Icon);
+ GTK_WIDGET_UNSET_FLAGS(TrackMButtonNbrFiles,GTK_CAN_DEFAULT); // To have enought space to display the icon
+ GTK_WIDGET_UNSET_FLAGS(TrackMButtonNbrFiles,GTK_CAN_FOCUS); // To have enought space to display the icon
+
+ TrackTotalEntry = gtk_entry_new();
+ gtk_table_attach(GTK_TABLE(Table),TrackTotalEntry,9,10,3,4,
+ GTK_EXPAND|GTK_FILL,GTK_EXPAND|GTK_FILL,TablePadding,TablePadding);
+ gtk_widget_set_size_request(TrackTotalEntry,30,-1);
+ g_signal_connect(G_OBJECT(GTK_ENTRY(TrackTotalEntry)),"insert_text",
+ G_CALLBACK(Insert_Only_Digit),NULL);
+
+ TrackMButton = gtk_button_new();
+ gtk_widget_set_size_request(TrackMButton,MButtonSize,MButtonSize);
+ gtk_table_attach(GTK_TABLE(Table),TrackMButton,10,11,3,4,0,0,TablePadding,TablePadding);
+ g_signal_connect(G_OBJECT(TrackMButton),"clicked",G_CALLBACK(Mini_Button_Clicked),NULL);
+ gtk_tooltips_set_tip(Tips,TrackMButton,_("Tag selected files with this number of tracks"),NULL);
+
+ g_object_set_data(G_OBJECT(GTK_ENTRY(GTK_BIN(TrackEntryCombo)->child)),"MButtonName",TrackMButton);
+ g_object_set_data(G_OBJECT(TrackTotalEntry),"MButtonName",TrackMButton);
+
+ /* Genre */
+ GenreLabel = gtk_label_new(_("Genre:"));
+ gtk_table_attach(GTK_TABLE(Table),GenreLabel,0,1,4,5,GTK_FILL,GTK_FILL,TablePadding,TablePadding);
+ gtk_misc_set_alignment(GTK_MISC(GenreLabel),1,0.5);
+
+ if (GenreComboModel != NULL)
+ gtk_list_store_clear(GenreComboModel);
+ else
+ GenreComboModel = gtk_list_store_new(MISC_COMBO_COUNT, G_TYPE_STRING);
+ GenreCombo = gtk_combo_box_entry_new_with_model(GTK_TREE_MODEL(GenreComboModel), MISC_COMBO_TEXT);
+ completion = gtk_entry_completion_new();
+ gtk_entry_set_completion(GTK_ENTRY(GTK_BIN(GenreCombo)->child), completion);
+ g_object_unref(completion);
+ gtk_entry_completion_set_model(completion, GTK_TREE_MODEL(GenreComboModel));
+ gtk_entry_completion_set_text_column(completion, 0);
+ gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(GenreComboModel), MISC_COMBO_TEXT, Combo_Alphabetic_Sort, NULL, NULL);
+ gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(GenreComboModel), MISC_COMBO_TEXT, GTK_SORT_ASCENDING);
+ gtk_table_attach(GTK_TABLE(Table),GenreCombo,1,10,4,5,
+ GTK_EXPAND|GTK_FILL,GTK_EXPAND|GTK_FILL,TablePadding,TablePadding);
+ Load_Genres_List_To_UI();
+ gtk_combo_box_set_wrap_width(GTK_COMBO_BOX(GenreCombo),2); // Two columns to display genres list
+
+ GenreMButton = gtk_button_new();
+ gtk_widget_set_size_request(GenreMButton,MButtonSize,MButtonSize);
+ gtk_table_attach(GTK_TABLE(Table),GenreMButton,10,11,4,5,0,0,TablePadding,TablePadding);
+ g_signal_connect(G_OBJECT(GenreMButton),"clicked",G_CALLBACK(Mini_Button_Clicked),NULL);
+ gtk_tooltips_set_tip(Tips,GenreMButton,_("Tag selected files with this genre"),NULL);
+
+ Attach_Popup_Menu_To_Tag_Entries(GTK_ENTRY(GTK_BIN(GenreCombo)->child));
+ g_object_set_data(G_OBJECT(GTK_BIN(GenreCombo)->child),"MButtonName",GenreMButton);
+
+ /* Comment */
+ CommentLabel = gtk_label_new(_("Comment:"));
+ gtk_table_attach(GTK_TABLE(Table),CommentLabel,0,1,5,6,GTK_FILL,GTK_FILL,TablePadding,TablePadding);
+ gtk_misc_set_alignment(GTK_MISC(CommentLabel),1,0.5);
+
+ CommentEntry = gtk_entry_new();
+ gtk_table_attach(GTK_TABLE(Table),CommentEntry,1,10,5,6,
+ GTK_EXPAND|GTK_FILL,GTK_EXPAND|GTK_FILL,TablePadding,TablePadding);
+
+ // Use of a text view instead of an entry...
+ //ScrollWindow = gtk_scrolled_window_new(NULL,NULL);
+ //gtk_table_attach_defaults(GTK_TABLE(Table),ScrollWindow,1,10,5,6);
+ //gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ScrollWindow), GTK_POLICY_AUTOMATIC,GTK_POLICY_AUTOMATIC);
+ //gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(ScrollWindow), GTK_SHADOW_IN);
+ //gtk_widget_set_size_request(ScrollWindow,-1,52); // Display ~3 lines...
+ //TextBuffer = gtk_text_buffer_new(NULL);
+ //CommentView = gtk_text_view_new_with_buffer(TextBuffer);
+ //gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(CommentView), GTK_WRAP_WORD); // To not display the horizontal scrollbar
+ //gtk_container_add(GTK_CONTAINER(ScrollWindow),CommentView);
+ //Attach_Popup_Menu_To_Tag_Entries(GTK_ENTRY(CommentView));
+
+ CommentMButton = gtk_button_new();
+ gtk_widget_set_size_request(CommentMButton,MButtonSize,MButtonSize);
+ gtk_table_attach(GTK_TABLE(Table),CommentMButton,10,11,5,6,0,0,TablePadding,TablePadding);
+ g_signal_connect(G_OBJECT(CommentMButton),"clicked",G_CALLBACK(Mini_Button_Clicked),NULL);
+ gtk_tooltips_set_tip(Tips,CommentMButton,_("Tag selected files with this comment"),NULL);
+
+ Attach_Popup_Menu_To_Tag_Entries(GTK_ENTRY(CommentEntry));
+ g_object_set_data(G_OBJECT(CommentEntry),"MButtonName",CommentMButton);
+ //Attach_Popup_Menu_To_Tag_Entries(GTK_ENTRY(CommentView));
+ //g_object_set_data(G_OBJECT(CommentView),"MButtonName",CommentMButton);
+
+
+ /* Composer (name of the composers) */
+ ComposerLabel = gtk_label_new(_("Composer:"));
+ gtk_table_attach(GTK_TABLE(Table),ComposerLabel,0,1,6,7,GTK_FILL,GTK_FILL,TablePadding,TablePadding);
+ gtk_misc_set_alignment(GTK_MISC(ComposerLabel),1,0.5);
+
+ ComposerEntry = gtk_entry_new();
+ gtk_table_attach(GTK_TABLE(Table),ComposerEntry,1,10,6,7,
+ GTK_EXPAND|GTK_FILL,GTK_EXPAND|GTK_FILL,TablePadding,TablePadding);
+
+ ComposerMButton = gtk_button_new();
+ gtk_widget_set_size_request(ComposerMButton,MButtonSize,MButtonSize);
+ gtk_table_attach(GTK_TABLE(Table),ComposerMButton,10,11,6,7,0,0,TablePadding,TablePadding);
+ g_signal_connect(G_OBJECT(ComposerMButton),"clicked", G_CALLBACK(Mini_Button_Clicked),NULL);
+ gtk_tooltips_set_tip(Tips,ComposerMButton,_("Tag selected files with this composer"),NULL);
+
+ Attach_Popup_Menu_To_Tag_Entries(GTK_ENTRY(ComposerEntry));
+ g_object_set_data(G_OBJECT(ComposerEntry),"MButtonName",ComposerMButton);
+
+
+ /* Original Artist / Performer */
+ OrigArtistLabel = gtk_label_new(_("Orig. Artist:"));
+ gtk_table_attach(GTK_TABLE(Table),OrigArtistLabel,0,1,7,8,GTK_FILL,GTK_FILL,TablePadding,TablePadding);
+ gtk_misc_set_alignment(GTK_MISC(OrigArtistLabel),1,0.5);
+
+ OrigArtistEntry = gtk_entry_new();
+ gtk_table_attach(GTK_TABLE(Table),OrigArtistEntry,1,10,7,8,
+ GTK_EXPAND|GTK_FILL,GTK_EXPAND|GTK_FILL,TablePadding,TablePadding);
+
+ OrigArtistMButton = gtk_button_new();
+ gtk_widget_set_size_request(OrigArtistMButton,MButtonSize,MButtonSize);
+ gtk_table_attach(GTK_TABLE(Table),OrigArtistMButton,10,11,7,8,0,0,TablePadding,TablePadding);
+ g_signal_connect(G_OBJECT(OrigArtistMButton),"clicked", G_CALLBACK(Mini_Button_Clicked),NULL);
+ gtk_tooltips_set_tip(Tips,OrigArtistMButton,_("Tag selected files with this original artist"),NULL);
+
+ Attach_Popup_Menu_To_Tag_Entries(GTK_ENTRY(OrigArtistEntry));
+ g_object_set_data(G_OBJECT(OrigArtistEntry),"MButtonName",OrigArtistMButton);
+
+
+ /* Copyright */
+ CopyrightLabel = gtk_label_new(_("Copyright:"));
+ gtk_table_attach(GTK_TABLE(Table),CopyrightLabel,0,1,8,9,GTK_FILL,GTK_FILL,TablePadding,TablePadding);
+ gtk_misc_set_alignment(GTK_MISC(CopyrightLabel),1,0.5);
+
+ CopyrightEntry = gtk_entry_new();
+ gtk_table_attach(GTK_TABLE(Table),CopyrightEntry,1,10,8,9,
+ GTK_EXPAND|GTK_FILL,GTK_EXPAND|GTK_FILL,TablePadding,TablePadding);
+
+ CopyrightMButton = gtk_button_new();
+ gtk_widget_set_size_request(CopyrightMButton,MButtonSize,MButtonSize);
+ gtk_table_attach(GTK_TABLE(Table),CopyrightMButton,10,11,8,9,0,0,TablePadding,TablePadding);
+ g_signal_connect(G_OBJECT(CopyrightMButton),"clicked", G_CALLBACK(Mini_Button_Clicked),NULL);
+ gtk_tooltips_set_tip(Tips,CopyrightMButton,_("Tag selected files with this copyright"),NULL);
+
+ Attach_Popup_Menu_To_Tag_Entries(GTK_ENTRY(CopyrightEntry));
+ g_object_set_data(G_OBJECT(CopyrightEntry),"MButtonName",CopyrightMButton);
+
+
+ /* URL */
+ URLLabel = gtk_label_new(_("URL:"));
+ gtk_table_attach(GTK_TABLE(Table),URLLabel,0,1,9,10,GTK_FILL,GTK_FILL,TablePadding,TablePadding);
+ gtk_misc_set_alignment(GTK_MISC(URLLabel),1,0.5);
+
+ URLEntry = gtk_entry_new();
+ gtk_table_attach(GTK_TABLE(Table),URLEntry,1,10,9,10,
+ GTK_EXPAND|GTK_FILL,GTK_EXPAND|GTK_FILL,TablePadding,TablePadding);
+
+ URLMButton = gtk_button_new();
+ gtk_widget_set_size_request(URLMButton,MButtonSize,MButtonSize);
+ gtk_table_attach(GTK_TABLE(Table),URLMButton,10,11,9,10,0,0,TablePadding,TablePadding);
+ g_signal_connect(G_OBJECT(URLMButton),"clicked", G_CALLBACK(Mini_Button_Clicked),NULL);
+ gtk_tooltips_set_tip(Tips,URLMButton,_("Tag selected files with this URL"),NULL);
+
+ Attach_Popup_Menu_To_Tag_Entries(GTK_ENTRY(URLEntry));
+ g_object_set_data(G_OBJECT(URLEntry),"MButtonName",URLMButton);
+
+
+ /* Encoded by */
+ EncodedByLabel = gtk_label_new(_("Encoded by:"));
+ gtk_table_attach(GTK_TABLE(Table),EncodedByLabel,0,1,10,11,GTK_FILL,GTK_FILL,TablePadding,TablePadding);
+ gtk_misc_set_alignment(GTK_MISC(EncodedByLabel),1,0.5);
+
+ EncodedByEntry = gtk_entry_new();
+ gtk_table_attach(GTK_TABLE(Table),EncodedByEntry,1,10,10,11,
+ GTK_EXPAND|GTK_FILL,GTK_EXPAND|GTK_FILL,TablePadding,TablePadding);
+
+ EncodedByMButton = gtk_button_new();
+ gtk_widget_set_size_request(EncodedByMButton,MButtonSize,MButtonSize);
+ gtk_table_attach(GTK_TABLE(Table),EncodedByMButton,10,11,10,11,0,0,TablePadding,TablePadding);
+ g_signal_connect(G_OBJECT(EncodedByMButton),"clicked", G_CALLBACK(Mini_Button_Clicked),NULL);
+ gtk_tooltips_set_tip(Tips,EncodedByMButton,_("Tag selected files with this encoder name"),NULL);
+
+ Attach_Popup_Menu_To_Tag_Entries(GTK_ENTRY(EncodedByEntry));
+ g_object_set_data(G_OBJECT(EncodedByEntry),"MButtonName",EncodedByMButton);
+
+
+ // Managing of entries when pressing the Enter key
+ g_signal_connect_swapped(G_OBJECT(TitleEntry), "activate",G_CALLBACK(gtk_widget_grab_focus),G_OBJECT(ArtistEntry));
+ g_signal_connect_swapped(G_OBJECT(ArtistEntry), "activate",G_CALLBACK(gtk_widget_grab_focus),G_OBJECT(AlbumEntry));
+ g_signal_connect_swapped(G_OBJECT(AlbumEntry), "activate",G_CALLBACK(gtk_widget_grab_focus),G_OBJECT(DiscNumberEntry));
+ g_signal_connect_swapped(G_OBJECT(DiscNumberEntry), "activate",G_CALLBACK(gtk_widget_grab_focus),G_OBJECT(YearEntry));
+ g_signal_connect_swapped(G_OBJECT(YearEntry), "activate",G_CALLBACK(gtk_widget_grab_focus),G_OBJECT(GTK_ENTRY(GTK_BIN(TrackEntryCombo)->child)));
+ g_signal_connect_swapped(G_OBJECT(GTK_ENTRY(GTK_BIN(TrackEntryCombo)->child)),"activate",G_CALLBACK(gtk_widget_grab_focus),G_OBJECT(TrackTotalEntry));
+ g_signal_connect_swapped(G_OBJECT(TrackTotalEntry), "activate",G_CALLBACK(gtk_widget_grab_focus),G_OBJECT(GTK_BIN(GenreCombo)->child));
+ g_signal_connect_swapped(G_OBJECT(GTK_BIN(GenreCombo)->child),"activate",G_CALLBACK(gtk_widget_grab_focus),G_OBJECT(CommentEntry));
+ g_signal_connect_swapped(G_OBJECT(CommentEntry), "activate",G_CALLBACK(gtk_widget_grab_focus),G_OBJECT(ComposerEntry));
+ g_signal_connect_swapped(G_OBJECT(ComposerEntry), "activate",G_CALLBACK(gtk_widget_grab_focus),G_OBJECT(OrigArtistEntry));
+ g_signal_connect_swapped(G_OBJECT(OrigArtistEntry), "activate",G_CALLBACK(gtk_widget_grab_focus),G_OBJECT(CopyrightEntry));
+ g_signal_connect_swapped(G_OBJECT(CopyrightEntry), "activate",G_CALLBACK(gtk_widget_grab_focus),G_OBJECT(URLEntry));
+ g_signal_connect_swapped(G_OBJECT(URLEntry), "activate",G_CALLBACK(gtk_widget_grab_focus),G_OBJECT(EncodedByEntry));
+ g_signal_connect_swapped(G_OBJECT(EncodedByEntry), "activate",G_CALLBACK(gtk_widget_grab_focus),G_OBJECT(TitleEntry));
+
+ // Set focus chain
+ focusable_widgets_list = g_list_append(focusable_widgets_list,TitleEntry);
+ focusable_widgets_list = g_list_append(focusable_widgets_list,TitleMButton);
+ focusable_widgets_list = g_list_append(focusable_widgets_list,ArtistEntry);
+ focusable_widgets_list = g_list_append(focusable_widgets_list,ArtistMButton);
+ focusable_widgets_list = g_list_append(focusable_widgets_list,AlbumEntry);
+ focusable_widgets_list = g_list_append(focusable_widgets_list,AlbumMButton);
+ focusable_widgets_list = g_list_append(focusable_widgets_list,DiscNumberEntry);
+ focusable_widgets_list = g_list_append(focusable_widgets_list,DiscNumberMButton);
+ focusable_widgets_list = g_list_append(focusable_widgets_list,YearEntry);
+ focusable_widgets_list = g_list_append(focusable_widgets_list,YearMButton);
+ //focusable_widgets_list = g_list_append(focusable_widgets_list,TrackMButtonSequence); // Doesn't work as focus disabled for this widget to have enought space to display icon
+ focusable_widgets_list = g_list_append(focusable_widgets_list,TrackEntryCombo);
+ //focusable_widgets_list = g_list_append(focusable_widgets_list,TrackMButtonNbrFiles);
+ focusable_widgets_list = g_list_append(focusable_widgets_list,TrackTotalEntry);
+ focusable_widgets_list = g_list_append(focusable_widgets_list,TrackMButton);
+ focusable_widgets_list = g_list_append(focusable_widgets_list,GenreCombo);
+ focusable_widgets_list = g_list_append(focusable_widgets_list,GenreMButton);
+ focusable_widgets_list = g_list_append(focusable_widgets_list,CommentEntry);
+ focusable_widgets_list = g_list_append(focusable_widgets_list,CommentMButton);
+ focusable_widgets_list = g_list_append(focusable_widgets_list,ComposerEntry);
+ focusable_widgets_list = g_list_append(focusable_widgets_list,ComposerMButton);
+ focusable_widgets_list = g_list_append(focusable_widgets_list,OrigArtistEntry);
+ focusable_widgets_list = g_list_append(focusable_widgets_list,OrigArtistMButton);
+ focusable_widgets_list = g_list_append(focusable_widgets_list,CopyrightEntry);
+ focusable_widgets_list = g_list_append(focusable_widgets_list,CopyrightMButton);
+ focusable_widgets_list = g_list_append(focusable_widgets_list,URLEntry);
+ focusable_widgets_list = g_list_append(focusable_widgets_list,URLMButton);
+ focusable_widgets_list = g_list_append(focusable_widgets_list,EncodedByEntry);
+ focusable_widgets_list = g_list_append(focusable_widgets_list,EncodedByMButton);
+ focusable_widgets_list = g_list_append(focusable_widgets_list,TitleEntry); // To loop to the beginning
+ gtk_container_set_focus_chain(GTK_CONTAINER(Table),focusable_widgets_list);
+
+
+
+ /*
+ * 2 - Page for extra tag fields
+ */
+ Label = gtk_label_new(_("Pictures")); // As there is only the picture field... - also used in ET_Display_File_Tag_To_UI
+ Frame = gtk_frame_new(NULL);
+ gtk_notebook_append_page(GTK_NOTEBOOK(TagNoteBook),Frame,Label);
+ gtk_container_set_border_width(GTK_CONTAINER(Frame), 0);
+ gtk_frame_set_shadow_type(GTK_FRAME(Frame),GTK_SHADOW_NONE); // Hide the Frame
+
+ Table = gtk_table_new(3,5,FALSE);
+ gtk_container_add(GTK_CONTAINER(Frame),Table);
+ gtk_container_set_border_width(GTK_CONTAINER(Table),2);
+
+ /* Picture */
+ PictureLabel = gtk_label_new(_("Pictures:"));
+ gtk_table_attach(GTK_TABLE(Table),PictureLabel,0,1,0,1,GTK_FILL,GTK_FILL,TablePadding,TablePadding);
+ gtk_misc_set_alignment(GTK_MISC(PictureLabel),1,0.5);
+
+ // Scroll window for PictureEntryView
+ PictureScrollWindow = gtk_scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(PictureScrollWindow),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+ gtk_table_attach(GTK_TABLE(Table), PictureScrollWindow, 1,4,0,1,
+ GTK_EXPAND|GTK_FILL,GTK_EXPAND|GTK_FILL,TablePadding,TablePadding);
+
+ PictureEntryModel = gtk_list_store_new(PICTURE_COLUMN_COUNT,
+ GDK_TYPE_PIXBUF,
+ G_TYPE_STRING,
+ G_TYPE_POINTER);
+ PictureEntryView = gtk_tree_view_new_with_model(GTK_TREE_MODEL(PictureEntryModel));
+ //gtk_tree_view_set_reorderable(GTK_TREE_VIEW(PictureEntryView),TRUE);
+ gtk_container_add(GTK_CONTAINER(PictureScrollWindow), PictureEntryView);
+ gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(PictureEntryView), FALSE);
+ gtk_widget_set_size_request(PictureEntryView, -1, 200);
+ gtk_tooltips_set_tip(Tips,PictureEntryView,_("You can use drag and drop to add picture."),NULL);
+
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(PictureEntryView));
+ gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
+
+ renderer = gtk_cell_renderer_pixbuf_new();
+ column = gtk_tree_view_column_new();
+ gtk_tree_view_column_pack_start(column, renderer, FALSE);
+ gtk_tree_view_column_set_attributes(column, renderer,
+ "pixbuf", PICTURE_COLUMN_PIC,
+ NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(PictureEntryView), column);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new();
+ gtk_tree_view_column_pack_start(column, renderer, FALSE);
+ gtk_tree_view_column_set_attributes(column, renderer,
+ "text", PICTURE_COLUMN_TEXT,
+ NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(PictureEntryView), column);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+
+ // Activate Drag'n'Drop for the PictureEntryView (and PictureAddButton)
+ gtk_drag_dest_set(GTK_WIDGET(PictureEntryView),
+ GTK_DEST_DEFAULT_HIGHLIGHT | GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP,
+ drops, sizeof(drops) / sizeof(GtkTargetEntry),
+ GDK_ACTION_COPY);
+ g_signal_connect(G_OBJECT(PictureEntryView),"drag_data_received", G_CALLBACK(Tag_Area_Picture_Drag_Data), 0);
+ g_signal_connect(G_OBJECT(selection), "changed", G_CALLBACK(Picture_Selection_Changed_cb), NULL);
+ g_signal_connect(G_OBJECT(PictureEntryView), "button_press_event",G_CALLBACK(Picture_Entry_View_Button_Pressed),NULL);
+ g_signal_connect(G_OBJECT(PictureEntryView),"key_press_event", G_CALLBACK(Picture_Entry_View_Key_Pressed),NULL);
+
+ // The mini button for picture
+ PictureMButton = gtk_button_new();
+ gtk_table_attach(GTK_TABLE(Table),PictureMButton,4,5,0,1,0,0,TablePadding,TablePadding);
+ gtk_widget_set_usize(PictureMButton,MButtonSize,MButtonSize);
+ g_signal_connect(G_OBJECT(PictureMButton),"clicked",G_CALLBACK(Mini_Button_Clicked),NULL);
+ gtk_tooltips_set_tip(Tips,PictureMButton,_("Tag selected files with these pictures"),NULL);
+
+ // Picture action buttons
+ hbox = gtk_hbox_new(FALSE, 4);
+ gtk_table_attach(GTK_TABLE(Table),hbox,1,4,1,2,GTK_FILL,GTK_FILL,TablePadding,TablePadding);
+
+ PictureClearButton = gtk_button_new();
+ Icon = gtk_image_new_from_stock(GTK_STOCK_REMOVE, GTK_ICON_SIZE_SMALL_TOOLBAR);
+ gtk_container_add(GTK_CONTAINER(PictureClearButton),Icon);
+ gtk_box_pack_start(GTK_BOX(hbox),PictureClearButton,FALSE,FALSE,0);
+ gtk_tooltips_set_tip(Tips,PictureClearButton,_("Remove selected pictures, else all pictures."),NULL);
+
+ PictureAddButton = gtk_button_new();
+ Icon = gtk_image_new_from_stock(GTK_STOCK_ADD, GTK_ICON_SIZE_SMALL_TOOLBAR);
+ gtk_container_add(GTK_CONTAINER(PictureAddButton),Icon);
+ gtk_box_pack_start(GTK_BOX(hbox),PictureAddButton,FALSE,FALSE,0);
+ gtk_tooltips_set_tip(Tips,PictureAddButton,_("Add pictures to the tag (drag and drop is also available)."),NULL);
+
+ Label = gtk_label_new(" ");
+ gtk_box_pack_start(GTK_BOX(hbox),Label,FALSE,FALSE,0);
+
+ PictureSaveButton = gtk_button_new();
+ Icon = gtk_image_new_from_stock(GTK_STOCK_SAVE, GTK_ICON_SIZE_SMALL_TOOLBAR);
+ gtk_container_add(GTK_CONTAINER(PictureSaveButton),Icon);
+ gtk_box_pack_start(GTK_BOX(hbox),PictureSaveButton,FALSE,FALSE,0);
+ gtk_widget_set_sensitive(GTK_WIDGET(PictureSaveButton), FALSE);
+ gtk_tooltips_set_tip(Tips,PictureSaveButton,_("Save the selected pictures on the hard disk."),NULL);
+
+ PicturePropertiesButton = gtk_button_new();
+ Icon = gtk_image_new_from_stock(GTK_STOCK_PROPERTIES, GTK_ICON_SIZE_SMALL_TOOLBAR);
+ gtk_container_add(GTK_CONTAINER(PicturePropertiesButton),Icon);
+ gtk_box_pack_start(GTK_BOX(hbox),PicturePropertiesButton,FALSE,FALSE,0);
+ gtk_widget_set_sensitive(GTK_WIDGET(PicturePropertiesButton), FALSE);
+ gtk_tooltips_set_tip(Tips,PicturePropertiesButton,_("Set properties of the selected pictures."),NULL);
+
+ g_signal_connect(G_OBJECT(PictureClearButton), "clicked", G_CALLBACK(Picture_Clear_Button_Clicked), NULL);
+ g_signal_connect(G_OBJECT(PictureAddButton), "clicked", G_CALLBACK(Picture_Add_Button_Clicked), NULL);
+ g_signal_connect(G_OBJECT(PictureSaveButton), "clicked", G_CALLBACK(Picture_Save_Button_Clicked), NULL);
+ g_signal_connect(G_OBJECT(PicturePropertiesButton), "clicked", G_CALLBACK(Picture_Properties_Button_Clicked), NULL);
+
+ // Activate Drag'n'Drop for the PictureAddButton (and PictureEntryView)
+ gtk_drag_dest_set(GTK_WIDGET(PictureAddButton),
+ GTK_DEST_DEFAULT_HIGHLIGHT | GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP,
+ drops, sizeof(drops) / sizeof(GtkTargetEntry),
+ GDK_ACTION_COPY);
+ g_signal_connect(G_OBJECT(PictureAddButton),"drag_data_received", G_CALLBACK(Tag_Area_Picture_Drag_Data), 0);
+
+
+ //Attach_Popup_Menu_To_Tag_Entries(GTK_ENTRY(PictureEntryView));
+ gtk_object_set_data(GTK_OBJECT(PictureEntryView),"MButtonName",PictureMButton);
+
+ gtk_widget_show_all(TagFrame);
+ return TagFrame;
+}
+
+
+
+/*
+ * Actions when mini buttons are pressed: apply the field to all others files
+ */
+void Menu_Mini_Button_Clicked (GtkEntry *entry)
+{
+ if ( g_object_get_data(G_OBJECT(entry),"MButtonName") )
+ Mini_Button_Clicked((GObject *)g_object_get_data(G_OBJECT(entry),"MButtonName"));
+}
+void Mini_Button_Clicked (GObject *object)
+{
+ GList *etfilelist = NULL;
+ GList *selection_filelist = NULL;
+ gchar *string_to_set = NULL;
+ gchar *string_to_set1 = NULL;
+ gchar *msg = NULL;
+ ET_File *etfile;
+ File_Tag *FileTag;
+ GtkTreeSelection *selection;
+
+
+ if (!ETCore->ETFileDisplayedList || !BrowserList) return;
+
+ // Save the current displayed data
+ ET_Save_File_Data_From_UI(ETCore->ETFileDisplayed);
+
+ // Warning : 'selection_filelist' is not a list of 'ETFile' items!
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(BrowserList));
+ selection_filelist = gtk_tree_selection_get_selected_rows(selection, NULL);
+ // Create an 'ETFile' list from 'selection_filelist'
+ while (selection_filelist)
+ {
+ etfile = Browser_List_Get_ETFile_From_Path(selection_filelist->data);
+ etfilelist = g_list_append(etfilelist,etfile);
+
+ if (!selection_filelist->next) break;
+ selection_filelist = g_list_next(selection_filelist);
+ }
+ g_list_foreach(selection_filelist, (GFunc)gtk_tree_path_free, NULL);
+ g_list_free(selection_filelist);
+
+
+ if (object==G_OBJECT(TitleMButton))
+ {
+ string_to_set = gtk_editable_get_chars(GTK_EDITABLE(TitleEntry),0,-1); // The string to apply to all other files
+ while (etfilelist)
+ {
+ etfile = (ET_File *)etfilelist->data;
+ FileTag = ET_File_Tag_Item_New();
+ ET_Copy_File_Tag_Item(etfile,FileTag);
+ ET_Set_Field_File_Tag_Item(&FileTag->title,string_to_set);
+ ET_Manage_Changes_Of_File_Data(etfile,NULL,FileTag);
+
+ if (!etfilelist->next) break;
+ etfilelist = g_list_next(etfilelist);
+ }
+ if (string_to_set != NULL && g_utf8_strlen(string_to_set, -1)>0)
+ msg = g_strdup_printf(_("Selected files tagged with title '%s'."),string_to_set);
+ else
+ msg = g_strdup(_("Removed title from selected files."));
+ }
+ else if (object==G_OBJECT(ArtistMButton))
+ {
+ string_to_set = gtk_editable_get_chars(GTK_EDITABLE(ArtistEntry),0,-1);
+ while (etfilelist)
+ {
+ etfile = (ET_File *)etfilelist->data;
+ FileTag = ET_File_Tag_Item_New();
+ ET_Copy_File_Tag_Item(etfile,FileTag);
+ ET_Set_Field_File_Tag_Item(&FileTag->artist,string_to_set);
+ ET_Manage_Changes_Of_File_Data(etfile,NULL,FileTag);
+
+ if (!etfilelist->next) break;
+ etfilelist = g_list_next(etfilelist);
+ }
+ if (string_to_set != NULL && g_utf8_strlen(string_to_set, -1)>0)
+ msg = g_strdup_printf(_("Selected files tagged with artist '%s'."),string_to_set);
+ else
+ msg = g_strdup(_("Removed artist from selected files."));
+ }
+ else if (object==G_OBJECT(AlbumMButton))
+ {
+ string_to_set = gtk_editable_get_chars(GTK_EDITABLE(AlbumEntry),0,-1);
+ while (etfilelist)
+ {
+ etfile = (ET_File *)etfilelist->data;
+ FileTag = ET_File_Tag_Item_New();
+ ET_Copy_File_Tag_Item(etfile,FileTag);
+ ET_Set_Field_File_Tag_Item(&FileTag->album,string_to_set);
+ ET_Manage_Changes_Of_File_Data(etfile,NULL,FileTag);
+
+ if (!etfilelist->next) break;
+ etfilelist = g_list_next(etfilelist);
+ }
+ if (string_to_set != NULL && g_utf8_strlen(string_to_set, -1)>0)
+ msg = g_strdup_printf(_("Selected files tagged with album '%s'."),string_to_set);
+ else
+ msg = g_strdup(_("Removed album name from selected files."));
+ }
+ else if (object==G_OBJECT(DiscNumberMButton))
+ {
+ string_to_set = gtk_editable_get_chars(GTK_EDITABLE(DiscNumberEntry),0,-1);
+ while (etfilelist)
+ {
+ etfile = (ET_File *)etfilelist->data;
+ FileTag = ET_File_Tag_Item_New();
+ ET_Copy_File_Tag_Item(etfile,FileTag);
+ ET_Set_Field_File_Tag_Item(&FileTag->disc_number,string_to_set);
+ ET_Manage_Changes_Of_File_Data(etfile,NULL,FileTag);
+
+ if (!etfilelist->next) break;
+ etfilelist = g_list_next(etfilelist);
+ }
+ if (string_to_set != NULL && g_utf8_strlen(string_to_set, -1)>0)
+ msg = g_strdup_printf(_("Selected files tagged with disc number '%s'."),string_to_set);
+ else
+ msg = g_strdup(_("Removed disc number from selected files."));
+ }
+ else if (object==G_OBJECT(YearMButton))
+ {
+ string_to_set = gtk_editable_get_chars(GTK_EDITABLE(YearEntry),0,-1);
+ while (etfilelist)
+ {
+ etfile = (ET_File *)etfilelist->data;
+ FileTag = ET_File_Tag_Item_New();
+ ET_Copy_File_Tag_Item(etfile,FileTag);
+ ET_Set_Field_File_Tag_Item(&FileTag->year,string_to_set);
+ ET_Manage_Changes_Of_File_Data(etfile,NULL,FileTag);
+
+ if (!etfilelist->next) break;
+ etfilelist = g_list_next(etfilelist);
+ }
+ if (string_to_set != NULL && g_utf8_strlen(string_to_set, -1)>0)
+ msg = g_strdup_printf(_("Selected files tagged with year '%s'."),string_to_set);
+ else
+ msg = g_strdup(_("Removed year from selected files."));
+ }
+ else if (object==G_OBJECT(TrackMButton))
+ {
+ /* Used of Track and Total Track values */
+ string_to_set = g_strdup(gtk_entry_get_text(GTK_ENTRY(GTK_BIN(TrackEntryCombo)->child)));
+ string_to_set1 = gtk_editable_get_chars(GTK_EDITABLE(TrackTotalEntry),0,-1);
+ while (etfilelist)
+ {
+ etfile = (ET_File *)etfilelist->data;
+ FileTag = ET_File_Tag_Item_New();
+ ET_Copy_File_Tag_Item(etfile,FileTag);
+
+ // We apply the TrackEntry field to all others files only if it is to delete
+ // the field (string=""). Else we don't overwrite the track number
+ if (!string_to_set || g_utf8_strlen(string_to_set, -1) == 0)
+ ET_Set_Field_File_Tag_Item(&FileTag->track,string_to_set);
+ ET_Set_Field_File_Tag_Item(&FileTag->track_total,string_to_set1);
+ ET_Manage_Changes_Of_File_Data(etfile,NULL,FileTag);
+
+ if (!etfilelist->next) break;
+ etfilelist = g_list_next(etfilelist);
+ }
+
+ if ( string_to_set && g_utf8_strlen(string_to_set, -1) > 0 ) //&& atoi(string_to_set)>0 )
+ {
+ if ( string_to_set1 != NULL && g_utf8_strlen(string_to_set1, -1)>0 ) //&& atoi(string_to_set1)>0 )
+ {
+ msg = g_strdup_printf(_("Selected files tagged with track like 'xx/%s'."),string_to_set1);
+ }else
+ {
+ msg = g_strdup_printf(_("Selected files tagged with track like 'xx'."));
+ }
+ }else
+ {
+ msg = g_strdup(_("Removed track number from selected files."));
+ }
+ }
+ else if (object==G_OBJECT(TrackMButtonSequence))
+ {
+ /* This part doesn't set the same track number to all files, but sequence the tracks.
+ * So we must browse the whole 'etfilelistfull' to get position of each selected file.
+ * Note : 'etfilelistfull' and 'etfilelist' must be sorted in the same order */
+ GList *etfilelistfull = NULL;
+ gchar *path = NULL;
+ gchar *path1 = NULL;
+ gint i = 0;
+
+ /* FIX ME!: see to fill also the Total Track (it's a good idea?) */
+ etfilelistfull = g_list_first(ETCore->ETFileList);
+
+ // Sort 'etfilelistfull' and 'etfilelist' in the same order
+ etfilelist = ET_Sort_File_List(etfilelist,SORTING_FILE_MODE);
+ etfilelistfull = ET_Sort_File_List(etfilelistfull,SORTING_FILE_MODE);
+
+ while (etfilelist && etfilelistfull)
+ {
+ // To get the path of the file
+ File_Name *FileNameCur = (File_Name *)((ET_File *)etfilelistfull->data)->FileNameCur->data;
+ // The ETFile in the selected file list
+ etfile = etfilelist->data;
+
+ // Restart counter when entering a new directory
+ g_free(path1);
+ path1 = g_path_get_dirname(FileNameCur->value);
+ if ( path && path1 && strcmp(path,path1)!=0 )
+ i = 0;
+ if (NUMBER_TRACK_FORMATED)
+ string_to_set = g_strdup_printf("%.*d",NUMBER_TRACK_FORMATED_SPIN_BUTTON,++i);
+ else
+ string_to_set = g_strdup_printf("%d",++i);
+
+ // The file is in the selection?
+ if ( (ET_File *)etfilelistfull->data == etfile )
+ {
+ FileTag = ET_File_Tag_Item_New();
+ ET_Copy_File_Tag_Item(etfile,FileTag);
+ ET_Set_Field_File_Tag_Item(&FileTag->track,string_to_set);
+ ET_Manage_Changes_Of_File_Data(etfile,NULL,FileTag);
+
+ if (!etfilelist->next) break;
+ etfilelist = g_list_next(etfilelist);
+ }
+
+ g_free(string_to_set);
+ g_free(path);
+ path = g_strdup(path1);
+
+ etfilelistfull = g_list_next(etfilelistfull);
+ }
+ g_free(path);
+ g_free(path1);
+ //msg = g_strdup_printf(_("All %d tracks numbered sequentially."), ETCore->ETFileSelectionList_Length);
+ msg = g_strdup_printf(_("Selected tracks numbered sequentially."));
+ }
+ else if (object==G_OBJECT(TrackMButtonNbrFiles))
+ {
+ /* Used of Track and Total Track values */
+ while (etfilelist)
+ {
+ gchar *path_utf8, *filename_utf8;
+
+ etfile = (ET_File *)etfilelist->data;
+ filename_utf8 = ((File_Name *)etfile->FileNameNew->data)->value_utf8;
+ path_utf8 = g_path_get_dirname(filename_utf8);
+ if (NUMBER_TRACK_FORMATED)
+ string_to_set = g_strdup_printf("%.*d",NUMBER_TRACK_FORMATED_SPIN_BUTTON,ET_Get_Number_Of_Files_In_Directory(path_utf8));
+ else
+ string_to_set = g_strdup_printf("%d",ET_Get_Number_Of_Files_In_Directory(path_utf8));
+ g_free(path_utf8);
+ if (!string_to_set1)
+ string_to_set1 = g_strdup(string_to_set); // Just for the message below...
+
+ FileTag = ET_File_Tag_Item_New();
+ ET_Copy_File_Tag_Item(etfile,FileTag);
+ ET_Set_Field_File_Tag_Item(&FileTag->track_total,string_to_set);
+ ET_Manage_Changes_Of_File_Data(etfile,NULL,FileTag);
+
+ if (!etfilelist->next) break;
+ etfilelist = g_list_next(etfilelist);
+ }
+
+ if ( string_to_set1 != NULL && g_utf8_strlen(string_to_set1, -1)>0 ) //&& atoi(string_to_set1)>0 )
+ {
+ msg = g_strdup_printf(_("Selected files tagged with track like 'xx/%s'."),string_to_set1);
+ }else
+ {
+ msg = g_strdup(_("Removed track number from selected files."));
+ }
+ }
+ else if (object==G_OBJECT(GenreMButton))
+ {
+ string_to_set = gtk_editable_get_chars(GTK_EDITABLE(GTK_BIN(GenreCombo)->child),0,-1);
+ while (etfilelist)
+ {
+ etfile = (ET_File *)etfilelist->data;
+ FileTag = ET_File_Tag_Item_New();
+ ET_Copy_File_Tag_Item(etfile,FileTag);
+ ET_Set_Field_File_Tag_Item(&FileTag->genre,string_to_set);
+ ET_Manage_Changes_Of_File_Data(etfile,NULL,FileTag);
+
+ if (!etfilelist->next) break;
+ etfilelist = g_list_next(etfilelist);
+ }
+ if (string_to_set != NULL && g_utf8_strlen(string_to_set, -1)>0)
+ msg = g_strdup_printf(_("Selected files tagged with genre '%s'."),string_to_set);
+ else
+ msg = g_strdup(_("Removed genre from selected files."));
+ }
+ else if (object==G_OBJECT(CommentMButton))
+ {
+ //GtkTextBuffer *textbuffer;
+ //GtkTextIter start_iter;
+ //GtkTextIter end_iter;
+ //textbuffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(CommentView));
+ //gtk_text_buffer_get_bounds(GTK_TEXT_BUFFER(textbuffer),&start_iter,&end_iter);
+ //string_to_set = gtk_text_buffer_get_text(GTK_TEXT_BUFFER(textbuffer),&start_iter,&end_iter,TRUE);
+
+ string_to_set = gtk_editable_get_chars(GTK_EDITABLE(CommentEntry),0,-1);
+ while (etfilelist)
+ {
+ etfile = (ET_File *)etfilelist->data;
+ FileTag = ET_File_Tag_Item_New();
+ ET_Copy_File_Tag_Item(etfile,FileTag);
+ ET_Set_Field_File_Tag_Item(&FileTag->comment,string_to_set);
+ ET_Manage_Changes_Of_File_Data(etfile,NULL,FileTag);
+
+ if (!etfilelist->next) break;
+ etfilelist = g_list_next(etfilelist);
+ }
+ if (string_to_set != NULL && g_utf8_strlen(string_to_set, -1)>0)
+ msg = g_strdup_printf(_("Selected files tagged with comment '%s'."),string_to_set);
+ else
+ msg = g_strdup(_("Removed comment from selected files."));
+ }
+ else if (object==G_OBJECT(ComposerMButton))
+ {
+ string_to_set = gtk_editable_get_chars(GTK_EDITABLE(ComposerEntry),0,-1);
+ while (etfilelist)
+ {
+ etfile = (ET_File *)etfilelist->data;
+ FileTag = ET_File_Tag_Item_New();
+ ET_Copy_File_Tag_Item(etfile,FileTag);
+ ET_Set_Field_File_Tag_Item(&FileTag->composer,string_to_set);
+ ET_Manage_Changes_Of_File_Data(etfile,NULL,FileTag);
+
+ if (!etfilelist->next) break;
+ etfilelist = g_list_next(etfilelist);
+ }
+ if (string_to_set != NULL && g_utf8_strlen(string_to_set, -1)>0)
+ msg = g_strdup_printf(_("Selected files tagged with composer '%s'."),string_to_set);
+ else
+ msg = g_strdup(_("Removed composer from selected files."));
+ }
+ else if (object==G_OBJECT(OrigArtistMButton))
+ {
+ string_to_set = gtk_editable_get_chars(GTK_EDITABLE(OrigArtistEntry),0,-1);
+ while (etfilelist)
+ {
+ etfile = (ET_File *)etfilelist->data;
+ FileTag = ET_File_Tag_Item_New();
+ ET_Copy_File_Tag_Item(etfile,FileTag);
+ ET_Set_Field_File_Tag_Item(&FileTag->orig_artist,string_to_set);
+ ET_Manage_Changes_Of_File_Data(etfile,NULL,FileTag);
+
+ if (!etfilelist->next) break;
+ etfilelist = g_list_next(etfilelist);
+ }
+ if (string_to_set != NULL && g_utf8_strlen(string_to_set, -1)>0)
+ msg = g_strdup_printf(_("Selected files tagged with original artist '%s'."),string_to_set);
+ else
+ msg = g_strdup(_("Removed original artist from selected files."));
+ }
+ else if (object==G_OBJECT(CopyrightMButton))
+ {
+ string_to_set = gtk_editable_get_chars(GTK_EDITABLE(CopyrightEntry),0,-1);
+ while (etfilelist)
+ {
+ etfile = (ET_File *)etfilelist->data;
+ FileTag = ET_File_Tag_Item_New();
+ ET_Copy_File_Tag_Item(etfile,FileTag);
+ ET_Set_Field_File_Tag_Item(&FileTag->copyright,string_to_set);
+ ET_Manage_Changes_Of_File_Data(etfile,NULL,FileTag);
+
+ if (!etfilelist->next) break;
+ etfilelist = g_list_next(etfilelist);
+ }
+ if (string_to_set != NULL && g_utf8_strlen(string_to_set, -1)>0)
+ msg = g_strdup_printf(_("Selected files tagged with copyright '%s'."),string_to_set);
+ else
+ msg = g_strdup(_("Removed copyright from selected files."));
+ }
+ else if (object==G_OBJECT(URLMButton))
+ {
+ string_to_set = gtk_editable_get_chars(GTK_EDITABLE(URLEntry),0,-1);
+ while (etfilelist)
+ {
+ etfile = (ET_File *)etfilelist->data;
+ FileTag = ET_File_Tag_Item_New();
+ ET_Copy_File_Tag_Item(etfile,FileTag);
+ ET_Set_Field_File_Tag_Item(&FileTag->url,string_to_set);
+ ET_Manage_Changes_Of_File_Data(etfile,NULL,FileTag);
+
+ if (!etfilelist->next) break;
+ etfilelist = g_list_next(etfilelist);
+ }
+ if (string_to_set != NULL && g_utf8_strlen(string_to_set, -1)>0)
+ msg = g_strdup_printf(_("Selected files tagged with URL '%s'."),string_to_set);
+ else
+ msg = g_strdup(_("Removed URL from selected files."));
+ }
+ else if (object==G_OBJECT(EncodedByMButton))
+ {
+ string_to_set = gtk_editable_get_chars(GTK_EDITABLE(EncodedByEntry),0,-1);
+ while (etfilelist)
+ {
+ etfile = (ET_File *)etfilelist->data;
+ FileTag = ET_File_Tag_Item_New();
+ ET_Copy_File_Tag_Item(etfile,FileTag);
+ ET_Set_Field_File_Tag_Item(&FileTag->encoded_by,string_to_set);
+ ET_Manage_Changes_Of_File_Data(etfile,NULL,FileTag);
+
+ if (!etfilelist->next) break;
+ etfilelist = g_list_next(etfilelist);
+ }
+ if (string_to_set != NULL && g_utf8_strlen(string_to_set, -1)>0)
+ msg = g_strdup_printf(_("Selected files tagged with encoder name '%s'."),string_to_set);
+ else
+ msg = g_strdup(_("Removed encoder name from selected files."));
+ }
+ else if (object==G_OBJECT(PictureMButton))
+ {
+ Picture *res = NULL, *pic, *prev_pic = NULL;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ model = gtk_tree_view_get_model(GTK_TREE_VIEW(PictureEntryView));
+ if (gtk_tree_model_get_iter_first(model, &iter))
+ {
+ do
+ {
+ gtk_tree_model_get(model, &iter, PICTURE_COLUMN_DATA, &pic, -1);
+ pic = Picture_Copy_One(pic);
+ if (!res)
+ res = pic;
+ else
+ prev_pic->next = pic;
+ prev_pic = pic;
+ } while (gtk_tree_model_iter_next(model, &iter));
+ }
+
+ while (etfilelist)
+ {
+ etfile = (ET_File *)etfilelist->data;
+ FileTag = ET_File_Tag_Item_New();
+ ET_Copy_File_Tag_Item(etfile,FileTag);
+ ET_Set_Field_File_Tag_Picture((Picture **)&FileTag->picture, res);
+ ET_Manage_Changes_Of_File_Data(etfile,NULL,FileTag);
+
+ if (!etfilelist->next) break;
+ etfilelist = g_list_next(etfilelist);
+ }
+ if (res)
+ msg = g_strdup(_("Selected files tagged with pictures."));
+ else
+ msg = g_strdup(_("Removed pictures from selected files."));
+ Picture_Free(res);
+ }
+
+ g_list_free(etfilelist);
+
+ // Refresh the whole list (faster than file by file) to show changes
+ Browser_List_Refresh_Whole_List();
+
+ /* Display the current file (Needed when sequencing tracks) */
+ ET_Display_File_Data_To_UI(ETCore->ETFileDisplayed);
+
+ if (msg)
+ {
+ Log_Print("%s",msg);
+ Statusbar_Message(msg,TRUE);
+ g_free(msg);
+ }
+ g_free(string_to_set);
+ g_free(string_to_set1);
+
+ /* To update state of Undo button */
+ Update_Command_Buttons_Sensivity();
+}
+
+
+
+
+/*
+ * Action when selecting all files
+ */
+void Action_Select_All_Files (void)
+{
+ /* Save the current displayed data */
+ ET_Save_File_Data_From_UI(ETCore->ETFileDisplayed);
+
+ Browser_List_Select_All_Files();
+ Update_Command_Buttons_Sensivity();
+}
+
+
+/*
+ * Action when unselecting all files
+ */
+void Action_Unselect_All_Files (void)
+{
+ /* Save the current displayed data */
+ ET_Save_File_Data_From_UI(ETCore->ETFileDisplayed);
+
+ Browser_List_Unselect_All_Files();
+ ETCore->ETFileDisplayed = NULL;
+}
+
+
+/*
+ * Action when inverting files selection
+ */
+void Action_Invert_Files_Selection (void)
+{
+ /* Save the current displayed data */
+ ET_Save_File_Data_From_UI(ETCore->ETFileDisplayed);
+
+ Browser_List_Invert_File_Selection();
+ Update_Command_Buttons_Sensivity();
+}
+
+
+
+/*
+ * Action when First button is selected
+ */
+void Action_Select_First_File (void)
+{
+ GList *etfilelist;
+
+ if (!ETCore->ETFileDisplayedList)
+ return;
+
+ /* Save the current displayed data */
+ ET_Save_File_Data_From_UI(ETCore->ETFileDisplayed);
+
+ /* Go to the first item of the list */
+ etfilelist = ET_Displayed_File_List_First();
+ if (etfilelist)
+ {
+ Browser_List_Unselect_All_Files(); // To avoid the last line still selected
+ Browser_List_Select_File_By_Etfile((ET_File *)etfilelist->data,TRUE);
+ ET_Display_File_Data_To_UI((ET_File *)etfilelist->data);
+ }
+
+ Update_Command_Buttons_Sensivity();
+ Scan_Rename_File_Generate_Preview();
+ Scan_Fill_Tag_Generate_Preview();
+}
+
+
+/*
+ * Action when Prev button is selected
+ */
+void Action_Select_Prev_File (void)
+{
+ GList *etfilelist;
+
+ if (!ETCore->ETFileDisplayedList || !ETCore->ETFileDisplayedList->prev)
+ return;
+
+ /* Save the current displayed data */
+ ET_Save_File_Data_From_UI(ETCore->ETFileDisplayed);
+
+ /* Go to the prev item of the list */
+ etfilelist = ET_Displayed_File_List_Previous();
+ if (etfilelist)
+ {
+ Browser_List_Unselect_All_Files();
+ Browser_List_Select_File_By_Etfile((ET_File *)etfilelist->data,TRUE);
+ ET_Display_File_Data_To_UI((ET_File *)etfilelist->data);
+ }
+
+// if (!ETFileList->prev)
+// gdk_beep(); // Warm the user
+
+ Update_Command_Buttons_Sensivity();
+ Scan_Rename_File_Generate_Preview();
+ Scan_Fill_Tag_Generate_Preview();
+
+ if (SET_FOCUS_TO_FIRST_TAG_FIELD)
+ gtk_widget_grab_focus(GTK_WIDGET(TitleEntry));
+}
+
+
+/*
+ * Action when Next button is selected
+ */
+void Action_Select_Next_File (void)
+{
+ GList *etfilelist;
+
+ if (!ETCore->ETFileDisplayedList || !ETCore->ETFileDisplayedList->next)
+ return;
+
+ /* Save the current displayed data */
+ ET_Save_File_Data_From_UI(ETCore->ETFileDisplayed);
+
+ /* Go to the next item of the list */
+ etfilelist = ET_Displayed_File_List_Next();
+ if (etfilelist)
+ {
+ Browser_List_Unselect_All_Files();
+ Browser_List_Select_File_By_Etfile((ET_File *)etfilelist->data,TRUE);
+ ET_Display_File_Data_To_UI((ET_File *)etfilelist->data);
+ }
+
+// if (!ETFileList->next)
+// gdk_beep(); // Warm the user
+
+ Update_Command_Buttons_Sensivity();
+ Scan_Rename_File_Generate_Preview();
+ Scan_Fill_Tag_Generate_Preview();
+
+ if (SET_FOCUS_TO_FIRST_TAG_FIELD)
+ gtk_widget_grab_focus(GTK_WIDGET(TitleEntry));
+}
+
+
+/*
+ * Action when Last button is selected
+ */
+void Action_Select_Last_File (void)
+{
+ GList *etfilelist;
+
+ if (!ETCore->ETFileDisplayedList || !ETCore->ETFileDisplayedList->next)
+ return;
+
+ /* Save the current displayed data */
+ ET_Save_File_Data_From_UI(ETCore->ETFileDisplayed);
+
+ /* Go to the last item of the list */
+ etfilelist = ET_Displayed_File_List_Last();
+ if (etfilelist)
+ {
+ Browser_List_Unselect_All_Files();
+ Browser_List_Select_File_By_Etfile((ET_File *)etfilelist->data,TRUE);
+ ET_Display_File_Data_To_UI((ET_File *)etfilelist->data);
+ }
+
+ Update_Command_Buttons_Sensivity();
+ Scan_Rename_File_Generate_Preview();
+ Scan_Fill_Tag_Generate_Preview();
+}
+
+
+/*
+ * Select a file in the "main list" using the ETFile adress of each item.
+ */
+void Action_Select_Nth_File_By_Etfile (ET_File *ETFile)
+{
+ if (!ETCore->ETFileDisplayedList)
+ return;
+
+ /* Save the current displayed data */
+ ET_Save_File_Data_From_UI(ETCore->ETFileDisplayed);
+
+ /* Display the item */
+ Browser_List_Select_File_By_Etfile(ETFile,TRUE);
+ ET_Displayed_File_List_By_Etfile(ETFile); // Just to update 'ETFileDisplayedList'
+ ET_Display_File_Data_To_UI(ETFile);
+
+ Update_Command_Buttons_Sensivity();
+ Scan_Rename_File_Generate_Preview();
+ Scan_Fill_Tag_Generate_Preview();
+}
+
+
+/*
+ * Action when Scan button is pressed
+ */
+void Action_Scan_Selected_Files (void)
+{
+ gint progress_bar_index;
+ gint selectcount;
+ gchar progress_bar_text[30];
+ double fraction;
+ GList *selfilelist = NULL;
+ ET_File *etfile;
+ GtkTreeSelection *selection;
+
+ if (!ETCore->ETFileDisplayedList || !BrowserList) return;
+
+ /* Check if scanner window is opened */
+ if (!ScannerWindow)
+ {
+ Open_ScannerWindow(SCANNER_TYPE);
+ Statusbar_Message(_("Select Mode and Mask, and redo the same action"),TRUE);
+ return;
+ }
+
+ /* Save the current displayed data */
+ ET_Save_File_Data_From_UI(ETCore->ETFileDisplayed);
+
+ /* Initialize status bar */
+ selectcount = gtk_tree_selection_count_selected_rows(gtk_tree_view_get_selection(GTK_TREE_VIEW(BrowserList)));
+ gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ProgressBar),0);
+ progress_bar_index = 0;
+ g_snprintf(progress_bar_text, 30, "%d/%d", progress_bar_index, selectcount);
+ gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ProgressBar), progress_bar_text);
+
+ /* Set to unsensitive all command buttons (except Quit button) */
+ Disable_Command_Buttons();
+
+ progress_bar_index = 0;
+
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(BrowserList));
+ selfilelist = gtk_tree_selection_get_selected_rows(selection, NULL);
+ while (selfilelist)
+ {
+ etfile = Browser_List_Get_ETFile_From_Path(selfilelist->data);
+
+ // Run the current scanner
+ Scan_Select_Mode_And_Run_Scanner(etfile);
+
+ fraction = (++progress_bar_index) / (double) selectcount;
+ gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ProgressBar), fraction);
+ g_snprintf(progress_bar_text, 30, "%d/%d", progress_bar_index, selectcount);
+ gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ProgressBar), progress_bar_text);
+
+ /* Needed to refresh status bar */
+ while (gtk_events_pending())
+ gtk_main_iteration();
+
+ if (!selfilelist->next) break;
+ selfilelist = g_list_next(selfilelist);
+ }
+
+ g_list_foreach(selfilelist, (GFunc) gtk_tree_path_free, NULL);
+ g_list_free(selfilelist);
+
+ // Refresh the whole list (faster than file by file) to show changes
+ Browser_List_Refresh_Whole_List();
+
+ /* Display the current file */
+ ET_Display_File_Data_To_UI(ETCore->ETFileDisplayed);
+
+ /* To update state of command buttons */
+ Update_Command_Buttons_Sensivity();
+
+ gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ProgressBar), "");
+ gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ProgressBar), 0);
+ Statusbar_Message(_("All tags have been scanned"),TRUE);
+}
+
+
+
+/*
+ * Action when Remove button is pressed
+ */
+void Action_Remove_Selected_Tags (void)
+{
+ GList *selfilelist = NULL;
+ ET_File *etfile;
+ File_Tag *FileTag;
+ gint progress_bar_index;
+ gint selectcount;
+ double fraction;
+ GtkTreeSelection *selection;
+
+ if (!ETCore->ETFileDisplayedList || !BrowserList) return;
+
+ /* Save the current displayed data */
+ ET_Save_File_Data_From_UI(ETCore->ETFileDisplayed);
+
+ /* Initialize status bar */
+ gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ProgressBar), 0.0);
+ selectcount = gtk_tree_selection_count_selected_rows(gtk_tree_view_get_selection(GTK_TREE_VIEW(BrowserList)));
+ progress_bar_index = 0;
+
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(BrowserList));
+ selfilelist = gtk_tree_selection_get_selected_rows(selection, NULL);
+ while (selfilelist)
+ {
+ etfile = Browser_List_Get_ETFile_From_Path(selfilelist->data);
+ FileTag = ET_File_Tag_Item_New();
+ ET_Manage_Changes_Of_File_Data(etfile,NULL,FileTag);
+
+ fraction = (++progress_bar_index) / (double) selectcount;
+ gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ProgressBar), fraction);
+ /* Needed to refresh status bar */
+ while (gtk_events_pending())
+ gtk_main_iteration();
+
+ if (!selfilelist->next) break;
+ selfilelist = g_list_next(selfilelist);
+ }
+
+ g_list_foreach(selfilelist, (GFunc) gtk_tree_path_free, NULL);
+ g_list_free(selfilelist);
+
+ // Refresh the whole list (faster than file by file) to show changes
+ Browser_List_Refresh_Whole_List();
+
+ /* Display the current file */
+ ET_Display_File_Data_To_UI(ETCore->ETFileDisplayed);
+ Update_Command_Buttons_Sensivity();
+
+ gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ProgressBar), 0.0);
+ Statusbar_Message(_("All tags have been removed"),TRUE);
+}
+
+
+
+/*
+ * Action when Undo button is pressed.
+ * Action_Undo_Selected_Files: Undo the last changes for the selected files.
+ * Action_Undo_From_History_List: Undo the changes of the last modified file of the list.
+ */
+gint Action_Undo_Selected_Files (void)
+{
+ GList *selfilelist = NULL;
+ gboolean state = FALSE;
+ ET_File *etfile;
+ GtkTreeSelection *selection;
+
+ if (!ETCore->ETFileDisplayedList || !BrowserList) return FALSE;
+
+ /* Save the current displayed data */
+ ET_Save_File_Data_From_UI(ETCore->ETFileDisplayed);
+
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(BrowserList));
+ selfilelist = gtk_tree_selection_get_selected_rows(selection, NULL);
+ while (selfilelist)
+ {
+ etfile = Browser_List_Get_ETFile_From_Path(selfilelist->data);
+ state |= ET_Undo_File_Data(etfile);
+
+ if (!selfilelist->next) break;
+ selfilelist = g_list_next(selfilelist);
+ }
+
+ g_list_foreach(selfilelist, (GFunc) gtk_tree_path_free, NULL);
+ g_list_free(selfilelist);
+
+ // Refresh the whole list (faster than file by file) to show changes
+ Browser_List_Refresh_Whole_List();
+
+ /* Display the current file */
+ ET_Display_File_Data_To_UI(ETCore->ETFileDisplayed);
+ Update_Command_Buttons_Sensivity();
+
+ //ET_Debug_Print_File_List(ETCore->ETFileList,__FILE__,__LINE__,__FUNCTION__);
+
+ return state;
+}
+
+
+void Action_Undo_From_History_List (void)
+{
+ ET_File *ETFile;
+
+ if (!ETCore->ETFileList) return;
+
+ /* Save the current displayed data */
+ ET_Save_File_Data_From_UI(ETCore->ETFileDisplayed);
+
+ ETFile = ET_Undo_History_File_Data();
+ if (ETFile)
+ {
+ ET_Display_File_Data_To_UI(ETFile);
+ Browser_List_Select_File_By_Etfile(ETFile,TRUE);
+ Browser_List_Refresh_File_In_List(ETFile);
+ }
+
+ Update_Command_Buttons_Sensivity();
+}
+
+
+
+/*
+ * Action when Redo button is pressed.
+ * Action_Redo_Selected_Files: Redo the last changes for the selected files.
+ * Action_Redo_From_History_List: Redo the changes of the last modified file of the list.
+ */
+gint Action_Redo_Selected_File (void)
+{
+ GList *selfilelist = NULL;
+ gboolean state = FALSE;
+ ET_File *etfile;
+ GtkTreeSelection *selection;
+
+ if (!ETCore->ETFileDisplayedList || !BrowserList) return FALSE;
+
+ /* Save the current displayed data */
+ ET_Save_File_Data_From_UI(ETCore->ETFileDisplayed);
+
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(BrowserList));
+ selfilelist = gtk_tree_selection_get_selected_rows(selection, NULL);
+ while (selfilelist)
+ {
+ etfile = Browser_List_Get_ETFile_From_Path(selfilelist->data);
+ state |= ET_Redo_File_Data(etfile);
+
+ if (!selfilelist->next) break;
+ selfilelist = g_list_next(selfilelist);
+ }
+
+ g_list_foreach(selfilelist, (GFunc) gtk_tree_path_free, NULL);
+ g_list_free(selfilelist);
+
+ // Refresh the whole list (faster than file by file) to show changes
+ Browser_List_Refresh_Whole_List();
+
+ /* Display the current file */
+ ET_Display_File_Data_To_UI(ETCore->ETFileDisplayed);
+ Update_Command_Buttons_Sensivity();
+
+ return state;
+}
+
+
+void Action_Redo_From_History_List (void)
+{
+ ET_File *ETFile;
+
+ if (!ETCore->ETFileDisplayedList) return;
+
+ /* Save the current displayed data */
+ ET_Save_File_Data_From_UI(ETCore->ETFileDisplayed);
+
+ ETFile = ET_Redo_History_File_Data();
+ if (ETFile)
+ {
+ ET_Display_File_Data_To_UI(ETFile);
+ Browser_List_Select_File_By_Etfile(ETFile,TRUE);
+ Browser_List_Refresh_File_In_List(ETFile);
+ }
+
+ Update_Command_Buttons_Sensivity();
+}
+
+
+
+
+/*
+ * Action when Save button is pressed
+ */
+void Action_Save_Selected_Files (void)
+{
+ Save_Selected_Files_With_Answer(FALSE);
+}
+
+void Action_Force_Saving_Selected_Files (void)
+{
+ Save_Selected_Files_With_Answer(TRUE);
+}
+
+
+/*
+ * Will save the full list of file (not only the selected files in list)
+ * and check if we must save also only the changed files or all files
+ * (force_saving_files==TRUE)
+ */
+gint Save_All_Files_With_Answer (gboolean force_saving_files)
+{
+ GList *etfilelist;
+
+ if (!ETCore || !ETCore->ETFileList) return FALSE;
+ etfilelist = g_list_first(ETCore->ETFileList);
+ return Save_List_Of_Files(etfilelist,force_saving_files);
+}
+
+/*
+ * Will save only the selected files in the file list
+ */
+gint Save_Selected_Files_With_Answer (gboolean force_saving_files)
+{
+ gint toreturn;
+ GList *etfilelist = NULL;
+ GList *selfilelist = NULL;
+ ET_File *etfile;
+ GtkTreeSelection *selection;
+
+ if (!BrowserList) return FALSE;
+
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(BrowserList));
+ selfilelist = gtk_tree_selection_get_selected_rows(selection, NULL);
+ while (selfilelist)
+ {
+ etfile = Browser_List_Get_ETFile_From_Path(selfilelist->data);
+ etfilelist = g_list_append(etfilelist, etfile);
+
+ if (!selfilelist->next) break;
+ selfilelist = selfilelist->next;
+ }
+ g_list_foreach(selfilelist, (GFunc) gtk_tree_path_free, NULL);
+ g_list_free(selfilelist);
+
+ toreturn = Save_List_Of_Files(etfilelist, force_saving_files);
+ g_list_free(etfilelist);
+ return toreturn;
+}
+
+/*
+ * Save_List_Of_Files: Function to save a list of files.
+ * - force_saving_files = TRUE => force saving the file even if it wasn't changed
+ * - force_saving_files = FALSE => force saving only the changed files
+ */
+gint Save_List_Of_Files (GList *etfilelist, gboolean force_saving_files)
+{
+ gint progress_bar_index;
+ gint saving_answer;
+ gint nb_files_to_save;
+ gchar *msg;
+ gchar progress_bar_text[30];
+ GList *etfilelist_tmp;
+ ET_File *etfile_save_position = NULL;
+ File_Tag *FileTag;
+ File_Name *FileNameNew;
+ double fraction;
+ GtkAction *uiaction;
+ GtkWidget *TBViewMode;
+ GtkTreePath *currentPath = NULL;
+
+ if (!ETCore) return FALSE;
+
+ /* Save the current position in the list */
+ etfile_save_position = ETCore->ETFileDisplayed;
+
+ /* Save the current displayed data */
+ ET_Save_File_Data_From_UI(ETCore->ETFileDisplayed);
+
+ /* Count the number of files to save */
+ nb_files_to_save = 0;
+ etfilelist_tmp = etfilelist;
+ while (etfilelist_tmp)
+ {
+ File_Tag *filetag = ((ET_File *)etfilelist_tmp->data)->FileTag->data;
+ File_Name *filename = ((ET_File *)etfilelist_tmp->data)->FileNameNew->data;
+
+ // Count only the changed files or all files if force_saving_files==TRUE
+ if ( force_saving_files
+ || (filename && filename->saved==FALSE) || (filetag && filetag->saved==FALSE) )
+ nb_files_to_save++;
+ etfilelist_tmp = etfilelist_tmp->next;
+ }
+
+ /* Initialize status bar */
+ gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ProgressBar),0);
+ progress_bar_index = 0;
+ g_snprintf(progress_bar_text, 30, "%d/%d", progress_bar_index, nb_files_to_save);
+ gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ProgressBar), progress_bar_text);
+
+ /* Set to unsensitive all command buttons (except Quit button) */
+ Disable_Command_Buttons();
+ Browser_Area_Set_Sensitive(FALSE);
+ Tag_Area_Set_Sensitive(FALSE);
+ File_Area_Set_Sensitive(FALSE);
+
+ /* Show msgbox (if needed) to ask confirmation */
+ SF_HideMsgbox_Write_Tag = 0;
+ SF_HideMsgbox_Rename_File = 0;
+
+ Main_Stop_Button_Pressed = 0;
+ uiaction = gtk_ui_manager_get_action(UIManager, "/ToolBar/Stop");
+ g_object_set(uiaction, "sensitive", FALSE, NULL);
+
+ etfilelist_tmp = etfilelist;
+ while (etfilelist_tmp)
+ {
+ FileTag = ((ET_File *)etfilelist_tmp->data)->FileTag->data;
+ FileNameNew = ((ET_File *)etfilelist_tmp->data)->FileNameNew->data;
+
+ /* We process only the files changed and not saved, or we force to save all
+ * files if force_saving_files==TRUE */
+ if ( force_saving_files
+ || FileTag->saved == FALSE || FileNameNew->saved == FALSE )
+ {
+ ET_Display_File_Data_To_UI((ET_File *)etfilelist_tmp->data);
+ // Use of 'currentPath' to try to increase speed. Indeed, in many
+ // cases, the next file to select, is the next in the list
+ currentPath = Browser_List_Select_File_By_Etfile2((ET_File *)etfilelist_tmp->data,FALSE,currentPath);
+
+ fraction = (++progress_bar_index) / (double) nb_files_to_save;
+ gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ProgressBar), fraction);
+ g_snprintf(progress_bar_text, 30, "%d/%d", progress_bar_index, nb_files_to_save);
+ gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ProgressBar), progress_bar_text);
+
+ /* Needed to refresh status bar */
+ while (gtk_events_pending())
+ gtk_main_iteration();
+
+ // Save tag and rename file
+ saving_answer = Save_File((ET_File *)etfilelist_tmp->data, (nb_files_to_save>1) ? TRUE : FALSE, force_saving_files);
+
+ if (saving_answer == -1)
+ {
+ /* Stop saving files + reinit progress bar */
+ gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ProgressBar), 0.0);
+ Statusbar_Message(_("All files have been saved..."),TRUE);
+ /* To update state of command buttons */
+ Update_Command_Buttons_Sensivity();
+ Browser_Area_Set_Sensitive(TRUE);
+ Tag_Area_Set_Sensitive(TRUE);
+ File_Area_Set_Sensitive(TRUE);
+
+ return -1; /* We stop all actions */
+ }
+ }
+
+ etfilelist_tmp = etfilelist_tmp->next;
+ if (Main_Stop_Button_Pressed == 1 )
+ break;
+
+ }
+
+ if (currentPath)
+ gtk_tree_path_free(currentPath);
+
+ if (Main_Stop_Button_Pressed)
+ msg = g_strdup(_("Files have been partially saved..."));
+ else
+ msg = g_strdup(_("All files have been saved..."));
+
+ Main_Stop_Button_Pressed = 0;
+ uiaction = gtk_ui_manager_get_action(UIManager, "/ToolBar/Stop");
+ g_object_set(uiaction, "sensitive", FALSE, NULL);
+
+ /* Return to the saved position in the list */
+ ET_Display_File_Data_To_UI(etfile_save_position);
+ Browser_List_Select_File_By_Etfile(etfile_save_position,TRUE);
+
+ /* Browser is on mode : Artist + Album list */
+ TBViewMode = gtk_ui_manager_get_widget(UIManager, "/ToolBar/ViewModeToggle");
+ if ( gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(TBViewMode)) )
+ Browser_Display_Tree_Or_Artist_Album_List();
+
+ /* To update state of command buttons */
+ Update_Command_Buttons_Sensivity();
+ Browser_Area_Set_Sensitive(TRUE);
+ Tag_Area_Set_Sensitive(TRUE);
+ File_Area_Set_Sensitive(TRUE);
+
+ gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ProgressBar), "");
+ gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ProgressBar), 0);
+ Statusbar_Message(msg,TRUE);
+ g_free(msg);
+
+ return TRUE;
+}
+
+
+
+/*
+ * Delete a file on the hard disk
+ */
+void Action_Delete_Selected_Files (void)
+{
+ Delete_Selected_Files_With_Answer();
+}
+
+
+gint Delete_Selected_Files_With_Answer (void)
+{
+ GList *selfilelist;
+ GList *rowreflist = NULL;
+ gint progress_bar_index;
+ gint saving_answer;
+ gint nb_files_to_delete;
+ gint nb_files_deleted = 0;
+ gchar *msg;
+ gchar progress_bar_text[30];
+ double fraction;
+ GtkTreeModel *treemodel;
+ GtkTreeRowReference *rowref;
+ GtkTreeSelection *selection;
+
+ if (!ETCore->ETFileDisplayedList || !BrowserList) return FALSE;
+
+ /* Save the current displayed data */
+ ET_Save_File_Data_From_UI(ETCore->ETFileDisplayed);
+
+ /* Number of files to save */
+ nb_files_to_delete = gtk_tree_selection_count_selected_rows(gtk_tree_view_get_selection(GTK_TREE_VIEW(BrowserList)));
+
+ /* Initialize status bar */
+ gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ProgressBar),0);
+ progress_bar_index = 0;
+ g_snprintf(progress_bar_text, 30, "%d/%d", progress_bar_index, nb_files_to_delete);
+ gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ProgressBar), progress_bar_text);
+
+ /* Set to unsensitive all command buttons (except Quit button) */
+ Disable_Command_Buttons();
+ Browser_Area_Set_Sensitive(FALSE);
+ Tag_Area_Set_Sensitive(FALSE);
+ File_Area_Set_Sensitive(FALSE);
+
+ /* Show msgbox (if needed) to ask confirmation */
+ SF_HideMsgbox_Delete_File = 0;
+
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(BrowserList));
+ selfilelist = gtk_tree_selection_get_selected_rows(selection, &treemodel);
+ while (selfilelist)
+ {
+ rowref = gtk_tree_row_reference_new(treemodel, selfilelist->data);
+ rowreflist = g_list_append(rowreflist, rowref);
+
+ if (!selfilelist->next) break;
+ selfilelist = selfilelist->next;
+ }
+
+ while (rowreflist)
+ {
+ GtkTreePath *path;
+ ET_File *ETFile;
+
+ path = gtk_tree_row_reference_get_path(rowreflist->data);
+ ETFile = Browser_List_Get_ETFile_From_Path(path);
+ gtk_tree_path_free(path);
+
+ ET_Display_File_Data_To_UI(ETFile);
+ Browser_List_Select_File_By_Etfile(ETFile,FALSE);
+ fraction = (++progress_bar_index) / (double) nb_files_to_delete;
+ gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ProgressBar), fraction);
+ g_snprintf(progress_bar_text, 30, "%d/%d", progress_bar_index, nb_files_to_delete);
+ gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ProgressBar), progress_bar_text);
+ /* Needed to refresh status bar */
+ while (gtk_events_pending())
+ gtk_main_iteration();
+
+ saving_answer = Delete_File(ETFile,(nb_files_to_delete>1)?TRUE:FALSE);
+
+ switch (saving_answer)
+ {
+ case 1:
+ nb_files_deleted += saving_answer;
+ // Remove file in the browser (corresponding line in the clist)
+ Browser_List_Remove_File(ETFile);
+ // Remove file from file list
+ ET_Remove_File_From_File_List(ETFile);
+ break;
+ case 0:
+ break;
+ case -1:
+ // Stop deleting files + reinit progress bar
+ gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ProgressBar),0.0);
+ // To update state of command buttons
+ Update_Command_Buttons_Sensivity();
+ Browser_Area_Set_Sensitive(TRUE);
+ Tag_Area_Set_Sensitive(TRUE);
+ File_Area_Set_Sensitive(TRUE);
+
+ g_list_foreach(selfilelist, (GFunc) gtk_tree_path_free, NULL);
+ g_list_free(selfilelist);
+ return -1; // We stop all actions
+ }
+
+ if (!rowreflist->next) break;
+ rowreflist = rowreflist->next;
+ }
+
+ g_list_foreach(selfilelist, (GFunc) gtk_tree_path_free, NULL);
+ g_list_free(selfilelist);
+ g_list_foreach(rowreflist, (GFunc) gtk_tree_row_reference_free, NULL);
+ g_list_free(rowreflist);
+
+ if (nb_files_deleted < nb_files_to_delete)
+ msg = g_strdup(_("Files have been partially deleted..."));
+ else
+ msg = g_strdup(_("All files have been deleted..."));
+
+ // It's important to displayed the new item, as it'll check the changes in Browser_Display_Tree_Or_Artist_Album_List
+ if (ETCore->ETFileDisplayed)
+ ET_Display_File_Data_To_UI(ETCore->ETFileDisplayed);
+ /*else if (ET_Displayed_File_List_Current())
+ ET_Display_File_Data_To_UI((ET_File *)ET_Displayed_File_List_Current()->data);*/
+
+ // Load list...
+ Browser_List_Load_File_List(ETCore->ETFileDisplayedList, NULL);
+ // Rebuild the list...
+ Browser_Display_Tree_Or_Artist_Album_List();
+
+ /* To update state of command buttons */
+ Update_Command_Buttons_Sensivity();
+ Browser_Area_Set_Sensitive(TRUE);
+ Tag_Area_Set_Sensitive(TRUE);
+ File_Area_Set_Sensitive(TRUE);
+
+ gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ProgressBar), "");
+ gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ProgressBar), 0);
+ Statusbar_Message(msg,TRUE);
+ g_free(msg);
+
+ return TRUE;
+}
+
+
+
+/*
+ * Save changes of the ETFile (write tag and rename file)
+ * - multiple_files = TRUE : when saving files, a msgbox appears with ability
+ * to do the same action for all files.
+ * - multiple_files = FALSE : appears only a msgbox to ask confirmation.
+ */
+gint Save_File (ET_File *ETFile, gboolean multiple_files, gboolean force_saving_files)
+{
+ File_Tag *FileTag;
+ File_Name *FileNameNew;
+ gchar *msg = NULL;
+ gchar *msg_title = NULL;
+ gint stop_loop = 0;
+ gchar *filename_cur_utf8 = ((File_Name *)ETFile->FileNameCur->data)->value_utf8;
+ gchar *filename_new_utf8 = ((File_Name *)ETFile->FileNameNew->data)->value_utf8;
+ gchar *basename_cur_utf8, *basename_new_utf8;
+ gchar *dirname_cur_utf8, *dirname_new_utf8;
+
+
+ if (!ETFile) return 0;
+
+ basename_cur_utf8 = g_path_get_basename(filename_cur_utf8);
+ basename_new_utf8 = g_path_get_basename(filename_new_utf8);
+
+ /* Save the current displayed data */
+ // Not needed, done before //ET_Save_File_Data_From_UI((ET_File *)ETFileList->data);
+ FileTag = ETFile->FileTag->data;
+ FileNameNew = ETFile->FileNameNew->data;
+
+ /*
+ * First part: write tag informations (artist, title,...)
+ */
+ // Note : the option 'force_saving_files' is only used to save tags
+ if ( force_saving_files
+ || FileTag->saved == FALSE ) // This tag had been already saved ?
+ {
+ GtkWidget *msgbox = NULL;
+ gint button;
+
+ if (CONFIRM_WRITE_TAG && !SF_HideMsgbox_Write_Tag)
+ {
+ ET_Display_File_Data_To_UI(ETFile);
+
+ msg = g_strdup_printf(_("Do you want to write the tag of file\n'%s' ?"),basename_cur_utf8);
+
+ if (multiple_files)
+ {
+ msgbox = msg_box_new(_("Write Tag..."),msg,GTK_STOCK_DIALOG_QUESTION,
+ BUTTON_CANCEL,BUTTON_NO,BUTTON_YES,0);
+ msg_box_check_button_set_active(MSG_BOX(msgbox),TRUE); // Checked by default
+ }else
+ {
+ msgbox = msg_box_new(_("Write Tag..."),msg,GTK_STOCK_DIALOG_QUESTION,
+ BUTTON_NO,BUTTON_YES,0);
+ msg_box_hide_check_button(MSG_BOX(msgbox));
+ }
+ g_free(msg);
+
+ SF_ButtonPressed_Write_Tag = button = msg_box_run(MSG_BOX(msgbox));
+ /* When check button in msgbox was activated : do not display the message again */
+ if (msg_box_check_button_get_active(MSG_BOX(msgbox)))
+ SF_HideMsgbox_Write_Tag = MSG_BOX(msgbox)->check_button_state;
+ gtk_widget_destroy(msgbox);
+ }else
+ {
+ if (SF_HideMsgbox_Write_Tag)
+ button = SF_ButtonPressed_Write_Tag;
+ else
+ button = BUTTON_YES;
+ }
+
+ switch (button)
+ {
+ case BUTTON_YES:
+ Write_File_Tag(ETFile);
+ break;
+ case BUTTON_NO:
+ break;
+ case BUTTON_CANCEL:
+ case -1:
+ stop_loop = -1;
+ return stop_loop;
+ break;
+ }
+ }
+
+
+ /*
+ * Second part: rename the file
+ */
+ // Do only if changed! (don't take force_saving_files into account)
+ if ( FileNameNew->saved == FALSE ) // This filename had been already saved ?
+ {
+ GtkWidget *msgbox = NULL;
+ gint button;
+
+ if (CONFIRM_RENAME_FILE && !SF_HideMsgbox_Rename_File)
+ {
+ ET_Display_File_Data_To_UI(ETFile);
+
+ dirname_cur_utf8 = g_path_get_dirname(filename_cur_utf8);
+ dirname_new_utf8 = g_path_get_dirname(filename_new_utf8);
+
+ // Directories were renamed? or only filename?
+ if (g_utf8_collate(dirname_cur_utf8,dirname_new_utf8) != 0)
+ {
+ if (g_utf8_collate(basename_cur_utf8,basename_new_utf8) != 0)
+ {
+ // Directories and filename changed
+ msg_title = g_strdup(_("Rename File and Directory..."));
+ msg = g_strdup_printf(_("Do you want to rename the file and directory \n'%s'\nto \n'%s' ?"),
+ filename_cur_utf8, filename_new_utf8);
+ }else
+ {
+ // Only directories changed
+ msg_title = g_strdup(_("Rename Directory..."));
+ msg = g_strdup_printf(_("Do you want to rename the directory \n'%s'\nto \n'%s' ?"),
+ dirname_cur_utf8, dirname_new_utf8);
+ }
+ }else
+ {
+ // Only filename changed
+ msg_title = g_strdup(_("Rename File..."));
+ msg = g_strdup_printf(_("Do you want to rename the file \n'%s'\nto \n'%s' ?"),
+ basename_cur_utf8, basename_new_utf8);
+ }
+
+ g_free(dirname_cur_utf8);
+ g_free(dirname_new_utf8);
+
+ if (multiple_files)
+ {
+ // Allow to cancel for all other files
+ msgbox = msg_box_new(msg_title,msg,GTK_STOCK_DIALOG_QUESTION,
+ BUTTON_CANCEL,BUTTON_NO,BUTTON_YES,0);
+ msg_box_check_button_set_active(MSG_BOX(msgbox),TRUE); // Checked by default
+ }else
+ {
+ msgbox = msg_box_new(msg_title,msg,GTK_STOCK_DIALOG_QUESTION,
+ BUTTON_NO,BUTTON_YES,0);
+ msg_box_hide_check_button(MSG_BOX(msgbox));
+ }
+ g_free(msg);
+ g_free(msg_title);
+ SF_ButtonPressed_Rename_File = button = msg_box_run(MSG_BOX(msgbox));
+ if (msg_box_check_button_get_active(MSG_BOX(msgbox)))
+ SF_HideMsgbox_Rename_File = msg_box_check_button_get_active(MSG_BOX(msgbox));
+ gtk_widget_destroy(msgbox);
+ }else
+ {
+ if (SF_HideMsgbox_Rename_File)
+ button = SF_ButtonPressed_Rename_File;
+ else
+ button = BUTTON_YES;
+ }
+
+ switch(button)
+ {
+ case BUTTON_YES:
+ Rename_File(ETFile);
+ break;
+ case BUTTON_NO:
+ break;
+ case BUTTON_CANCEL:
+ case -1:
+ stop_loop = -1;
+ return stop_loop;
+ break;
+ }
+ }
+
+ g_free(basename_cur_utf8);
+ g_free(basename_new_utf8);
+
+ /* Refresh file into browser list */
+ Browser_List_Refresh_File_In_List(ETFile);
+
+ return 1;
+}
+
+
+/*
+ * Write tag of the ETFile
+ */
+void Write_File_Tag (ET_File *ETFile)
+{
+ gchar *cur_filename_utf8 = ((File_Name *)ETFile->FileNameCur->data)->value_utf8;
+ gchar *msg;
+ gchar *basename_utf8;
+ GtkWidget *msgbox;
+
+ basename_utf8 = g_path_get_basename(cur_filename_utf8);
+ msg = g_strdup_printf(_("Writing tag of '%s'"),basename_utf8);
+ Statusbar_Message(msg,TRUE);
+ g_free(msg);
+
+ if (ET_Save_File_Tag_To_HD(ETFile))
+ {
+ Statusbar_Message(_("Tag(s) written"),TRUE);
+ return;
+ }
+
+ switch ( ((ET_File_Description *)ETFile->ETFileDescription)->TagType)
+ {
+#ifdef ENABLE_OGG
+ case OGG_TAG:
+ // Special for Ogg Vorbis because the error is defined into 'vcedit_error(state)'
+ msg = g_strdup_printf(_("Can't write tag in file '%s'!\n(%s)"),
+ basename_utf8,ogg_error_msg);
+ break;
+#endif
+ default:
+ msg = g_strdup_printf(_("Can't write tag in file '%s'!\n(%s)"),
+ basename_utf8,g_strerror(errno));
+ }
+
+ msgbox = msg_box_new(_("Error..."),msg,GTK_STOCK_DIALOG_ERROR,BUTTON_OK,0);
+ g_free(msg);
+ g_free(basename_utf8);
+ msg_box_hide_check_button(MSG_BOX(msgbox));
+ msg_box_run(MSG_BOX(msgbox));
+ gtk_widget_destroy(msgbox);
+}
+
+
+/*
+ * Make dir and all parents with permission mode
+ */
+gint Make_Dir (const gchar *dirname_old, const gchar *dirname_new)
+{
+ gchar *parent, *temp;
+ struct stat dirstat;
+#ifdef WIN32
+ gboolean first = TRUE;
+#endif
+
+ // Use same permissions as the old directory
+ stat(dirname_old,&dirstat);
+
+ temp = parent = g_strdup(dirname_new);
+ for (temp++;*temp;temp++)
+ {
+ if (*temp!=G_DIR_SEPARATOR)
+ continue;
+
+#ifdef WIN32
+ if (first)
+ {
+ first = FALSE;
+ continue;
+ }
+#endif
+
+ *temp=0; // To truncate temporarly dirname_new
+
+ if (mkdir(parent,dirstat.st_mode)==-1 && errno!=EEXIST)
+ {
+ g_free(parent);
+ return(-1);
+ }
+ *temp=G_DIR_SEPARATOR; // To cancel the '*temp=0;'
+ }
+ g_free(parent);
+
+ if (mkdir(dirname_new,dirstat.st_mode)==-1 && errno!=EEXIST)
+ return(-1);
+
+ return(0);
+}
+
+/*
+ * Remove old directories after renaming the file
+ * Badly coded, but works....
+ */
+gint Remove_Dir (const gchar *dirname_old, const gchar *dirname_new)
+{
+ gchar *temp_old, *temp_new;
+ gchar *temp_end_old, *temp_end_new;
+
+ temp_old = g_strdup(dirname_old);
+ temp_new = g_strdup(dirname_new);
+
+ while (temp_old && temp_new && strcmp(temp_old,temp_new)!=0 )
+ {
+ if (rmdir(temp_old)==-1)
+ {
+ if (errno!=ENOTEMPTY)
+ {
+ g_free(temp_old);
+ g_free(temp_new);
+ return(-1);
+ }else
+ {
+ break;
+ }
+ }
+
+ temp_end_old = temp_old + strlen(temp_old) - 1;
+ temp_end_new = temp_new + strlen(temp_new) - 1;
+
+ while (*temp_end_old && *temp_end_old!=G_DIR_SEPARATOR)
+ {
+ temp_end_old--;
+ }
+ *temp_end_old=0;
+ while (*temp_end_new && *temp_end_new!=G_DIR_SEPARATOR)
+ {
+ temp_end_new--;
+ }
+ *temp_end_new=0;
+ }
+ g_free(temp_old);
+ g_free(temp_new);
+
+ return(0);
+}
+
+
+/*
+ * Rename the file ETFile
+ */
+void Rename_File (ET_File *ETFile)
+{
+ FILE *file;
+ gchar *tmp_filename = NULL;
+ gchar *cur_filename = ((File_Name *)ETFile->FileNameCur->data)->value;
+ gchar *new_filename = ((File_Name *)ETFile->FileNameNew->data)->value;
+ gchar *cur_filename_utf8 = ((File_Name *)ETFile->FileNameCur->data)->value_utf8; // Filename + path
+ gchar *new_filename_utf8 = ((File_Name *)ETFile->FileNameNew->data)->value_utf8;
+ gchar *cur_basename_utf8 = g_path_get_basename(cur_filename_utf8); // Only filename
+ gchar *new_basename_utf8 = g_path_get_basename(new_filename_utf8);
+ gint fd_tmp;
+ gchar *msg;
+ gchar *dirname_cur;
+ gchar *dirname_new;
+ gchar *dirname_cur_utf8;
+ gchar *dirname_new_utf8;
+
+ msg = g_strdup_printf(_("Renaming file '%s'"),cur_filename_utf8);
+ Statusbar_Message(msg,TRUE);
+ g_free(msg);
+
+ /* We use two stages to rename file, to avoid problem with some system
+ * that doesn't allow to rename the file if only the case has changed. */
+ tmp_filename = g_strdup_printf("%s.XXXXXX",cur_filename);
+ if ( (fd_tmp = mkstemp(tmp_filename)) >= 0 )
+ {
+ close(fd_tmp);
+ unlink(tmp_filename);
+ }
+
+ // Rename to the temporary name
+ if ( rename(cur_filename,tmp_filename)!=0 ) // => rename() fails
+ {
+ gchar *msg;
+ GtkWidget *msgbox;
+
+ /* Renaming file to the temporary filename has failed */
+ msg = g_strdup_printf(_("Can't rename file '%s'\n to \n'%s'!\n(%s)"),
+ cur_basename_utf8,new_basename_utf8,g_strerror(errno));
+ msgbox = msg_box_new(_("Error..."),msg,GTK_STOCK_DIALOG_ERROR,BUTTON_OK,0);
+ g_free(msg);
+ msg_box_hide_check_button(MSG_BOX(msgbox));
+ msg_box_run(MSG_BOX(msgbox));
+ gtk_widget_destroy(msgbox);
+
+ Statusbar_Message(_("File(s) not renamed..."),TRUE);
+ g_free(tmp_filename);
+ g_free(cur_basename_utf8);
+ g_free(new_basename_utf8);
+ return;
+ }
+
+ /* Check if the new file name already exists. Must be done after changing
+ * filename to the temporary name, else we can't detect the problem under
+ * Linux when renaming a file 'aa.mp3' to 'AA.mp3' and if the last one
+ * already exists */
+ if ( (file=fopen(new_filename,"r"))!=NULL )
+ {
+ GtkWidget *msgbox;
+
+ fclose(file);
+
+ // Restore the initial name
+ if ( rename(tmp_filename,cur_filename)!=0 ) // => rename() fails
+ {
+ gchar *msg;
+ GtkWidget *msgbox;
+
+ /* Renaming file from the temporary filename has failed */
+ msg = g_strdup_printf(_("Can't rename file '%s'\n to \n'%s'!\n(%s)"),
+ new_basename_utf8,cur_basename_utf8,g_strerror(errno));
+ msgbox = msg_box_new(_("Error..."),msg,GTK_STOCK_DIALOG_ERROR,BUTTON_OK,0);
+ g_free(msg);
+ msg_box_hide_check_button(MSG_BOX(msgbox));
+ msg_box_run(MSG_BOX(msgbox));
+ gtk_widget_destroy(msgbox);
+
+ Statusbar_Message(_("File(s) not renamed..."),TRUE);
+ g_free(tmp_filename);
+ g_free(cur_basename_utf8);
+ g_free(new_basename_utf8);
+ return;
+ }
+
+ msg = g_strdup_printf(_("Can't rename file \n'%s'\nbecause the following "
+ "file already exists:\n'%s'"),cur_basename_utf8,new_basename_utf8);
+ msgbox = msg_box_new(_("Error..."),msg,GTK_STOCK_DIALOG_ERROR,BUTTON_OK,0);
+ g_free(msg);
+ msg_box_hide_check_button(MSG_BOX(msgbox));
+ msg_box_run(MSG_BOX(msgbox));
+ gtk_widget_destroy(msgbox);
+
+ Statusbar_Message(_("File(s) not renamed..."),TRUE);
+
+ g_free(new_basename_utf8);
+ g_free(cur_basename_utf8);
+ return;
+
+ }
+
+ /* If files are in different directories, we need to create the new
+ * destination directory */
+ dirname_cur = g_path_get_dirname(tmp_filename);
+ dirname_new = g_path_get_dirname(new_filename);
+
+ if (dirname_cur && dirname_new && strcmp(dirname_cur,dirname_new)) /* Need to create target directory? */
+ {
+ if (Make_Dir(dirname_cur,dirname_new))
+ {
+ gchar *msg;
+ GtkWidget *msgbox;
+
+ /* Renaming file has failed, but we try to set the initial name */
+ rename(tmp_filename,cur_filename);
+
+ dirname_new_utf8 = filename_to_display(dirname_new);
+ msg = g_strdup_printf(_("Can't create target directory\n'%s'!\n(%s)"),
+ dirname_new_utf8,g_strerror(errno));
+ msgbox = msg_box_new(_("Error..."),msg,GTK_STOCK_DIALOG_ERROR,BUTTON_OK,0);
+ g_free(msg);
+ g_free(dirname_new_utf8);
+ msg_box_hide_check_button(MSG_BOX(msgbox));
+ msg_box_run(MSG_BOX(msgbox));
+ gtk_widget_destroy(msgbox);
+
+ g_free(tmp_filename);
+ g_free(cur_basename_utf8);
+ g_free(new_basename_utf8);
+ g_free(dirname_cur);
+ g_free(dirname_new);
+ return;
+ }
+ }
+
+ /* Now, we rename the file to his final name */
+ if ( rename(tmp_filename,new_filename)==0 )
+ {
+ /* Renaming file has succeeded */
+ Log_Print(_("Renamed file '%s' to '%s'"),cur_basename_utf8,new_basename_utf8);
+
+ ETFile->FileNameCur = ETFile->FileNameNew;
+ /* Now the file was renamed, so mark his state */
+ ET_Mark_File_Name_As_Saved(ETFile);
+
+ Statusbar_Message(_("File(s) renamed..."),TRUE);
+
+ /* Remove the of directory (check automatically if it is empty) */
+ if (Remove_Dir(dirname_cur,dirname_new))
+ {
+ gchar *msg;
+ GtkWidget *msgbox;
+
+ /* Removing directories failed */
+ dirname_cur_utf8 = filename_to_display(dirname_cur);
+ msg = g_strdup_printf(_("Can't remove old directory\n'%s'!\n(%s)"),
+ dirname_cur_utf8,g_strerror(errno));
+ msgbox = msg_box_new(_("Error..."),msg,GTK_STOCK_DIALOG_ERROR,BUTTON_OK,0);
+ g_free(msg);
+ g_free(dirname_cur_utf8);
+ msg_box_hide_check_button(MSG_BOX(msgbox));
+ msg_box_run(MSG_BOX(msgbox));
+ gtk_widget_destroy(msgbox);
+
+ g_free(tmp_filename);
+ g_free(cur_basename_utf8);
+ g_free(new_basename_utf8);
+ g_free(dirname_cur);
+ g_free(dirname_new);
+ return;
+ }
+ }else if ( errno==EXDEV )
+ {
+ /* For the error "Invalid cross-device link" during renaming, when the
+ * new filename isn't on the same device of the omd filename. In this
+ * case, we need to move the file, and not only to update the hard disk
+ * index table. For example : 'renaming' /mnt/D/file.mp3 to /mnt/E/file.mp3
+ *
+ * So, we need to copy the old file to the new location, and then to
+ * deleted the old file */
+ if ( Copy_File(tmp_filename,new_filename) )
+ {
+ /* Delete the old file */
+ unlink(tmp_filename);
+
+ /* Renaming file has succeeded */
+ Log_Print(_("Moved file '%s' to '%s'"),cur_basename_utf8,new_basename_utf8);
+
+ ETFile->FileNameCur = ETFile->FileNameNew;
+ /* Now the file was renamed, so mark his state */
+ ET_Mark_File_Name_As_Saved(ETFile);
+
+ Statusbar_Message(_("File(s) moved..."),TRUE);
+
+ /* Remove the of directory (check automatically if it is empty) */
+ if (Remove_Dir(dirname_cur,dirname_new))
+ {
+ gchar *msg;
+ GtkWidget *msgbox;
+
+ /* Removing directories failed */
+ dirname_cur_utf8 = filename_to_display(dirname_cur);
+ msg = g_strdup_printf(_("Can't remove old directory\n'%s'!\n(%s)"),
+ dirname_cur_utf8,g_strerror(errno));
+ msgbox = msg_box_new(_("Error..."),msg,GTK_STOCK_DIALOG_ERROR,BUTTON_OK,0);
+ g_free(msg);
+ g_free(dirname_cur_utf8);
+ msg_box_hide_check_button(MSG_BOX(msgbox));
+ msg_box_run(MSG_BOX(msgbox));
+ gtk_widget_destroy(msgbox);
+
+ g_free(tmp_filename);
+ g_free(cur_basename_utf8);
+ g_free(new_basename_utf8);
+ g_free(dirname_cur);
+ g_free(dirname_new);
+ return;
+ }
+ }else
+ {
+ gchar *msg;
+ GtkWidget *msgbox;
+
+ /* Moving file has failed */
+ msg = g_strdup_printf(_("Can't move file '%s'\n to \n'%s'!\n(%s)"),
+ cur_basename_utf8,new_basename_utf8,g_strerror(errno));
+ msgbox = msg_box_new(_("Error..."),msg,GTK_STOCK_DIALOG_ERROR,BUTTON_OK,0);
+ g_free(msg);
+ msg_box_hide_check_button(MSG_BOX(msgbox));
+ msg_box_run(MSG_BOX(msgbox));
+ gtk_widget_destroy(msgbox);
+
+ Statusbar_Message(_("File(s) not moved..."),TRUE);
+ }
+ }else
+ {
+ gchar *msg;
+ GtkWidget *msgbox;
+
+ /* Renaming file has failed, but we try to set the initial name */
+ rename(tmp_filename,cur_filename);
+
+ msg = g_strdup_printf(_("Can't rename file '%s'\n to \n'%s'!\n(%s)"),
+ cur_basename_utf8,new_basename_utf8,g_strerror(errno));
+ msgbox = msg_box_new(_("Error..."),msg,GTK_STOCK_DIALOG_ERROR,BUTTON_OK,0);
+ g_free(msg);
+ msg_box_hide_check_button(MSG_BOX(msgbox));
+ msg_box_run(MSG_BOX(msgbox));
+ gtk_widget_destroy(msgbox);
+
+ Statusbar_Message(_("File(s) not renamed..."),TRUE);
+ }
+ g_free(tmp_filename);
+ g_free(cur_basename_utf8);
+ g_free(new_basename_utf8);
+ g_free(dirname_cur);
+ g_free(dirname_new);
+}
+
+/*
+ * Delete the file ETFile
+ */
+gint Delete_File (ET_File *ETFile, gboolean multiple_files)
+{
+ GtkWidget *msgbox = NULL;
+ gchar *cur_filename;
+ gchar *cur_filename_utf8;
+ gchar *basename_utf8;
+ gchar *msg;
+ gint button;
+ gint stop_loop;
+
+ if (!ETFile) return FALSE;
+
+ // Filename of the file to delete
+ cur_filename = ((File_Name *)(ETFile->FileNameCur)->data)->value;
+ cur_filename_utf8 = ((File_Name *)(ETFile->FileNameCur)->data)->value_utf8;
+ basename_utf8 = g_path_get_basename(cur_filename);
+
+ /*
+ * Remove the file
+ */
+ if (CONFIRM_DELETE_FILE && !SF_HideMsgbox_Delete_File)
+ {
+ msg = g_strdup_printf(_("Do you really want to delete definitively the file\n'%s' ?"),basename_utf8);
+ if (multiple_files)
+ {
+ msgbox = msg_box_new(_("Delete File..."),msg,GTK_STOCK_DIALOG_QUESTION,
+ BUTTON_CANCEL,BUTTON_NO,BUTTON_YES,0);
+ }else
+ {
+ msgbox = msg_box_new(_("Delete File..."),msg,GTK_STOCK_DIALOG_QUESTION,
+ BUTTON_NO,BUTTON_YES,0);
+ msg_box_hide_check_button(MSG_BOX(msgbox));
+ }
+ g_free(msg);
+ SF_ButtonPressed_Delete_File = button = msg_box_run(MSG_BOX(msgbox));
+ if (msg_box_check_button_get_active(MSG_BOX(msgbox)))
+ SF_HideMsgbox_Delete_File = MSG_BOX(msgbox)->check_button_state;
+ gtk_widget_destroy(msgbox);
+ }else
+ {
+ if (SF_HideMsgbox_Delete_File)
+ button = SF_ButtonPressed_Delete_File;
+ else
+ button = BUTTON_YES;
+ }
+
+ switch(button)
+ {
+ case BUTTON_YES:
+ if (remove(cur_filename)==0)
+ {
+ msg = g_strdup_printf(_("File '%s' deleted"), basename_utf8);
+ Statusbar_Message(msg,FALSE);
+ g_free(msg);
+ g_free(basename_utf8);
+ return 1;
+ }
+ break;
+ case BUTTON_NO:
+ break;
+ case BUTTON_CANCEL:
+ case -1:
+ stop_loop = -1;
+ g_free(basename_utf8);
+ return stop_loop;
+ break;
+ }
+
+ g_free(basename_utf8);
+ return 0;
+}
+
+/*
+ * Copy a file to a new location
+ */
+gint Copy_File (gchar const *fileold, gchar const *filenew)
+{
+ FILE* fOld;
+ FILE* fNew;
+ gchar buffer[512];
+ gint NbRead;
+ struct stat statbuf;
+ struct utimbuf utimbufbuf;
+
+ if ( (fOld=fopen(fileold, "rb")) == NULL )
+ {
+ return FALSE;
+ }
+
+ if ( (fNew=fopen(filenew, "wb")) == NULL )
+ {
+ fclose(fOld);
+ return FALSE;
+ }
+
+ while ( (NbRead=fread(buffer, 1, 512, fOld)) != 0 )
+ {
+ fwrite(buffer, 1, NbRead, fNew);
+ }
+
+ fclose(fNew);
+ fclose(fOld);
+
+ // Copy properties of the old file to the new one.
+ stat(fileold,&statbuf);
+ chmod(filenew,statbuf.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO));
+ chown(filenew,statbuf.st_uid,statbuf.st_gid);
+ utimbufbuf.actime = statbuf.st_atime; // Last access time
+ utimbufbuf.modtime = statbuf.st_mtime; // Last modification time
+ utime(filenew,&utimbufbuf);
+
+ return TRUE;
+}
+
+void Action_Select_Browser_Style (void)
+{
+ if (!ETCore->ETFileDisplayedList) return;
+
+ /* Save the current displayed data */
+ ET_Save_File_Data_From_UI(ETCore->ETFileDisplayed);
+
+ Browser_Display_Tree_Or_Artist_Album_List();
+
+ Update_Command_Buttons_Sensivity();
+}
+
+
+
+
+
+/*
+ * Scans the specified directory: and load files into a list.
+ * If the path doesn't exist, we free the previous loaded list of files.
+ */
+#include <sys/types.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <string.h>
+void Read_Directory (gchar *path_real)
+{
+ DIR *dir;
+ gchar *msg;
+ gchar *tmp;
+ gchar progress_bar_text[30];
+ guint nbrfile = 0;
+ double fraction;
+ GList *FileList = NULL;
+ gint progress_bar_index = 0;
+ GtkAction *uiaction;
+ GtkWidget *TBViewMode;
+
+ if (!path_real) return;
+
+ ReadingDirectory = TRUE; /* A flag to avoid to start an other reading */
+
+ /* Initialize file list */
+ ET_Core_Free();
+ ET_Core_Initialize();
+ Update_Command_Buttons_Sensivity();
+
+ /* Initialize browser list */
+ Browser_List_Clear();
+
+ /* Clear entry boxes */
+ Clear_File_Entry_Field();
+ Clear_Header_Fields();
+ Clear_Tag_Entry_Fields();
+ gtk_label_set_text(GTK_LABEL(FileIndex),"0/0:");
+
+ TBViewMode = gtk_ui_manager_get_widget(UIManager, "/ToolBar/ViewModeToggle");
+ gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(TBViewMode),FALSE);
+ //Browser_Display_Tree_Or_Artist_Album_List(); // To show the corresponding lists...
+
+ // Set to unsensitive the Browser Area, to avoid to select an other file while loading the first one
+ Browser_Area_Set_Sensitive(FALSE);
+
+ /* Placed only here, to empty the previous list of files */
+ if ((dir=opendir(path_real)) == NULL)
+ {
+ GtkWidget *msgbox;
+ gchar *path_utf8 = filename_to_display(path_real);
+ gchar *msg;
+
+ msg = g_strdup_printf(_("Can't read directory :\n'%s'\n(%s)"),path_utf8,g_strerror(errno));
+ msgbox = msg_box_new(_("Error..."),msg,GTK_STOCK_DIALOG_ERROR,BUTTON_OK,0);
+ g_free(msg);
+ g_free(path_utf8);
+
+ msg_box_hide_check_button(MSG_BOX(msgbox));
+ msg_box_run(MSG_BOX(msgbox));
+ gtk_widget_destroy(msgbox);
+ ReadingDirectory = FALSE; //Allow a new reading
+ return;
+ }
+ closedir(dir);
+
+ /* Open the window to quit recursion (since 27/04/2007 : not only into recursion mode) */
+ Set_Busy_Cursor();
+ uiaction = gtk_ui_manager_get_action(UIManager, "/ToolBar/Stop");
+ g_object_set(uiaction, "sensitive", BROWSE_SUBDIR, NULL);
+ //if (BROWSE_SUBDIR)
+ Open_Quit_Recursion_Function_Window();
+
+ /* Read the directory recursively */
+ msg = g_strdup_printf(_("Search in progress..."));
+ Statusbar_Message(msg,FALSE);
+ g_free(msg);
+ // Search the supported files
+ FileList = Read_Directory_Recursively(FileList,path_real,BROWSE_SUBDIR);
+ nbrfile = g_list_length(FileList);
+
+ gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ProgressBar), 0.0);
+ g_snprintf(progress_bar_text, 30, "%d/%d", 0, nbrfile);
+ gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ProgressBar), progress_bar_text);
+
+ // Load the supported files (Extension recognized)
+ while (FileList)
+ {
+ gchar *filename_real = FileList->data; // Contains real filenames
+ gchar *filename_utf8 = filename_to_display(filename_real);
+
+ msg = g_strdup_printf(_("File: '%s'"),filename_utf8);
+ Statusbar_Message(msg,FALSE);
+ g_free(msg);
+ g_free(filename_utf8);
+
+ // Warning: Do not free filename_real because ET_Add_File.. uses it for internal structures
+ ET_Add_File_To_File_List(filename_real);
+
+ // Update the progress bar
+ fraction = (++progress_bar_index) / (double) nbrfile;
+ gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ProgressBar), fraction);
+ g_snprintf(progress_bar_text, 30, "%d/%d", progress_bar_index, nbrfile);
+ gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ProgressBar), progress_bar_text);
+ while (gtk_events_pending())
+ gtk_main_iteration();
+
+ if ( !FileList->next || (Main_Stop_Button_Pressed==1) ) break;
+ FileList = FileList->next;
+ }
+ if (FileList) g_list_free(FileList);
+ gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ProgressBar), "");
+
+ /* Close window to quit recursion */
+ Destroy_Quit_Recursion_Function_Window();
+ Main_Stop_Button_Pressed = 0;
+ uiaction = gtk_ui_manager_get_action(UIManager, "/ToolBar/Stop");
+ g_object_set(uiaction, "sensitive", FALSE, NULL);
+
+ //ET_Debug_Print_File_List(ETCore->ETFileList,__FILE__,__LINE__,__FUNCTION__);
+
+ if (ETCore->ETFileList)
+ {
+ //GList *etfilelist;
+ /* Load the list of file into the browser list widget */
+ Browser_Display_Tree_Or_Artist_Album_List();
+
+ /* Load the list attached to the TrackEntry */
+ Load_Track_List_To_UI();
+
+ /* Display the first file */
+ //No need to select first item, because Browser_Display_Tree_Or_Artist_Album_List() does this
+ //etfilelist = ET_Displayed_File_List_First();
+ //if (etfilelist)
+ //{
+ // ET_Display_File_Data_To_UI((ET_File *)etfilelist->data);
+ // Browser_List_Select_File_By_Etfile((ET_File *)etfilelist->data,FALSE);
+ //}
+
+ /* Prepare message for the status bar */
+ if (BROWSE_SUBDIR)
+ msg = g_strdup_printf(_("Found %d file(s) in this directory and subdirectories."),ETCore->ETFileDisplayedList_Length);
+ else
+ msg = g_strdup_printf(_("Found %d file(s) in this directory."),ETCore->ETFileDisplayedList_Length);
+
+ }else
+ {
+ /* Clear entry boxes */
+ Clear_File_Entry_Field();
+ Clear_Header_Fields();
+ Clear_Tag_Entry_Fields();
+
+ if (FileIndex)
+ gtk_label_set_text(GTK_LABEL(FileIndex),"0/0:");
+
+
+ tmp = g_strdup_printf(_("%u file(s)"),0); // See in ET_Display_Filename_To_UI
+ Browser_Label_Set_Text(tmp);
+ g_free(tmp);
+
+ /* Prepare message for the status bar */
+ if (BROWSE_SUBDIR)
+ msg = g_strdup(_("No file found in this directory and subdirectories!"));
+ else
+ msg = g_strdup(_("No file found in this directory!"));
+ }
+
+ /* Update sensitivity of buttons and menus */
+ Update_Command_Buttons_Sensivity();
+
+ Browser_Area_Set_Sensitive(TRUE);
+
+ gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ProgressBar), 0.0);
+ Statusbar_Message(msg,FALSE);
+ g_free(msg);
+ Set_Unbusy_Cursor();
+ ReadingDirectory = FALSE;
+}
+
+
+
+/*
+ * Recurse the path to create a list of files. Return a GList of the files found.
+ */
+GList *Read_Directory_Recursively (GList *file_list, gchar *path_real, gint recurse)
+{
+ DIR *dir;
+ struct dirent *dirent;
+ struct stat statbuf;
+ gchar *filename;
+
+ if ((dir = opendir(path_real)) == NULL)
+ return file_list;
+
+ while ((dirent = readdir(dir)) != NULL)
+ {
+ if (Main_Stop_Button_Pressed == 1)
+ {
+ closedir(dir);
+ return file_list;
+ }
+
+ // We don't read the directories '.' and '..', but may read hidden directories like '.mydir'
+ if ( (g_ascii_strcasecmp (dirent->d_name,"..") != 0)
+ && ((g_ascii_strncasecmp(dirent->d_name,".", 1) != 0) || (BROWSE_HIDDEN_DIR && strlen(dirent->d_name) > 1)) )
+ {
+ if (path_real[strlen(path_real)-1]!=G_DIR_SEPARATOR)
+ filename = g_strconcat(path_real,G_DIR_SEPARATOR_S,dirent->d_name,NULL);
+ else
+ filename = g_strconcat(path_real,dirent->d_name,NULL);
+
+ if (lstat(filename, &statbuf) == -1)
+ {
+ g_free(filename);
+ continue;
+ }
+
+ if (S_ISDIR(statbuf.st_mode))
+ {
+ if (recurse)
+ file_list = Read_Directory_Recursively(file_list,filename,recurse);
+ //}else if ( (S_ISREG(statbuf.st_mode) || S_ISLNK(statbuf.st_mode)) && ET_File_Is_Supported(filename))
+ }else if ( S_ISREG(statbuf.st_mode) && ET_File_Is_Supported(filename))
+ {
+ file_list = g_list_append(file_list,g_strdup(filename));
+ }
+ g_free(filename);
+
+ // Just to not block X events
+ while (gtk_events_pending())
+ gtk_main_iteration();
+ }
+ }
+
+ closedir(dir);
+ return file_list;
+}
+
+
+/*
+ * Window with the 'STOP' button to stop recursion when reading directories
+ */
+void Open_Quit_Recursion_Function_Window (void)
+{
+ GtkWidget *button;
+ GtkWidget *frame;
+
+ if (QuitRecursionWindow != NULL)
+ return;
+ QuitRecursionWindow = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title(GTK_WINDOW(QuitRecursionWindow),_("Searching..."));
+ gtk_window_set_transient_for(GTK_WINDOW(QuitRecursionWindow),GTK_WINDOW(MainWindow));
+ //gtk_window_set_policy(GTK_WINDOW(QuitRecursionWindow),FALSE,FALSE,TRUE);
+
+ // Just center on mainwindow
+ gtk_window_set_position(GTK_WINDOW(QuitRecursionWindow), GTK_WIN_POS_CENTER_ON_PARENT);
+
+ g_signal_connect(G_OBJECT(QuitRecursionWindow),"destroy",
+ G_CALLBACK(Quit_Recursion_Function_Button_Pressed),NULL);
+ g_signal_connect(G_OBJECT(QuitRecursionWindow),"delete_event",
+ G_CALLBACK(Quit_Recursion_Function_Button_Pressed),NULL);
+ g_signal_connect(G_OBJECT(QuitRecursionWindow),"key_press_event",
+ G_CALLBACK(Quit_Recursion_Window_Key_Press),NULL);
+
+ frame = gtk_frame_new(NULL);
+ gtk_frame_set_shadow_type(GTK_FRAME(frame),GTK_SHADOW_IN);
+ gtk_container_add(GTK_CONTAINER(QuitRecursionWindow),frame);
+ gtk_container_set_border_width(GTK_CONTAINER(frame),2);
+
+ // Button to stop...
+ button = Create_Button_With_Icon_And_Label(GTK_STOCK_STOP,_(" STOP the search... "));
+ gtk_container_set_border_width(GTK_CONTAINER(button),8);
+ gtk_container_add(GTK_CONTAINER(frame),button);
+ g_signal_connect(G_OBJECT(button),"clicked",G_CALLBACK(Quit_Recursion_Function_Button_Pressed),NULL);
+
+ gtk_widget_show_all(QuitRecursionWindow);
+}
+void Destroy_Quit_Recursion_Function_Window (void)
+{
+ if (QuitRecursionWindow)
+ {
+ gtk_widget_destroy(QuitRecursionWindow);
+ QuitRecursionWindow = (GtkWidget *)NULL;
+ /*Statusbar_Message(_("Recursive file search interrupted."),FALSE);*/
+ }
+}
+void Quit_Recursion_Function_Button_Pressed (void)
+{
+ Action_Main_Stop_Button_Pressed();
+ Destroy_Quit_Recursion_Function_Window();
+}
+void Quit_Recursion_Window_Key_Press (GtkWidget *window, GdkEvent *event)
+{
+ GdkEventKey *kevent;
+
+ if (event && event->type == GDK_KEY_PRESS)
+ {
+ kevent = (GdkEventKey *)event;
+ switch(kevent->keyval)
+ {
+ case GDK_Escape:
+ Destroy_Quit_Recursion_Function_Window();
+ break;
+ }
+ }
+}
+
+
+/*
+ * To stop the recursive search within directories or saving files
+ */
+void Action_Main_Stop_Button_Pressed (void)
+{
+ GtkAction *uiaction;
+ Main_Stop_Button_Pressed = 1;
+ uiaction = gtk_ui_manager_get_action(UIManager, "/ToolBar/Stop");
+ g_object_set(uiaction, "sensitive", FALSE, NULL);
+}
+
+void ui_widget_set_sensitive (const gchar *menu, const gchar *action, gboolean sensitive)
+{
+ GtkAction *uiaction;
+ gchar *path;
+
+ path = g_strconcat("/MenuBar/", menu,"/", action, NULL);
+
+ uiaction = gtk_ui_manager_get_action(UIManager, path);
+ if (uiaction)
+ g_object_set(uiaction, "sensitive", sensitive, NULL);
+ g_free(path);
+}
+
+/*
+ * Update_Command_Buttons_Sensivity: Set to sensitive/unsensitive the state of each button into
+ * the commands area and menu items in function of state of the "main list".
+ */
+void Update_Command_Buttons_Sensivity (void)
+{
+ GtkAction *uiaction;
+
+ if (!ETCore->ETFileDisplayedList)
+ {
+ /* No file found */
+
+ /* File and Tag frames */
+ File_Area_Set_Sensitive(FALSE);
+ Tag_Area_Set_Sensitive(FALSE);
+
+ /* Tool bar buttons (the others are covered by the menu) */
+ uiaction = gtk_ui_manager_get_action(UIManager, "/ToolBar/Stop");
+ g_object_set(uiaction, "sensitive", FALSE, NULL);
+
+ /* Scanner Window */
+ if (SWScanButton)
+ gtk_widget_set_sensitive(GTK_WIDGET(SWScanButton),FALSE);
+
+ /* Menu commands */
+ ui_widget_set_sensitive(MENU_FILE, AM_OPEN_FILE_WITH, FALSE);
+ ui_widget_set_sensitive(MENU_FILE, AM_SELECT_ALL_FILES, FALSE);
+ ui_widget_set_sensitive(MENU_FILE, AM_UNSELECT_ALL_FILES, FALSE);
+ ui_widget_set_sensitive(MENU_FILE, AM_INVERT_SELECTION, FALSE);
+ ui_widget_set_sensitive(MENU_FILE, AM_DELETE_FILE, FALSE);
+ ui_widget_set_sensitive(MENU_SORT_PROP_PATH, AM_SORT_ASCENDING_FILENAME, FALSE);
+ ui_widget_set_sensitive(MENU_SORT_PROP_PATH, AM_SORT_DESCENDING_FILENAME,FALSE);
+ ui_widget_set_sensitive(MENU_SORT_PROP_PATH, AM_SORT_ASCENDING_CREATION_DATE,FALSE);
+ ui_widget_set_sensitive(MENU_SORT_PROP_PATH, AM_SORT_DESCENDING_CREATION_DATE,FALSE);
+ ui_widget_set_sensitive(MENU_SORT_TAG_PATH, AM_SORT_ASCENDING_TRACK_NUMBER,FALSE);
+ ui_widget_set_sensitive(MENU_SORT_TAG_PATH, AM_SORT_DESCENDING_TRACK_NUMBER,FALSE);
+ ui_widget_set_sensitive(MENU_SORT_TAG_PATH, AM_SORT_ASCENDING_TITLE,FALSE);
+ ui_widget_set_sensitive(MENU_SORT_TAG_PATH, AM_SORT_DESCENDING_TITLE,FALSE);
+ ui_widget_set_sensitive(MENU_SORT_TAG_PATH, AM_SORT_ASCENDING_ARTIST,FALSE);
+ ui_widget_set_sensitive(MENU_SORT_TAG_PATH, AM_SORT_DESCENDING_ARTIST,FALSE);
+ ui_widget_set_sensitive(MENU_SORT_TAG_PATH, AM_SORT_ASCENDING_ALBUM,FALSE);
+ ui_widget_set_sensitive(MENU_SORT_TAG_PATH, AM_SORT_DESCENDING_ALBUM,FALSE);
+ ui_widget_set_sensitive(MENU_SORT_TAG_PATH, AM_SORT_ASCENDING_YEAR,FALSE);
+ ui_widget_set_sensitive(MENU_SORT_TAG_PATH, AM_SORT_DESCENDING_YEAR,FALSE);
+ ui_widget_set_sensitive(MENU_SORT_TAG_PATH, AM_SORT_ASCENDING_GENRE,FALSE);
+ ui_widget_set_sensitive(MENU_SORT_TAG_PATH, AM_SORT_DESCENDING_GENRE,FALSE);
+ ui_widget_set_sensitive(MENU_SORT_TAG_PATH, AM_SORT_ASCENDING_COMMENT,FALSE);
+ ui_widget_set_sensitive(MENU_SORT_TAG_PATH, AM_SORT_DESCENDING_COMMENT,FALSE);
+ ui_widget_set_sensitive(MENU_SORT_PROP_PATH, AM_SORT_ASCENDING_FILE_TYPE,FALSE);
+ ui_widget_set_sensitive(MENU_SORT_PROP_PATH, AM_SORT_DESCENDING_FILE_TYPE,FALSE);
+ ui_widget_set_sensitive(MENU_SORT_PROP_PATH, AM_SORT_ASCENDING_FILE_SIZE,FALSE);
+ ui_widget_set_sensitive(MENU_SORT_PROP_PATH, AM_SORT_DESCENDING_FILE_SIZE,FALSE);
+ ui_widget_set_sensitive(MENU_SORT_PROP_PATH, AM_SORT_ASCENDING_FILE_DURATION,FALSE);
+ ui_widget_set_sensitive(MENU_SORT_PROP_PATH, AM_SORT_DESCENDING_FILE_DURATION,FALSE);
+ ui_widget_set_sensitive(MENU_SORT_PROP_PATH, AM_SORT_ASCENDING_FILE_BITRATE,FALSE);
+ ui_widget_set_sensitive(MENU_SORT_PROP_PATH, AM_SORT_DESCENDING_FILE_BITRATE,FALSE);
+ ui_widget_set_sensitive(MENU_SORT_PROP_PATH, AM_SORT_ASCENDING_FILE_SAMPLERATE,FALSE);
+ ui_widget_set_sensitive(MENU_SORT_PROP_PATH, AM_SORT_DESCENDING_FILE_SAMPLERATE,FALSE);
+ ui_widget_set_sensitive(MENU_FILE, AM_PREV, FALSE);
+ ui_widget_set_sensitive(MENU_FILE, AM_NEXT, FALSE);
+ ui_widget_set_sensitive(MENU_FILE, AM_FIRST, FALSE);
+ ui_widget_set_sensitive(MENU_FILE, AM_LAST, FALSE);
+ ui_widget_set_sensitive(MENU_FILE, AM_SCAN, FALSE);
+ ui_widget_set_sensitive(MENU_FILE, AM_REMOVE, FALSE);
+ ui_widget_set_sensitive(MENU_FILE, AM_UNDO, FALSE);
+ ui_widget_set_sensitive(MENU_FILE, AM_REDO, FALSE);
+ ui_widget_set_sensitive(MENU_FILE, AM_SAVE, FALSE);
+ ui_widget_set_sensitive(MENU_FILE, AM_SAVE_FORCED, FALSE);
+ ui_widget_set_sensitive(MENU_FILE, AM_UNDO_HISTORY, FALSE);
+ ui_widget_set_sensitive(MENU_FILE, AM_REDO_HISTORY, FALSE);
+ ui_widget_set_sensitive(MENU_MISC, AM_SEARCH_FILE, FALSE);
+ ui_widget_set_sensitive(MENU_MISC, AM_FILENAME_FROM_TXT, FALSE);
+ ui_widget_set_sensitive(MENU_MISC, AM_WRITE_PLAYLIST, FALSE);
+ ui_widget_set_sensitive(MENU_MISC, AM_RUN_AUDIO_PLAYER, FALSE);
+ ui_widget_set_sensitive(MENU_SCANNER, AM_SCANNER_FILL_TAG, FALSE);
+ ui_widget_set_sensitive(MENU_SCANNER, AM_SCANNER_RENAME_FILE, FALSE);
+ ui_widget_set_sensitive(MENU_SCANNER, AM_SCANNER_PROCESS_FIELDS, FALSE);
+
+ return;
+ }else
+ {
+ GList *selfilelist = NULL;
+ ET_File *etfile;
+ gboolean has_undo = FALSE;
+ gboolean has_redo = FALSE;
+ GtkTreeSelection *selection;
+
+ /* File and Tag frames */
+ File_Area_Set_Sensitive(TRUE);
+ Tag_Area_Set_Sensitive(TRUE);
+
+ /* Tool bar buttons */
+ uiaction = gtk_ui_manager_get_action(UIManager, "/ToolBar/Stop");
+ g_object_set(uiaction, "sensitive", FALSE, NULL);
+
+ /* Scanner Window */
+ if (SWScanButton) gtk_widget_set_sensitive(GTK_WIDGET(SWScanButton),TRUE);
+
+ /* Commands into menu */
+ ui_widget_set_sensitive(MENU_FILE, AM_OPEN_FILE_WITH,TRUE);
+ ui_widget_set_sensitive(MENU_FILE, AM_SELECT_ALL_FILES,TRUE);
+ ui_widget_set_sensitive(MENU_FILE, AM_UNSELECT_ALL_FILES,TRUE);
+ ui_widget_set_sensitive(MENU_FILE, AM_INVERT_SELECTION,TRUE);
+ ui_widget_set_sensitive(MENU_FILE, AM_DELETE_FILE,TRUE);
+ ui_widget_set_sensitive(MENU_SORT_PROP_PATH,AM_SORT_ASCENDING_FILENAME,TRUE);
+ ui_widget_set_sensitive(MENU_SORT_PROP_PATH,AM_SORT_DESCENDING_FILENAME,TRUE);
+ ui_widget_set_sensitive(MENU_SORT_PROP_PATH,AM_SORT_ASCENDING_CREATION_DATE,TRUE);
+ ui_widget_set_sensitive(MENU_SORT_PROP_PATH,AM_SORT_DESCENDING_CREATION_DATE,TRUE);
+ ui_widget_set_sensitive(MENU_SORT_TAG_PATH,AM_SORT_ASCENDING_TRACK_NUMBER,TRUE);
+ ui_widget_set_sensitive(MENU_SORT_TAG_PATH,AM_SORT_DESCENDING_TRACK_NUMBER,TRUE);
+ ui_widget_set_sensitive(MENU_SORT_TAG_PATH,AM_SORT_ASCENDING_TITLE,TRUE);
+ ui_widget_set_sensitive(MENU_SORT_TAG_PATH,AM_SORT_DESCENDING_TITLE,TRUE);
+ ui_widget_set_sensitive(MENU_SORT_TAG_PATH,AM_SORT_ASCENDING_ARTIST,TRUE);
+ ui_widget_set_sensitive(MENU_SORT_TAG_PATH,AM_SORT_DESCENDING_ARTIST,TRUE);
+ ui_widget_set_sensitive(MENU_SORT_TAG_PATH,AM_SORT_ASCENDING_ALBUM,TRUE);
+ ui_widget_set_sensitive(MENU_SORT_TAG_PATH,AM_SORT_DESCENDING_ALBUM,TRUE);
+ ui_widget_set_sensitive(MENU_SORT_TAG_PATH,AM_SORT_ASCENDING_YEAR,TRUE);
+ ui_widget_set_sensitive(MENU_SORT_TAG_PATH,AM_SORT_DESCENDING_YEAR,TRUE);
+ ui_widget_set_sensitive(MENU_SORT_TAG_PATH,AM_SORT_ASCENDING_GENRE,TRUE);
+ ui_widget_set_sensitive(MENU_SORT_TAG_PATH,AM_SORT_DESCENDING_GENRE,TRUE);
+ ui_widget_set_sensitive(MENU_SORT_TAG_PATH,AM_SORT_ASCENDING_COMMENT,TRUE);
+ ui_widget_set_sensitive(MENU_SORT_TAG_PATH,AM_SORT_DESCENDING_COMMENT,TRUE);
+ ui_widget_set_sensitive(MENU_SORT_PROP_PATH,AM_SORT_ASCENDING_FILE_TYPE,TRUE);
+ ui_widget_set_sensitive(MENU_SORT_PROP_PATH,AM_SORT_DESCENDING_FILE_TYPE,TRUE);
+ ui_widget_set_sensitive(MENU_SORT_PROP_PATH,AM_SORT_ASCENDING_FILE_SIZE,TRUE);
+ ui_widget_set_sensitive(MENU_SORT_PROP_PATH,AM_SORT_DESCENDING_FILE_SIZE,TRUE);
+ ui_widget_set_sensitive(MENU_SORT_PROP_PATH,AM_SORT_ASCENDING_FILE_DURATION,TRUE);
+ ui_widget_set_sensitive(MENU_SORT_PROP_PATH,AM_SORT_DESCENDING_FILE_DURATION,TRUE);
+ ui_widget_set_sensitive(MENU_SORT_PROP_PATH,AM_SORT_ASCENDING_FILE_BITRATE,TRUE);
+ ui_widget_set_sensitive(MENU_SORT_PROP_PATH,AM_SORT_DESCENDING_FILE_BITRATE,TRUE);
+ ui_widget_set_sensitive(MENU_SORT_PROP_PATH,AM_SORT_ASCENDING_FILE_SAMPLERATE,TRUE);
+ ui_widget_set_sensitive(MENU_SORT_PROP_PATH,AM_SORT_DESCENDING_FILE_SAMPLERATE,TRUE);
+ ui_widget_set_sensitive(MENU_FILE,AM_SCAN,TRUE);
+ ui_widget_set_sensitive(MENU_FILE,AM_REMOVE,TRUE);
+ ui_widget_set_sensitive(MENU_MISC,AM_SEARCH_FILE,TRUE);
+ ui_widget_set_sensitive(MENU_MISC,AM_FILENAME_FROM_TXT,TRUE);
+ ui_widget_set_sensitive(MENU_MISC,AM_WRITE_PLAYLIST,TRUE);
+ ui_widget_set_sensitive(MENU_MISC,AM_RUN_AUDIO_PLAYER,TRUE);
+ ui_widget_set_sensitive(MENU_SCANNER,AM_SCANNER_FILL_TAG,TRUE);
+ ui_widget_set_sensitive(MENU_SCANNER,AM_SCANNER_RENAME_FILE,TRUE);
+ ui_widget_set_sensitive(MENU_SCANNER,AM_SCANNER_PROCESS_FIELDS,TRUE);
+
+ /* Check if one of the selected files has undo or redo data */
+ if (BrowserList)
+ {
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(BrowserList));
+ selfilelist = gtk_tree_selection_get_selected_rows(selection, NULL);
+ while (selfilelist)
+ {
+ etfile = Browser_List_Get_ETFile_From_Path(selfilelist->data);
+ has_undo |= ET_File_Data_Has_Undo_Data(etfile);
+ has_redo |= ET_File_Data_Has_Redo_Data(etfile);
+ //has_to_save |= ET_Check_If_File_Is_Saved((ET_File *)etfilelist->data);
+ if ((has_undo && has_redo) || !selfilelist->next) // Useless to check the other files
+ break;
+ selfilelist = g_list_next(selfilelist);
+ }
+ g_list_foreach(selfilelist, (GFunc) gtk_tree_path_free, NULL);
+ g_list_free(selfilelist);
+ }
+
+ /* Enable undo commands if there are undo data */
+ if (has_undo)
+ ui_widget_set_sensitive(MENU_FILE, AM_UNDO, TRUE);
+ else
+ ui_widget_set_sensitive(MENU_FILE, AM_UNDO, FALSE);
+
+ /* Enable redo commands if there are redo data */
+ if (has_redo)
+ ui_widget_set_sensitive(MENU_FILE, AM_REDO, TRUE);
+ else
+ ui_widget_set_sensitive(MENU_FILE, AM_REDO, FALSE);
+
+ /* Enable save file command if file has been changed */
+ // Desactivated because problem with only one file in the list
+ //if (has_to_save)
+ //{
+ // gtk_widget_set_sensitive(TBSaveButton,FALSE);
+ // ui_widget_set_sensitive(AM_SAVE,FALSE);
+ //}else
+ //{
+ ui_widget_set_sensitive(MENU_FILE, AM_SAVE, TRUE);
+ //}
+ ui_widget_set_sensitive(MENU_FILE, AM_SAVE_FORCED, TRUE);
+
+ /* Enable undo command if there are data into main undo list (history list) */
+ if (ET_History_File_List_Has_Undo_Data())
+ ui_widget_set_sensitive(MENU_FILE, AM_UNDO_HISTORY, TRUE);
+ else
+ ui_widget_set_sensitive(MENU_FILE, AM_UNDO_HISTORY, FALSE);
+
+ /* Enable redo commands if there are data into main redo list (history list) */
+ if (ET_History_File_List_Has_Redo_Data())
+ ui_widget_set_sensitive(MENU_FILE, AM_REDO_HISTORY, TRUE);
+ else
+ ui_widget_set_sensitive(MENU_FILE, AM_REDO_HISTORY, FALSE);
+ }
+
+ if (!ETCore->ETFileDisplayedList->prev) /* Is it the 1st item ? */
+ {
+ ui_widget_set_sensitive(MENU_FILE, AM_PREV,FALSE);
+ ui_widget_set_sensitive(MENU_FILE, AM_FIRST,FALSE);
+ }else
+ {
+ ui_widget_set_sensitive(MENU_FILE, AM_PREV,TRUE);
+ ui_widget_set_sensitive(MENU_FILE, AM_FIRST,TRUE);
+ }
+ if (!ETCore->ETFileDisplayedList->next) /* Is it the last item ? */
+ {
+ ui_widget_set_sensitive(MENU_FILE, AM_NEXT,FALSE);
+ ui_widget_set_sensitive(MENU_FILE, AM_LAST,FALSE);
+ }else
+ {
+ ui_widget_set_sensitive(MENU_FILE, AM_NEXT,TRUE);
+ ui_widget_set_sensitive(MENU_FILE, AM_LAST,TRUE);
+ }
+}
+
+/*
+ * Just to disable buttons when we are saving files (do not disable Quit button)
+ */
+void Disable_Command_Buttons (void)
+{
+ /* Scanner Window */
+ if (SWScanButton)
+ gtk_widget_set_sensitive(SWScanButton,FALSE);
+
+ /* "File" menu commands */
+ ui_widget_set_sensitive(MENU_FILE,AM_OPEN_FILE_WITH,FALSE);
+ ui_widget_set_sensitive(MENU_FILE,AM_SELECT_ALL_FILES,FALSE);
+ ui_widget_set_sensitive(MENU_FILE,AM_UNSELECT_ALL_FILES,FALSE);
+ ui_widget_set_sensitive(MENU_FILE,AM_INVERT_SELECTION,FALSE);
+ ui_widget_set_sensitive(MENU_FILE,AM_DELETE_FILE,FALSE);
+ ui_widget_set_sensitive(MENU_FILE,AM_FIRST,FALSE);
+ ui_widget_set_sensitive(MENU_FILE,AM_PREV,FALSE);
+ ui_widget_set_sensitive(MENU_FILE,AM_NEXT,FALSE);
+ ui_widget_set_sensitive(MENU_FILE,AM_LAST,FALSE);
+ ui_widget_set_sensitive(MENU_FILE,AM_SCAN,FALSE);
+ ui_widget_set_sensitive(MENU_FILE,AM_REMOVE,FALSE);
+ ui_widget_set_sensitive(MENU_FILE,AM_UNDO,FALSE);
+ ui_widget_set_sensitive(MENU_FILE,AM_REDO,FALSE);
+ ui_widget_set_sensitive(MENU_FILE,AM_SAVE,FALSE);
+ ui_widget_set_sensitive(MENU_FILE,AM_SAVE_FORCED,FALSE);
+ ui_widget_set_sensitive(MENU_FILE,AM_UNDO_HISTORY,FALSE);
+ ui_widget_set_sensitive(MENU_FILE,AM_REDO_HISTORY,FALSE);
+
+ /* "Scanner" menu commands */
+ ui_widget_set_sensitive(MENU_SCANNER,AM_SCANNER_FILL_TAG,FALSE);
+ ui_widget_set_sensitive(MENU_SCANNER,AM_SCANNER_RENAME_FILE,FALSE);
+ ui_widget_set_sensitive(MENU_SCANNER,AM_SCANNER_PROCESS_FIELDS,FALSE);
+
+}
+
+/*
+ * Disable (FALSE) / Enable (TRUE) all user widgets in the tag area
+ */
+void Tag_Area_Set_Sensitive (gboolean activate)
+{
+ if (!TagArea) return;
+
+ // TAG Area (entries + buttons)
+ gtk_widget_set_sensitive(GTK_BIN(TagArea)->child,activate);
+
+ /*// TAG Area
+ gtk_widget_set_sensitive(GTK_WIDGET(TitleEntry), activate);
+ gtk_widget_set_sensitive(GTK_WIDGET(ArtistEntry), activate);
+ gtk_widget_set_sensitive(GTK_WIDGET(AlbumEntry), activate);
+ gtk_widget_set_sensitive(GTK_WIDGET(DiscNumberEntry), activate);
+ gtk_widget_set_sensitive(GTK_WIDGET(YearEntry), activate);
+ gtk_widget_set_sensitive(GTK_WIDGET(TrackEntryCombo), activate);
+ gtk_widget_set_sensitive(GTK_WIDGET(TrackTotalEntry), activate);
+ gtk_widget_set_sensitive(GTK_WIDGET(CommentEntry), activate);
+ gtk_widget_set_sensitive(GTK_WIDGET(GenreCombo), activate);
+ gtk_widget_set_sensitive(GTK_WIDGET(PictureScrollWindow), activate);
+ // Mini buttons
+ gtk_widget_set_sensitive(GTK_WIDGET(TitleMButton), activate);
+ gtk_widget_set_sensitive(GTK_WIDGET(ArtistMButton), activate);
+ gtk_widget_set_sensitive(GTK_WIDGET(DiscNumberMButton), activate);
+ gtk_widget_set_sensitive(GTK_WIDGET(AlbumMButton), activate);
+ gtk_widget_set_sensitive(GTK_WIDGET(YearMButton), activate);
+ gtk_widget_set_sensitive(GTK_WIDGET(TrackMButton), activate);
+ gtk_widget_set_sensitive(GTK_WIDGET(TrackMButtonSequence), activate);
+ gtk_widget_set_sensitive(GTK_WIDGET(TrackMButtonNbrFiles), activate);
+ gtk_widget_set_sensitive(GTK_WIDGET(CommentMButton), activate);
+ gtk_widget_set_sensitive(GTK_WIDGET(GenreMButton), activate);
+ gtk_widget_set_sensitive(GTK_WIDGET(PictureMButton), activate);*/
+}
+
+/*
+ * Disable (FALSE) / Enable (TRUE) all user widgets in the file area
+ */
+void File_Area_Set_Sensitive (gboolean activate)
+{
+ if (!FileArea) return;
+
+ // File Area
+ gtk_widget_set_sensitive(GTK_BIN(FileArea)->child,activate);
+ /*gtk_widget_set_sensitive(GTK_WIDGET(FileEntry),activate);*/
+}
+
+/*
+ * Display controls according the kind of tag... (Hide some controls if not available for a tag type)
+ */
+void Tag_Area_Display_Controls (ET_File *ETFile)
+{
+ if (!ETFile || !ETFile->ETFileDescription || !TitleLabel)
+ return;
+
+ // Commun controls for all tags
+ gtk_widget_show(GTK_WIDGET(TitleLabel));
+ gtk_widget_show(GTK_WIDGET(TitleEntry));
+ gtk_widget_show(GTK_WIDGET(TitleMButton));
+ gtk_widget_show(GTK_WIDGET(ArtistLabel));
+ gtk_widget_show(GTK_WIDGET(ArtistEntry));
+ gtk_widget_show(GTK_WIDGET(ArtistMButton));
+ gtk_widget_show(GTK_WIDGET(AlbumLabel));
+ gtk_widget_show(GTK_WIDGET(AlbumEntry));
+ gtk_widget_show(GTK_WIDGET(AlbumMButton));
+ gtk_widget_show(GTK_WIDGET(YearLabel));
+ gtk_widget_show(GTK_WIDGET(YearEntry));
+ gtk_widget_show(GTK_WIDGET(YearMButton));
+ gtk_widget_show(GTK_WIDGET(TrackLabel));
+ gtk_widget_show(GTK_WIDGET(TrackEntryCombo));
+ gtk_widget_show(GTK_WIDGET(TrackTotalEntry));
+ gtk_widget_show(GTK_WIDGET(TrackMButton));
+ gtk_widget_show(GTK_WIDGET(TrackMButtonSequence));
+ gtk_widget_show(GTK_WIDGET(TrackMButtonNbrFiles));
+ gtk_widget_show(GTK_WIDGET(GenreLabel));
+ gtk_widget_show(GTK_WIDGET(GenreCombo));
+ gtk_widget_show(GTK_WIDGET(GenreMButton));
+ gtk_widget_show(GTK_WIDGET(CommentLabel));
+ gtk_widget_show(GTK_WIDGET(CommentEntry));
+ gtk_widget_show(GTK_WIDGET(CommentMButton));
+
+ // Special controls to display or not!
+ switch (ETFile->ETFileDescription->TagType)
+ {
+ case ID3_TAG:
+ if (!FILE_WRITING_ID3V2_WRITE_TAG)
+ {
+ // ID3v1 : Hide specifics ID3v2 fields if not activated!
+ gtk_widget_hide(GTK_WIDGET(DiscNumberLabel));
+ gtk_widget_hide(GTK_WIDGET(DiscNumberEntry));
+ gtk_widget_hide(GTK_WIDGET(DiscNumberMButton));
+ gtk_widget_hide(GTK_WIDGET(ComposerLabel));
+ gtk_widget_hide(GTK_WIDGET(ComposerEntry));
+ gtk_widget_hide(GTK_WIDGET(ComposerMButton));
+ gtk_widget_hide(GTK_WIDGET(OrigArtistLabel));
+ gtk_widget_hide(GTK_WIDGET(OrigArtistEntry));
+ gtk_widget_hide(GTK_WIDGET(OrigArtistMButton));
+ gtk_widget_hide(GTK_WIDGET(CopyrightLabel));
+ gtk_widget_hide(GTK_WIDGET(CopyrightEntry));
+ gtk_widget_hide(GTK_WIDGET(CopyrightMButton));
+ gtk_widget_hide(GTK_WIDGET(URLLabel));
+ gtk_widget_hide(GTK_WIDGET(URLEntry));
+ gtk_widget_hide(GTK_WIDGET(URLMButton));
+ gtk_widget_hide(GTK_WIDGET(EncodedByLabel));
+ gtk_widget_hide(GTK_WIDGET(EncodedByEntry));
+ gtk_widget_hide(GTK_WIDGET(EncodedByMButton));
+ gtk_widget_hide(GTK_WIDGET(PictureLabel));
+ gtk_widget_hide(GTK_WIDGET(PictureScrollWindow));
+ gtk_widget_hide(GTK_WIDGET(PictureMButton));
+ gtk_widget_hide(GTK_WIDGET(PictureClearButton));
+ gtk_widget_hide(GTK_WIDGET(PictureAddButton));
+ gtk_widget_hide(GTK_WIDGET(PictureSaveButton));
+ gtk_widget_hide(GTK_WIDGET(PicturePropertiesButton));
+ }else
+ {
+ gtk_widget_show(GTK_WIDGET(DiscNumberLabel));
+ gtk_widget_show(GTK_WIDGET(DiscNumberEntry));
+ gtk_widget_show(GTK_WIDGET(DiscNumberMButton));
+ gtk_widget_show(GTK_WIDGET(ComposerLabel));
+ gtk_widget_show(GTK_WIDGET(ComposerEntry));
+ gtk_widget_show(GTK_WIDGET(ComposerMButton));
+ gtk_widget_show(GTK_WIDGET(OrigArtistLabel));
+ gtk_widget_show(GTK_WIDGET(OrigArtistEntry));
+ gtk_widget_show(GTK_WIDGET(OrigArtistMButton));
+ gtk_widget_show(GTK_WIDGET(CopyrightLabel));
+ gtk_widget_show(GTK_WIDGET(CopyrightEntry));
+ gtk_widget_show(GTK_WIDGET(CopyrightMButton));
+ gtk_widget_show(GTK_WIDGET(URLLabel));
+ gtk_widget_show(GTK_WIDGET(URLEntry));
+ gtk_widget_show(GTK_WIDGET(URLMButton));
+ gtk_widget_show(GTK_WIDGET(EncodedByLabel));
+ gtk_widget_show(GTK_WIDGET(EncodedByEntry));
+ gtk_widget_show(GTK_WIDGET(EncodedByMButton));
+ gtk_widget_show(GTK_WIDGET(PictureLabel));
+ gtk_widget_show(GTK_WIDGET(PictureScrollWindow));
+ gtk_widget_show(GTK_WIDGET(PictureMButton));
+ gtk_widget_show(GTK_WIDGET(PictureClearButton));
+ gtk_widget_show(GTK_WIDGET(PictureAddButton));
+ gtk_widget_show(GTK_WIDGET(PictureSaveButton));
+ gtk_widget_show(GTK_WIDGET(PicturePropertiesButton));
+ }
+ break;
+
+#ifdef ENABLE_OGG
+ case OGG_TAG:
+ gtk_widget_show(GTK_WIDGET(DiscNumberLabel));
+ gtk_widget_show(GTK_WIDGET(DiscNumberEntry));
+ gtk_widget_show(GTK_WIDGET(DiscNumberMButton));
+ gtk_widget_show(GTK_WIDGET(ComposerLabel));
+ gtk_widget_show(GTK_WIDGET(ComposerEntry));
+ gtk_widget_show(GTK_WIDGET(ComposerMButton));
+ gtk_widget_show(GTK_WIDGET(OrigArtistLabel));
+ gtk_widget_show(GTK_WIDGET(OrigArtistEntry));
+ gtk_widget_show(GTK_WIDGET(OrigArtistMButton));
+ gtk_widget_show(GTK_WIDGET(CopyrightLabel));
+ gtk_widget_show(GTK_WIDGET(CopyrightEntry));
+ gtk_widget_show(GTK_WIDGET(CopyrightMButton));
+ gtk_widget_show(GTK_WIDGET(URLLabel));
+ gtk_widget_show(GTK_WIDGET(URLEntry));
+ gtk_widget_show(GTK_WIDGET(URLMButton));
+ gtk_widget_show(GTK_WIDGET(EncodedByLabel));
+ gtk_widget_show(GTK_WIDGET(EncodedByEntry));
+ gtk_widget_show(GTK_WIDGET(EncodedByMButton));
+ gtk_widget_hide(GTK_WIDGET(PictureLabel));
+ gtk_widget_hide(GTK_WIDGET(PictureScrollWindow));
+ gtk_widget_hide(GTK_WIDGET(PictureMButton));
+ gtk_widget_hide(GTK_WIDGET(PictureClearButton));
+ gtk_widget_hide(GTK_WIDGET(PictureAddButton));
+ gtk_widget_hide(GTK_WIDGET(PictureSaveButton));
+ gtk_widget_hide(GTK_WIDGET(PicturePropertiesButton));
+ break;
+#endif
+
+#ifdef ENABLE_FLAC
+ case FLAC_TAG:
+ gtk_widget_show(GTK_WIDGET(DiscNumberLabel));
+ gtk_widget_show(GTK_WIDGET(DiscNumberEntry));
+ gtk_widget_show(GTK_WIDGET(DiscNumberMButton));
+ gtk_widget_show(GTK_WIDGET(ComposerLabel));
+ gtk_widget_show(GTK_WIDGET(ComposerEntry));
+ gtk_widget_show(GTK_WIDGET(ComposerMButton));
+ gtk_widget_show(GTK_WIDGET(OrigArtistLabel));
+ gtk_widget_show(GTK_WIDGET(OrigArtistEntry));
+ gtk_widget_show(GTK_WIDGET(OrigArtistMButton));
+ gtk_widget_show(GTK_WIDGET(CopyrightLabel));
+ gtk_widget_show(GTK_WIDGET(CopyrightEntry));
+ gtk_widget_show(GTK_WIDGET(CopyrightMButton));
+ gtk_widget_show(GTK_WIDGET(URLLabel));
+ gtk_widget_show(GTK_WIDGET(URLEntry));
+ gtk_widget_show(GTK_WIDGET(URLMButton));
+ gtk_widget_show(GTK_WIDGET(EncodedByLabel));
+ gtk_widget_show(GTK_WIDGET(EncodedByEntry));
+ gtk_widget_show(GTK_WIDGET(EncodedByMButton));
+ if (WRITE_ID3_TAGS_IN_FLAC_FILE)
+ {
+ gtk_widget_show(GTK_WIDGET(PictureLabel));
+ gtk_widget_show(GTK_WIDGET(PictureScrollWindow));
+ gtk_widget_show(GTK_WIDGET(PictureMButton));
+ gtk_widget_show(GTK_WIDGET(PictureClearButton));
+ gtk_widget_show(GTK_WIDGET(PictureAddButton));
+ gtk_widget_show(GTK_WIDGET(PictureSaveButton));
+ gtk_widget_show(GTK_WIDGET(PicturePropertiesButton));
+ }else
+ {
+ gtk_widget_hide(GTK_WIDGET(PictureLabel));
+ gtk_widget_hide(GTK_WIDGET(PictureScrollWindow));
+ gtk_widget_hide(GTK_WIDGET(PictureMButton));
+ gtk_widget_hide(GTK_WIDGET(PictureClearButton));
+ gtk_widget_hide(GTK_WIDGET(PictureAddButton));
+ gtk_widget_hide(GTK_WIDGET(PictureSaveButton));
+ gtk_widget_hide(GTK_WIDGET(PicturePropertiesButton));
+ }
+ break;
+#endif
+
+ case APE_TAG:
+ gtk_widget_show(GTK_WIDGET(DiscNumberLabel));
+ gtk_widget_show(GTK_WIDGET(DiscNumberEntry));
+ gtk_widget_show(GTK_WIDGET(DiscNumberMButton));
+ gtk_widget_show(GTK_WIDGET(ComposerLabel));
+ gtk_widget_show(GTK_WIDGET(ComposerEntry));
+ gtk_widget_show(GTK_WIDGET(ComposerMButton));
+ gtk_widget_show(GTK_WIDGET(OrigArtistLabel));
+ gtk_widget_show(GTK_WIDGET(OrigArtistEntry));
+ gtk_widget_show(GTK_WIDGET(OrigArtistMButton));
+ gtk_widget_show(GTK_WIDGET(CopyrightLabel));
+ gtk_widget_show(GTK_WIDGET(CopyrightEntry));
+ gtk_widget_show(GTK_WIDGET(CopyrightMButton));
+ gtk_widget_show(GTK_WIDGET(URLLabel));
+ gtk_widget_show(GTK_WIDGET(URLEntry));
+ gtk_widget_show(GTK_WIDGET(URLMButton));
+ gtk_widget_show(GTK_WIDGET(EncodedByLabel));
+ gtk_widget_show(GTK_WIDGET(EncodedByEntry));
+ gtk_widget_show(GTK_WIDGET(EncodedByMButton));
+ gtk_widget_hide(GTK_WIDGET(PictureLabel));
+ gtk_widget_hide(GTK_WIDGET(PictureScrollWindow));
+ gtk_widget_hide(GTK_WIDGET(PictureMButton));
+ gtk_widget_hide(GTK_WIDGET(PictureClearButton));
+ gtk_widget_hide(GTK_WIDGET(PictureAddButton));
+ gtk_widget_hide(GTK_WIDGET(PictureSaveButton));
+ gtk_widget_hide(GTK_WIDGET(PicturePropertiesButton));
+ break;
+
+#ifdef ENABLE_MP4
+ case MP4_TAG:
+ gtk_widget_show(GTK_WIDGET(DiscNumberLabel));
+ gtk_widget_show(GTK_WIDGET(DiscNumberEntry));
+ gtk_widget_show(GTK_WIDGET(DiscNumberMButton));
+ gtk_widget_show(GTK_WIDGET(ComposerLabel));
+ gtk_widget_show(GTK_WIDGET(ComposerEntry));
+ gtk_widget_show(GTK_WIDGET(ComposerMButton));
+ gtk_widget_hide(GTK_WIDGET(OrigArtistLabel));
+ gtk_widget_hide(GTK_WIDGET(OrigArtistEntry));
+ gtk_widget_hide(GTK_WIDGET(OrigArtistMButton));
+ gtk_widget_hide(GTK_WIDGET(CopyrightLabel));
+ gtk_widget_hide(GTK_WIDGET(CopyrightEntry));
+ gtk_widget_hide(GTK_WIDGET(CopyrightMButton));
+ gtk_widget_hide(GTK_WIDGET(URLLabel));
+ gtk_widget_hide(GTK_WIDGET(URLEntry));
+ gtk_widget_hide(GTK_WIDGET(URLMButton));
+ gtk_widget_show(GTK_WIDGET(EncodedByLabel));
+ gtk_widget_show(GTK_WIDGET(EncodedByEntry));
+ gtk_widget_show(GTK_WIDGET(EncodedByMButton));
+ gtk_widget_show(GTK_WIDGET(PictureLabel));
+ gtk_widget_show(GTK_WIDGET(PictureScrollWindow));
+ gtk_widget_show(GTK_WIDGET(PictureMButton));
+ gtk_widget_show(GTK_WIDGET(PictureClearButton));
+ gtk_widget_show(GTK_WIDGET(PictureAddButton));
+ gtk_widget_show(GTK_WIDGET(PictureSaveButton));
+ gtk_widget_show(GTK_WIDGET(PicturePropertiesButton));
+ break;
+#endif
+
+#ifdef ENABLE_WAVPACK
+ case WAVPACK_TAG:
+ gtk_widget_show(GTK_WIDGET(DiscNumberLabel));
+ gtk_widget_show(GTK_WIDGET(DiscNumberEntry));
+ gtk_widget_show(GTK_WIDGET(DiscNumberMButton));
+ gtk_widget_show(GTK_WIDGET(ComposerLabel));
+ gtk_widget_show(GTK_WIDGET(ComposerEntry));
+ gtk_widget_show(GTK_WIDGET(ComposerMButton));
+ gtk_widget_show(GTK_WIDGET(OrigArtistLabel));
+ gtk_widget_show(GTK_WIDGET(OrigArtistEntry));
+ gtk_widget_show(GTK_WIDGET(OrigArtistMButton));
+ gtk_widget_show(GTK_WIDGET(CopyrightLabel));
+ gtk_widget_show(GTK_WIDGET(CopyrightEntry));
+ gtk_widget_show(GTK_WIDGET(CopyrightMButton));
+ gtk_widget_show(GTK_WIDGET(URLLabel));
+ gtk_widget_show(GTK_WIDGET(URLEntry));
+ gtk_widget_show(GTK_WIDGET(URLMButton));
+ gtk_widget_show(GTK_WIDGET(EncodedByLabel));
+ gtk_widget_show(GTK_WIDGET(EncodedByEntry));
+ gtk_widget_show(GTK_WIDGET(EncodedByMButton));
+ gtk_widget_hide(GTK_WIDGET(PictureLabel));
+ gtk_widget_hide(GTK_WIDGET(PictureScrollWindow));
+ gtk_widget_hide(GTK_WIDGET(PictureMButton));
+ gtk_widget_hide(GTK_WIDGET(PictureClearButton));
+ gtk_widget_hide(GTK_WIDGET(PictureAddButton));
+ gtk_widget_hide(GTK_WIDGET(PictureSaveButton));
+ gtk_widget_hide(GTK_WIDGET(PicturePropertiesButton));
+ break;
+#endif /* ENABLE_WAVPACK */
+
+ case UNKNOWN_TAG:
+ default:
+ gtk_widget_hide(GTK_WIDGET(DiscNumberLabel));
+ gtk_widget_hide(GTK_WIDGET(DiscNumberEntry));
+ gtk_widget_hide(GTK_WIDGET(DiscNumberMButton));
+ gtk_widget_hide(GTK_WIDGET(ComposerLabel));
+ gtk_widget_hide(GTK_WIDGET(ComposerEntry));
+ gtk_widget_hide(GTK_WIDGET(ComposerMButton));
+ gtk_widget_hide(GTK_WIDGET(OrigArtistLabel));
+ gtk_widget_hide(GTK_WIDGET(OrigArtistEntry));
+ gtk_widget_hide(GTK_WIDGET(OrigArtistMButton));
+ gtk_widget_hide(GTK_WIDGET(CopyrightLabel));
+ gtk_widget_hide(GTK_WIDGET(CopyrightEntry));
+ gtk_widget_hide(GTK_WIDGET(CopyrightMButton));
+ gtk_widget_hide(GTK_WIDGET(URLLabel));
+ gtk_widget_hide(GTK_WIDGET(URLEntry));
+ gtk_widget_hide(GTK_WIDGET(URLMButton));
+ gtk_widget_hide(GTK_WIDGET(EncodedByLabel));
+ gtk_widget_hide(GTK_WIDGET(EncodedByEntry));
+ gtk_widget_hide(GTK_WIDGET(EncodedByMButton));
+ gtk_widget_hide(GTK_WIDGET(PictureLabel));
+ gtk_widget_hide(GTK_WIDGET(PictureScrollWindow));
+ gtk_widget_hide(GTK_WIDGET(PictureMButton));
+ gtk_widget_hide(GTK_WIDGET(PictureClearButton));
+ gtk_widget_hide(GTK_WIDGET(PictureAddButton));
+ gtk_widget_hide(GTK_WIDGET(PictureSaveButton));
+ gtk_widget_hide(GTK_WIDGET(PicturePropertiesButton));
+ break;
+ }
+}
+
+
+/*
+ * Clear the entries of tag area
+ */
+void Clear_Tag_Entry_Fields (void)
+{
+ //GtkTextBuffer *textbuffer;
+
+ if (!TitleEntry) return;
+
+ gtk_entry_set_text(GTK_ENTRY(TitleEntry), "");
+ gtk_entry_set_text(GTK_ENTRY(ArtistEntry), "");
+ gtk_entry_set_text(GTK_ENTRY(AlbumEntry), "");
+ gtk_entry_set_text(GTK_ENTRY(DiscNumberEntry), "");
+ gtk_entry_set_text(GTK_ENTRY(YearEntry), "");
+ gtk_entry_set_text(GTK_ENTRY(GTK_BIN(TrackEntryCombo)->child), "");
+ gtk_entry_set_text(GTK_ENTRY(TrackTotalEntry), "");
+ gtk_entry_set_text(GTK_ENTRY(GTK_BIN(GenreCombo)->child), "");
+ gtk_entry_set_text(GTK_ENTRY(CommentEntry), "");
+ //textbuffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(CommentView));
+ //gtk_text_buffer_set_text(GTK_TEXT_BUFFER(textbuffer), "", -1);
+ gtk_entry_set_text(GTK_ENTRY(ComposerEntry), "");
+ gtk_entry_set_text(GTK_ENTRY(OrigArtistEntry), "");
+ gtk_entry_set_text(GTK_ENTRY(CopyrightEntry), "");
+ gtk_entry_set_text(GTK_ENTRY(URLEntry), "");
+ gtk_entry_set_text(GTK_ENTRY(EncodedByEntry), "");
+ PictureEntry_Clear();
+}
+
+
+/*
+ * Clear the entry of file area
+ */
+void Clear_File_Entry_Field (void)
+{
+ if (!FileEntry) return;
+
+ gtk_entry_set_text(GTK_ENTRY(FileEntry),"");
+}
+
+
+/*
+ * Clear the header informations
+ */
+void Clear_Header_Fields (void)
+{
+ if (!VersionValueLabel) return;
+
+ /* Default values are MPs data */
+ gtk_label_set_text(GTK_LABEL(VersionLabel), _("MPEG"));
+ gtk_label_set_text(GTK_LABEL(VersionValueLabel), _("?, Layer ?"));
+ gtk_label_set_text(GTK_LABEL(BitrateValueLabel), _("? kb/s"));
+ gtk_label_set_text(GTK_LABEL(SampleRateValueLabel),_("? Hz"));
+ gtk_label_set_text(GTK_LABEL(ModeLabel), _("Mode:"));
+ gtk_label_set_text(GTK_LABEL(ModeValueLabel), _("?"));
+ gtk_label_set_text(GTK_LABEL(SizeValueLabel), _("?"));
+ gtk_label_set_text(GTK_LABEL(DurationValueLabel), _("?"));
+}
+
+
+
+
+/*
+ * Load the default directory when the user interface is completely displayed
+ * to avoid bad visualization effect at startup.
+ */
+void Init_Load_Default_Dir (void)
+{
+ //ETCore->ETFileList = (GList *)NULL;
+ ET_Core_Free();
+ ET_Core_Initialize();
+
+ // Open the scanner window
+ if (OPEN_SCANNER_WINDOW_ON_STARTUP)
+ Open_ScannerWindow(SCANNER_TYPE); // Open the last selected scanner
+
+ Statusbar_Message(_("Select a directory to browse!"),FALSE);
+ if (INIT_DIRECTORY)
+ {
+ Browser_Tree_Select_Dir(INIT_DIRECTORY);
+ }else
+ {
+ Browser_Load_Default_Directory();
+ }
+
+ // To set sensivity of buttons in the case if the default directory is invalid
+ Update_Command_Buttons_Sensivity();
+
+ g_source_remove(idle_handler_id);
+}
+
+
+
+void Convert_P20_And_Undescore_Into_Spaces (GtkWidget *entry)
+{
+ gchar *string = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
+
+ Scan_Convert_Underscore_Into_Space(string);
+ Scan_Convert_P20_Into_Space(string);
+ gtk_entry_set_text(GTK_ENTRY(entry),string);
+ g_free(string);
+}
+
+void Convert_Space_Into_Undescore (GtkWidget *entry)
+{
+ gchar *string = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
+
+ Scan_Convert_Space_Into_Undescore(string);
+ gtk_entry_set_text(GTK_ENTRY(entry),string);
+ g_free(string);
+}
+
+void Convert_All_Uppercase (GtkWidget *entry)
+{
+ gchar *string = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
+
+ Scan_Process_Fields_All_Uppercase(string);
+ gtk_entry_set_text(GTK_ENTRY(entry),string);
+ g_free(string);
+}
+
+void Convert_All_Downcase (GtkWidget *entry)
+{
+ gchar *string = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
+
+ Scan_Process_Fields_All_Downcase(string);
+ gtk_entry_set_text(GTK_ENTRY(entry),string);
+ g_free(string);
+}
+
+void Convert_Letter_Uppercase (GtkWidget *entry)
+{
+ gchar *string = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
+
+ Scan_Process_Fields_Letter_Uppercase(string);
+ gtk_entry_set_text(GTK_ENTRY(entry),string);
+ g_free(string);
+}
+
+void Convert_First_Letters_Uppercase (GtkWidget *entry)
+{
+ gchar *string = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
+
+ Scan_Process_Fields_First_Letters_Uppercase(string);
+ gtk_entry_set_text(GTK_ENTRY(entry),string);
+ g_free(string);
+}
+
+void Convert_Remove_Space (GtkWidget *entry)
+{
+ gchar *string = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
+
+ Scan_Process_Fields_Remove_Space(string);
+ gtk_entry_set_text(GTK_ENTRY(entry),string);
+ g_free(string);
+}
+
+void Convert_Insert_Space (GtkWidget *entry)
+{
+ // FIX ME : we suppose that it will not grow more than 2 times its size...
+ guint string_length = 2 * strlen(gtk_entry_get_text(GTK_ENTRY(entry)));
+ gchar *string = g_malloc(string_length+1);
+ strncpy(string,gtk_entry_get_text(GTK_ENTRY(entry)),string_length);
+ string[string_length]='\0';
+
+ Scan_Process_Fields_Insert_Space(string);
+ gtk_entry_set_text(GTK_ENTRY(entry),string);
+ g_free(string);
+}
+
+void Convert_Only_One_Space (GtkWidget *entry)
+{
+ gchar *string = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
+
+ Scan_Process_Fields_Keep_One_Space(string);
+ gtk_entry_set_text(GTK_ENTRY(entry),string);
+ g_free(string);
+}
+
+/*
+ * Entry_Popup_Menu_Handler: show the popup menu when the third mouse button is pressed.
+ */
+gboolean Entry_Popup_Menu_Handler (GtkMenu *menu, GdkEventButton *event)
+{
+ if (event && (event->type==GDK_BUTTON_PRESS) && (event->button==3))
+ {
+ /* FIX ME : this is not very clean, but if we use 'event->button' (contains value of
+ * the 3rd button) instead of '1', we need to click two times the left mouse button
+ * to activate an item of the opened popup menu (when menu is attached to an entry). */
+ //gtk_menu_popup(menu,NULL,NULL,NULL,NULL,event->button,event->time);
+ gtk_menu_popup(menu,NULL,NULL,NULL,NULL,1,event->time);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/*
+ * Popup menu attached to all entries of tag + filename + rename combobox.
+ * Displayed when pressing the right mouse button and contains functions to process ths strings.
+ */
+void Attach_Popup_Menu_To_Tag_Entries (GtkEntry *entry)
+{
+ GtkWidget *PopupMenu;
+ GtkWidget *Image;
+ GtkWidget *MenuItem;
+
+
+ PopupMenu = gtk_menu_new();
+ g_signal_connect_swapped(G_OBJECT(entry),"button_press_event",
+ G_CALLBACK(Entry_Popup_Menu_Handler),G_OBJECT(PopupMenu));
+
+ /* Menu items */
+ MenuItem = gtk_image_menu_item_new_with_label(_("Tag selected files with this field"));
+ Image = gtk_image_new_from_stock(GTK_STOCK_JUMP_TO,GTK_ICON_SIZE_MENU);
+ gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(MenuItem),Image);
+ gtk_menu_shell_append(GTK_MENU_SHELL(PopupMenu),MenuItem);
+ g_signal_connect_swapped(G_OBJECT(MenuItem),"activate",
+ G_CALLBACK(Menu_Mini_Button_Clicked),G_OBJECT(entry));
+
+ /* Separator */
+ MenuItem = gtk_menu_item_new();
+ gtk_menu_shell_append(GTK_MENU_SHELL(PopupMenu),MenuItem);
+
+ MenuItem = gtk_image_menu_item_new_with_label(_("Convert '_' and '%20' to spaces"));
+ Image = gtk_image_new_from_stock(GTK_STOCK_CONVERT,GTK_ICON_SIZE_MENU);
+ gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(MenuItem),Image);
+ gtk_menu_shell_append(GTK_MENU_SHELL(PopupMenu),MenuItem);
+ g_signal_connect_swapped(G_OBJECT(MenuItem),"activate",
+ G_CALLBACK(Convert_P20_And_Undescore_Into_Spaces),G_OBJECT(entry));
+
+ MenuItem = gtk_image_menu_item_new_with_label(_("Convert ' ' to '_'"));
+ Image = gtk_image_new_from_stock(GTK_STOCK_CONVERT,GTK_ICON_SIZE_MENU);
+ gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(MenuItem),Image);
+ gtk_menu_shell_append(GTK_MENU_SHELL(PopupMenu),MenuItem);
+ g_signal_connect_swapped(G_OBJECT(MenuItem),"activate",
+ G_CALLBACK(Convert_Space_Into_Undescore),G_OBJECT(entry));
+
+ /* Separator */
+ MenuItem = gtk_menu_item_new();
+ gtk_menu_shell_append(GTK_MENU_SHELL(PopupMenu),MenuItem);
+
+ MenuItem = gtk_image_menu_item_new_with_label(_("All uppercase"));
+ Image = gtk_image_new_from_stock("easytag-all-uppercase",GTK_ICON_SIZE_MENU);
+ gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(MenuItem),Image);
+ gtk_menu_shell_append(GTK_MENU_SHELL(PopupMenu),MenuItem);
+ g_signal_connect_swapped(G_OBJECT(MenuItem),"activate",
+ G_CALLBACK(Convert_All_Uppercase),G_OBJECT(entry));
+
+ MenuItem = gtk_image_menu_item_new_with_label(_("All downcase"));
+ Image = gtk_image_new_from_stock("easytag-all-downcase",GTK_ICON_SIZE_MENU);
+ gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(MenuItem),Image);
+ gtk_menu_shell_append(GTK_MENU_SHELL(PopupMenu),MenuItem);
+ g_signal_connect_swapped(G_OBJECT(MenuItem),"activate",
+ G_CALLBACK(Convert_All_Downcase),G_OBJECT(entry));
+
+ MenuItem = gtk_image_menu_item_new_with_label(_("First letter uppercase"));
+ Image = gtk_image_new_from_stock("easytag-first-letter-uppercase",GTK_ICON_SIZE_MENU);
+ gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(MenuItem),Image);
+ gtk_menu_shell_append(GTK_MENU_SHELL(PopupMenu),MenuItem);
+ g_signal_connect_swapped(G_OBJECT(MenuItem),"activate",
+ G_CALLBACK(Convert_Letter_Uppercase),G_OBJECT(entry));
+
+ MenuItem = gtk_image_menu_item_new_with_label(_("First letter uppercase of each word"));
+ Image = gtk_image_new_from_stock("easytag-first-letter-uppercase-word",GTK_ICON_SIZE_MENU);
+ gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(MenuItem),Image);
+ gtk_menu_shell_append(GTK_MENU_SHELL(PopupMenu),MenuItem);
+ g_signal_connect_swapped(G_OBJECT(MenuItem),"activate",
+ G_CALLBACK(Convert_First_Letters_Uppercase),G_OBJECT(entry));
+
+ /* Separator */
+ MenuItem = gtk_menu_item_new();
+ gtk_menu_shell_append(GTK_MENU_SHELL(PopupMenu),MenuItem);
+
+ MenuItem = gtk_image_menu_item_new_with_label(_("Remove spaces"));
+ Image = gtk_image_new_from_stock(GTK_STOCK_REMOVE,GTK_ICON_SIZE_MENU);
+ gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(MenuItem),Image);
+ gtk_menu_shell_append(GTK_MENU_SHELL(PopupMenu),MenuItem);
+ g_signal_connect_swapped(G_OBJECT(MenuItem),"activate",
+ G_CALLBACK(Convert_Remove_Space),G_OBJECT(entry));
+
+ MenuItem = gtk_image_menu_item_new_with_label(_("Insert space before uppercase letter"));
+ Image = gtk_image_new_from_stock(GTK_STOCK_ADD,GTK_ICON_SIZE_MENU);
+ gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(MenuItem),Image);
+ gtk_menu_shell_append(GTK_MENU_SHELL(PopupMenu),MenuItem);
+ g_signal_connect_swapped(G_OBJECT(MenuItem),"activate",
+ G_CALLBACK(Convert_Insert_Space),G_OBJECT(entry));
+
+ MenuItem = gtk_image_menu_item_new_with_label(_("Remove duplicate spaces or underscores"));
+ Image = gtk_image_new_from_stock(GTK_STOCK_REMOVE,GTK_ICON_SIZE_MENU);
+ gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(MenuItem),Image);
+ gtk_menu_shell_append(GTK_MENU_SHELL(PopupMenu),MenuItem);
+ g_signal_connect_swapped(G_OBJECT(MenuItem),"activate",
+ G_CALLBACK(Convert_Only_One_Space),G_OBJECT(entry));
+
+ gtk_widget_show_all(PopupMenu);
+}
+
+
+
+/*
+ * Function to manage the received signals (specially for segfaults)
+ * Handle crashs
+ */
+void Handle_Crash (gint signal_id)
+{
+ //gchar commmand[256];
+
+ Log_Print(_("EasyTAG %s: Abnormal exit! (PId: %d)."),VERSION,getpid());
+ Log_Print(_("Received signal %s (%d)\a"),signal_to_string(signal_id),signal_id);
+
+ Log_Print(_("You have probably found a bug in EasyTAG. Please, send a bug "
+ "report with a gdb backtrace ('gdb easytag core' then 'bt' and "
+ "'l') and informations to reproduce it to easytag@gmail.com"));
+
+ // To send messages to the console...
+ g_print(_("EasyTAG %s: Abnormal exit! (PId: %d)."),VERSION,getpid());
+ g_print("\n");
+ g_print(_("Received signal %s (%d)\a"),signal_to_string(signal_id),signal_id);
+ g_print("\n");
+ g_print(_("You have probably found a bug in EasyTAG. Please, send a bug "
+ "report with a gdb backtrace ('gdb easytag core' then 'bt' and "
+ "'l') and informations to reproduce it to easytag@gmail.com"));
+ g_print("\n");
+
+ signal(signal_id,SIG_DFL); // Let the OS handle recursive seg faults
+ //signal(SIGTSTP, exit);
+ //snprintf(commmand,sizeof(commmand),"gdb -x /root/core.txt easytag %d", getpid());
+ //system(commmand);
+}
+
+gchar *signal_to_string (gint signal)
+{
+#ifdef SIGHUP
+ if (signal == SIGHUP) return ("SIGHUP");
+#endif
+#ifdef SIGINT
+ if (signal == SIGINT) return ("SIGINT");
+#endif
+#ifdef SIGQUIT
+ if (signal == SIGQUIT) return ("SIGQUIT");
+#endif
+#ifdef SIGILL
+ if (signal == SIGILL) return ("SIGILL");
+#endif
+#ifdef SIGTRAP
+ if (signal == SIGTRAP) return ("SIGTRAP");
+#endif
+#ifdef SIGABRT
+ if (signal == SIGABRT) return ("SIGABRT");
+#endif
+#ifdef SIGIOT
+ if (signal == SIGIOT) return ("SIGIOT");
+#endif
+#ifdef SIGEMT
+ if (signal == SIGEMT) return ("SIGEMT");
+#endif
+#ifdef SIGFPE
+ if (signal == SIGFPE) return ("SIGFPE");
+#endif
+#ifdef SIGKILL
+ if (signal == SIGKILL) return ("SIGKILL");
+#endif
+#ifdef SIGBUS
+ if (signal == SIGBUS) return ("SIGBUS");
+#endif
+#ifdef SIGSEGV
+ if (signal == SIGSEGV) return ("SIGSEGV");
+#endif
+#ifdef SIGSYS
+ if (signal == SIGSYS) return ("SIGSYS");
+#endif
+#ifdef SIGPIPE
+ if (signal == SIGPIPE) return ("SIGPIPE");
+#endif
+#ifdef SIGALRM
+ if (signal == SIGALRM) return ("SIGALRM");
+#endif
+#ifdef SIGTERM
+ if (signal == SIGTERM) return ("SIGTERM");
+#endif
+#ifdef SIGUSR1
+ if (signal == SIGUSR1) return ("SIGUSR1");
+#endif
+#ifdef SIGUSR2
+ if (signal == SIGUSR2) return ("SIGUSR2");
+#endif
+#ifdef SIGCHLD
+ if (signal == SIGCHLD) return ("SIGCHLD");
+#endif
+#ifdef SIGCLD
+ if (signal == SIGCLD) return ("SIGCLD");
+#endif
+#ifdef SIGPWR
+ if (signal == SIGPWR) return ("SIGPWR");
+#endif
+#ifdef SIGVTALRM
+ if (signal == SIGVTALRM) return ("SIGVTALRM");
+#endif
+#ifdef SIGPROF
+ if (signal == SIGPROF) return ("SIGPROF");
+#endif
+#ifdef SIGIO
+ if (signal == SIGIO) return ("SIGIO");
+#endif
+#ifdef SIGPOLL
+ if (signal == SIGPOLL) return ("SIGPOLL");
+#endif
+#ifdef SIGWINCH
+ if (signal == SIGWINCH) return ("SIGWINCH");
+#endif
+#ifdef SIGWINDOW
+ if (signal == SIGWINDOW) return ("SIGWINDOW");
+#endif
+#ifdef SIGSTOP
+ if (signal == SIGSTOP) return ("SIGSTOP");
+#endif
+#ifdef SIGTSTP
+ if (signal == SIGTSTP) return ("SIGTSTP");
+#endif
+#ifdef SIGCONT
+ if (signal == SIGCONT) return ("SIGCONT");
+#endif
+#ifdef SIGTTIN
+ if (signal == SIGTTIN) return ("SIGTTIN");
+#endif
+#ifdef SIGTTOU
+ if (signal == SIGTTOU) return ("SIGTTOU");
+#endif
+#ifdef SIGURG
+ if (signal == SIGURG) return ("SIGURG");
+#endif
+#ifdef SIGLOST
+ if (signal == SIGLOST) return ("SIGLOST");
+#endif
+#ifdef SIGRESERVE
+ if (signal == SIGRESERVE) return ("SIGRESERVE");
+#endif
+#ifdef SIGDIL
+ if (signal == SIGDIL) return ("SIGDIL");
+#endif
+#ifdef SIGXCPU
+ if (signal == SIGXCPU) return ("SIGXCPU");
+#endif
+#ifdef SIGXFSZ
+ if (signal == SIGXFSZ) return ("SIGXFSZ");
+#endif
+ return (_("Unknown signal"));
+}
+
+
+/*
+ * Display usage informations
+ */
+void Display_Usage (void)
+{
+ g_print(_("\nUsage: easytag [option] "
+ "\n or: easytag [directory]\n"
+ "\n"
+ "Option:\n"
+ "-------\n"
+ "-h, --help Display this text and exit.\n"
+ "-v, --version Print basic informations and exit.\n"
+ "\n"
+ "Directory:\n"
+ "----------\n"
+#ifdef WIN32
+ "c:/path_to/files Use an absolute path to load,\n"
+#else
+ "/path_to/files Use an absolute path to load,\n"
+#endif
+ "path_to/files Use a relative path.\n"
+ "\n"));
+ exit(0);
+}
+
+
+
+/*
+ * Exit the program
+ */
+void EasyTAG_Exit (void)
+{
+ ET_Core_Destroy();
+ Charset_Insert_Locales_Destroy();
+ Log_Print(_("EasyTAG: Normal exit."));
+ gtk_main_quit();
+#ifdef WIN32
+ ET_Win32_Cleanup();
+#endif
+ exit(0);
+}
+
+void Quit_MainWindow_Confirmed (void)
+{
+ // Save the configuration when exiting...
+ Save_Changes_Of_UI();
+
+ // Quit EasyTAG
+ EasyTAG_Exit();
+}
+
+void Quit_MainWindow_Save_And_Quit (void)
+{
+ /* Save modified tags */
+ if (Save_All_Files_With_Answer(FALSE) == -1)
+ return;
+ Quit_MainWindow_Confirmed();
+}
+
+void Quit_MainWindow (void)
+{
+ GtkWidget *msgbox;
+ gint button;
+
+ /* If you change the displayed data and quit immediately */
+ if (ETCore->ETFileList)
+ ET_Save_File_Data_From_UI(ETCore->ETFileDisplayed); // To detect change before exiting
+
+ /* Save combobox history list before exit */
+ Save_Path_Entry_List(BrowserEntryModel, MISC_COMBO_TEXT);
+
+ /* Exit ? */
+ if (CONFIRM_BEFORE_EXIT)
+ {
+ if (ET_Check_If_All_Files_Are_Saved() != TRUE)
+ {
+ /* Some files haven't been saved */
+ msgbox = msg_box_new(_("Confirm..."),_("Some files have been modified but not "
+ "saved...\nDo you want to save them before exiting the program?"),
+ GTK_STOCK_DIALOG_QUESTION,BUTTON_CANCEL,BUTTON_NO,BUTTON_YES,0);
+ msg_box_hide_check_button(MSG_BOX(msgbox));
+ button = msg_box_run(MSG_BOX(msgbox));
+ gtk_widget_destroy(msgbox);
+ switch(button)
+ {
+ case BUTTON_YES:
+ Quit_MainWindow_Save_And_Quit();
+ break;
+ case BUTTON_NO:
+ Quit_MainWindow_Confirmed();
+ break;
+ case BUTTON_CANCEL:
+ case -1:
+ return;
+ }
+ } else
+ {
+ msgbox = msg_box_new(_("Confirm..."),_(" Do you really want to exit the program? "),
+ GTK_STOCK_DIALOG_QUESTION,BUTTON_NO,BUTTON_YES,0);
+ msg_box_hide_check_button(MSG_BOX(msgbox));
+ button = msg_box_run(MSG_BOX(msgbox));
+ gtk_widget_destroy(msgbox);
+ switch(button)
+ {
+ case BUTTON_YES:
+ Quit_MainWindow_Confirmed();
+ break;
+ case BUTTON_NO:
+ case -1:
+ return;
+ break;
+ }
+ }
+ }else if (ET_Check_If_All_Files_Are_Saved() != TRUE)
+ {
+ /* Some files aren't saved */
+ msgbox = msg_box_new(_("Confirm..."),_("Some files have been modified but not "
+ "saved...\nDo you want to save them before exiting the program?"),
+ GTK_STOCK_DIALOG_QUESTION,BUTTON_CANCEL,BUTTON_NO,BUTTON_YES,0);
+ msg_box_hide_check_button(MSG_BOX(msgbox));
+ button = msg_box_run(MSG_BOX(msgbox));
+ gtk_widget_destroy(msgbox);
+ switch(button)
+ {
+ case BUTTON_YES:
+ Quit_MainWindow_Save_And_Quit();
+ break;
+ case BUTTON_NO:
+ Quit_MainWindow_Confirmed();
+ break;
+ case BUTTON_CANCEL:
+ case -1:
+ return;
+ }
+ }else
+ {
+ Quit_MainWindow_Confirmed();
+ }
+}
+
+/*
+ * For the configuration file...
+ */
+void MainWindow_Apply_Changes (void)
+{
+ if ( MainWindow && MainWindow->window && gdk_window_is_visible(MainWindow->window)
+ && gdk_window_get_state(MainWindow->window)!=GDK_WINDOW_STATE_MAXIMIZED )
+ {
+ gint x, y, width, height;
+
+ // Position and Origin of the window
+ gdk_window_get_root_origin(MainWindow->window,&x,&y);
+ MAIN_WINDOW_X = x;
+ MAIN_WINDOW_Y = y;
+ gdk_window_get_size(MainWindow->window,&width,&height);
+ MAIN_WINDOW_WIDTH = width;
+ MAIN_WINDOW_HEIGHT = height;
+
+ // Handle panes position
+ PANE_HANDLE_POSITION1 = GTK_PANED(MainWindowHPaned)->child1_size;
+ PANE_HANDLE_POSITION2 = GTK_PANED(BrowserHPaned)->child1_size;
+ PANE_HANDLE_POSITION3 = GTK_PANED(ArtistAlbumVPaned)->child1_size;
+ PANE_HANDLE_POSITION4 = GTK_PANED(MainWindowVPaned)->child1_size;
+ }
+
+}
diff --git a/src/easytag.h b/src/easytag.h
new file mode 100755
index 0000000..1b4fdd1
--- /dev/null
+++ b/src/easytag.h
@@ -0,0 +1,206 @@
+/* easytag.h - 2000/04/28 */
+/*
+ * EasyTAG - Tag editor for MP3 and Ogg Vorbis files
+ * Copyright (C) 2000-2003 Jerome Couderc <easytag@gmail.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef __EASYTAG_H__
+#define __EASYTAG_H__
+
+
+/* 'include' and 'define' created by autoconf/automake */
+#include "../config.h"
+
+#include "et_core.h"
+
+
+#define APPNAME "EasyTAG"
+#define AUTHOR "Jerome Couderc" // Note: do not use accents
+#define EMAIL "easytag@gmail.com"
+#define WEBPAGE "http://easytag.sourceforge.net"
+/*#define VERSION "0.15.1" //Now version is defined in ../configure.h.in */
+
+#define MAX_STRING_LEN 1024
+
+#if defined(__GNUC__) && (__GNUC__ > 2 || __GNUC__ == 2 && __GNUC_MINOR__ >= 7)
+# define ATTRIBUTE_UNUSED __attribute__((__unused__))
+#else
+# define ATTRIBUTE_UNUSED
+#endif
+
+
+/***************
+ * Declaration *
+ ***************/
+
+/* Variable to force to quit recursive functions (reading dirs) or stop saving files */
+gboolean Main_Stop_Button_Pressed;
+
+GtkWidget *MainWindow;
+GtkWidget *MenuArea;
+GtkWidget *ToolArea;
+GtkWidget *BrowseArea;
+GtkWidget *FileArea;
+GtkWidget *TagArea;
+GtkWidget *StatusArea;
+GtkWidget *ProgressArea;
+GtkWidget *LogArea;
+GtkWidget *ReadOnlyStatusIconBox;
+GtkWidget *BrokenStatusIconBox;
+GtkWidget *MainWindowHPaned;
+GtkWidget *MainWindowVPaned;
+
+/* File Area */
+GtkWidget *FileFrame;
+GtkWidget *FileIndex;
+GtkWidget *FileEntry;
+GtkWidget *HeaderInfosTable; /* declarated here to show/hide file header infos */
+GtkWidget *VersionLabel;
+GtkWidget *VersionValueLabel;
+GtkWidget *BitrateLabel;
+GtkWidget *BitrateValueLabel;
+GtkWidget *SampleRateLabel;
+GtkWidget *SampleRateValueLabel;
+GtkWidget *ModeLabel;
+GtkWidget *ModeValueLabel;
+GtkWidget *SizeLabel;
+GtkWidget *SizeValueLabel;
+GtkWidget *DurationLabel;
+GtkWidget *DurationValueLabel;
+
+/* TAG Area */
+GtkWidget *TagFrame;
+GtkWidget *TagNoteBook;
+GtkWidget *TitleEntry;
+GtkWidget *ArtistEntry;
+GtkWidget *AlbumEntry;
+GtkWidget *DiscNumberEntry;
+GtkWidget *YearEntry;
+GtkWidget *TrackEntryCombo;
+GtkListStore *TrackEntryComboModel;
+GtkWidget *TrackTotalEntry;
+GtkWidget *GenreCombo;
+GtkListStore *GenreComboModel;
+GtkWidget *CommentEntry;
+//GtkWidget *CommentView;
+GtkWidget *ComposerEntry;
+GtkWidget *OrigArtistEntry;
+GtkWidget *CopyrightEntry;
+GtkWidget *URLEntry;
+GtkWidget *EncodedByEntry;
+GtkWidget *PictureEntryView;
+GtkListStore *PictureEntryModel;
+// Labels
+GtkWidget *TitleLabel;
+GtkWidget *ArtistLabel;
+GtkWidget *AlbumLabel;
+GtkWidget *DiscNumberLabel;
+GtkWidget *YearLabel;
+GtkWidget *TrackLabel;
+GtkWidget *GenreLabel;
+GtkWidget *CommentLabel;
+GtkWidget *ComposerLabel;
+GtkWidget *OrigArtistLabel;
+GtkWidget *CopyrightLabel;
+GtkWidget *URLLabel;
+GtkWidget *EncodedByLabel;
+GtkWidget *PictureLabel;
+// Mini buttons
+GtkWidget *TitleMButton;
+GtkWidget *ArtistMButton;
+GtkWidget *AlbumMButton;
+GtkWidget *DiscNumberMButton;
+GtkWidget *YearMButton;
+GtkWidget *TrackMButton;
+GtkWidget *TrackMButtonSequence;
+GtkWidget *TrackMButtonNbrFiles;
+GtkWidget *GenreMButton;
+GtkWidget *CommentMButton;
+GtkWidget *ComposerMButton;
+GtkWidget *OrigArtistMButton;
+GtkWidget *CopyrightMButton;
+GtkWidget *URLMButton;
+GtkWidget *EncodedByMButton;
+GtkWidget *PictureMButton;
+
+// Other for picture
+GtkWidget *PictureClearButton;
+GtkWidget *PictureAddButton;
+GtkWidget *PictureSaveButton;
+GtkWidget *PicturePropertiesButton;
+GtkWidget *PictureScrollWindow;
+
+GdkCursor *MouseCursor;
+
+gchar *HOME_VARIABLE;
+gchar *INIT_DIRECTORY;
+
+#ifndef errno
+extern int errno;
+#endif
+
+/* A flag to start/avoid a new reading while an other one is running */
+gboolean ReadingDirectory;
+
+
+/**************
+ * Prototypes *
+ **************/
+void Action_Select_All_Files (void);
+void Action_Unselect_All_Files (void);
+void Action_Invert_Files_Selection (void);
+void Action_Select_Prev_File (void);
+void Action_Select_Next_File (void);
+void Action_Select_First_File (void);
+void Action_Select_Last_File (void);
+void Action_Select_Nth_File_By_Position (gulong num_item);
+void Action_Select_Nth_File_By_Etfile (ET_File *ETFile);
+
+void Action_Scan_Selected_Files (void);
+void Action_Remove_Selected_Tags (void);
+gint Action_Undo_Selected_Files (void);
+gint Action_Redo_Selected_File (void);
+void Action_Undo_All_Files (void);
+void Action_Redo_All_Files (void);
+void Action_Save_Selected_Files (void);
+void Action_Force_Saving_Selected_Files (void);
+void Action_Undo_From_History_List (void);
+void Action_Redo_From_History_List (void);
+void Action_Delete_Selected_Files (void);
+gint Save_All_Files_With_Answer (gboolean force_saving_files);
+gint Save_Selected_Files_With_Answer (gboolean force_saving_files);
+
+void Action_Main_Stop_Button_Pressed (void);
+void Action_Select_Browser_Style (void);
+
+void File_Area_Set_Sensitive (gboolean activate);
+void Tag_Area_Set_Sensitive (gboolean activate);
+void Tag_Area_Display_Controls (ET_File *ETFile);
+
+void Read_Directory (gchar *path);
+void Quit_MainWindow (void);
+void MainWindow_Apply_Changes (void);
+void Update_Command_Buttons_Sensivity (void);
+void Attach_Popup_Menu_To_Tag_Entries (GtkEntry *entry);
+
+void Clear_File_Entry_Field (void);
+void Clear_Tag_Entry_Fields (void);
+void Clear_Header_Fields (void);
+
+
+#endif /* __EASYTAG_H__ */
diff --git a/src/et_core.c b/src/et_core.c
new file mode 100755
index 0000000..3e608f3
--- /dev/null
+++ b/src/et_core.c
@@ -0,0 +1,4835 @@
+/* et_core.c - 2001/10/21 */
+/*
+ * EasyTAG - Tag editor for MP3 and Ogg Vorbis files
+ * Copyright (C) 2000-2003 Jerome Couderc <easytag@gmail.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+
+#include <gtk/gtk.h>
+#include <glib/gi18n-lib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <utime.h>
+#include <ctype.h>
+#include "errno.h"
+
+#include "easytag.h"
+#include "et_core.h"
+#include "mpeg_header.h"
+#include "monkeyaudio_header.h"
+#include "musepack_header.h"
+#ifdef ENABLE_MP3
+# include "id3_tag.h"
+#endif
+#include "picture.h"
+#include "ape_tag.h"
+#ifdef ENABLE_OGG
+# include "ogg_header.h"
+# include "ogg_tag.h"
+#endif
+#ifdef ENABLE_FLAC
+# include "flac_header.h"
+# include "flac_tag.h"
+#endif
+#ifdef ENABLE_MP4
+# include "mp4_header.h"
+# include "mp4_tag.h"
+#endif
+#ifdef ENABLE_WAVPACK
+# include "wavpack_header.h"
+# include "wavpack_tag.h"
+#endif
+#include "bar.h"
+#include "browser.h"
+#include "log.h"
+#include "misc.h"
+#include "setting.h"
+#include "msgbox.h"
+#include "charset.h"
+
+#ifdef WIN32
+# include "win32/win32dep.h"
+#endif
+
+
+/***************
+ * Declaration *
+ ***************/
+
+ET_Core *ETCore = NULL;
+
+/*
+ * Colors Used
+ */
+GdkColor LIGHT_BLUE = {0, 0xddd1, 0xeeec, 0xffff};
+GdkColor RED = {0, 0xffff, 0x0000, 0x0000};
+GdkColor LIGHT_RED = {0, 0xffff, 0x8888, 0x8888};
+GdkColor GREY = {0, 0xaa00, 0xaa00, 0xaa00};
+GdkColor LIGHT_GREY = {0, 0xd2d2, 0xd2d2, 0xd2d2};
+GdkColor YELLOW = {0, 0xffff, 0xffff, 0xcccc};
+GdkColor BLACK = {0, 0x0000, 0x0000, 0x0000};
+
+
+/**************
+ * Prototypes *
+ **************/
+
+void ET_Core_Create (void);
+void ET_Core_Initialize (void);
+void ET_Core_Free (void);
+void ET_Core_Destroy (void);
+
+//gboolean ET_File_Is_Supported (gchar *filename);
+gchar *ET_Get_File_Extension (gchar *filename);
+ET_File_Description *ET_Get_File_Description (gchar *filename);
+ET_File_Description *ET_Get_File_Description_From_Extension (gchar *extension);
+
+gboolean ET_Free_File_List (void);
+gboolean ET_Free_File_List_Item (ET_File *ETFile);
+gboolean ET_Free_File_Name_List (GList *FileNameList);
+gboolean ET_Free_File_Tag_List (GList *FileTagList);
+gboolean ET_Free_File_Name_Item (File_Name *FileName);
+gboolean ET_Free_File_Tag_Item (File_Tag *FileTag);
+gboolean ET_Free_File_Tag_Item_Other_Field (File_Tag *FileTag);
+gboolean ET_Free_File_Info_Item (ET_File_Info *ETFileInfo);
+gboolean ET_Free_History_File_List (void);
+gboolean ET_Free_Displayed_File_List (void);
+gboolean ET_Free_Artist_Album_File_List (void);
+
+void ET_Initialize_File_Item (ET_File *ETFile);
+void ET_Initialize_File_Tag_Item (File_Tag *FileTag);
+void ET_Initialize_File_Name_Item (File_Name *FileName);
+void ET_Initialize_File_Info_Item (ET_File_Info *ETFileInfo);
+
+//gboolean ET_Copy_File_Tag_Item (ET_File *ETFile, File_Tag *FileTag);
+gboolean ET_Copy_File_Tag_Item_Other_Field (ET_File *ETFile, File_Tag *FileTag);
+//gboolean ET_Set_Field_File_Name_Item (gint *FileNameField, gchar *value);
+//gboolean ET_Set_Field_File_Name_Item (gchar **FileNameField, gchar *value);
+//gboolean ET_Set_Field_File_Tag_Item (gint *FileTagField, gchar *value);
+//gboolean ET_Set_Field_File_Tag_Picture (gchar **FileTagField, Picture *pic);
+
+guint ET_File_Key_New (void);
+guint ET_Undo_Key_New (void);
+
+GList *ET_File_List_Remove (GList *item_to_remove);
+
+gboolean ET_Remove_File_From_File_List (ET_File *ETFile);
+gboolean ET_Remove_File_From_Artist_Album_List (ET_File *ETFile);
+
+void ET_Display_File_And_List_Status_To_UI (ET_File *ETFile);
+void ET_Display_Filename_To_UI (ET_File *ETFile);
+gboolean ET_Display_File_Tag_To_UI (ET_File *ETFile);
+gboolean ET_Display_File_Info_To_UI (ET_File_Info *ETFileInfo);
+
+gboolean ET_Save_File_Name_From_UI (ET_File *ETFile, File_Name *FileName);
+gboolean ET_Save_File_Name_Internal (ET_File *ETFile, File_Name *FileName);
+gboolean ET_Save_File_Tag_From_UI (File_Tag *FileTag);
+gboolean ET_Save_File_Tag_Internal (ET_File *ETFile, File_Tag *FileTag);
+
+void ET_Mark_File_Tag_As_Saved (ET_File *ETFile);
+void ET_Mark_File_Name_As_Saved (ET_File *ETFile);
+
+gboolean ET_Manage_Changes_Of_File_Data (ET_File *ETFile, File_Name *FileName, File_Tag *FileTag);
+gboolean ET_Detect_Changes_Of_File_Name (File_Name *FileName1, File_Name *FileName2);
+gboolean ET_Detect_Changes_Of_File_Tag (File_Tag *FileTag1, File_Tag *FileTag2);
+gboolean ET_Add_File_Name_To_List (ET_File *ETFile, File_Name *FileName);
+gboolean ET_Add_File_Tag_To_List (ET_File *ETFile, File_Tag *FileTag);
+gboolean ET_Add_File_To_History_List (ET_File *ETFile);
+gboolean ET_Add_File_To_Artist_Album_File_List (ET_File *ETFile);
+
+GList *ET_Displayed_File_List_First (void);
+GList *ET_Displayed_File_List_Previous (void);
+GList *ET_Displayed_File_List_Next (void);
+GList *ET_Displayed_File_List_Last (void);
+GList *ET_Displayed_File_List_By_Etfile (ET_File *ETFile);
+GList *ET_Displayed_File_List_By_Position (gulong pos_in_list);
+guint ET_Displayed_File_List_Get_Length (void);
+void ET_Displayed_File_List_Number (void);
+
+gboolean ET_Set_Displayed_File_List (GList *ETFileList);
+
+gboolean ET_Read_File_Info (gchar *filename, ET_File_Info *ETFileInfo);
+//gboolean ET_File_Name_Convert_Character (gchar *filename_utf8);
+void ET_File_Name_Check_Length (ET_File *ETFile, gchar *filename_utf8);
+
+gint ET_Comp_Func_Sort_File_By_Ascending_Index_Key (ET_File *ETFile1, ET_File *ETFile2);
+
+gint ET_Comp_Func_Sort_Artist_Item_By_Ascending_Artist (GList *AlbumList1, GList *AlbumList2);
+gint ET_Comp_Func_Sort_Album_Item_By_Ascending_Album (GList *etfilelist1, GList *etfilelist2);
+gint ET_Comp_Func_Sort_Etfile_Item_By_Ascending_Filename (ET_File *ETFile1, ET_File *ETFile2);
+
+
+/*******************
+ * Basic functions *
+ *******************/
+
+/*
+ * Returns the extension of the file
+ */
+gchar *ET_Get_File_Extension (gchar *filename)
+{
+ if (filename)
+ return strrchr(filename, '.');
+ else
+ return NULL;
+}
+
+
+/*
+ * Determine description of file using his extension.
+ * If extension is NULL or not found into the tab, it returns the last entry for UNKNOWN_FILE.
+ */
+ET_File_Description *ET_Get_File_Description_From_Extension (gchar *extension)
+{
+ guint i;
+
+ if (!extension) // Unknown file
+ return (ET_File_Description*) &ETFileDescription[ET_FILE_DESCRIPTION_SIZE];
+
+ for (i=0; i<ET_FILE_DESCRIPTION_SIZE; i++) // Use of '<' instead of '<=' to avoid to test for Unknown file
+ if ( strcasecmp(extension,ETFileDescription[i].Extension)==0 )
+ return (ET_File_Description*) &ETFileDescription[i];
+
+ // If not found in the list
+ return (ET_File_Description*) &ETFileDescription[ET_FILE_DESCRIPTION_SIZE];
+}
+
+
+/*
+ * Determines description of file.
+ * Determines first the extension. If extension is NULL or not found into the tab,
+ * it returns the last entry for UNKNOWN_FILE.
+ */
+ET_File_Description *ET_Get_File_Description (gchar *filename)
+{
+ return ET_Get_File_Description_From_Extension(ET_Get_File_Extension(filename));
+}
+
+
+/*
+ * Returns TRUE if the file is supported, else returns FALSE
+ */
+gboolean ET_File_Is_Supported (gchar *filename)
+{
+ if (ET_Get_File_Description(filename)->FileType != UNKNOWN_FILE)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+
+
+
+/*****************************************************************************
+ * Manipulation of ET_Core functions (main functions needed for the program) *
+ *****************************************************************************/
+void ET_Core_Create (void)
+{
+ // Allocate
+ if (ETCore == NULL)
+ ETCore = g_malloc0(sizeof(ET_Core));
+
+ // Initialize
+ ET_Core_Initialize();
+}
+
+void ET_Core_Initialize (void)
+{
+ ETCore->ETFileList = NULL;
+ ETCore->ETFileDisplayedList = NULL;
+ ETCore->ETFileDisplayedListPtr = NULL;
+ ETCore->ETFileDisplayedList_Length = 0;
+ ETCore->ETFileDisplayedList_TotalSize = 0;
+ ETCore->ETFileDisplayedList_TotalDuration = 0;
+ ETCore->ETFileDisplayed = NULL;
+ ETCore->ETHistoryFileList = NULL;
+ ETCore->ETArtistAlbumFileList = NULL;
+}
+
+void ET_Core_Free (void)
+{
+ // Frees first lists, then initialize
+ if (ETCore->ETFileList)
+ ET_Free_File_List();
+
+ if (ETCore->ETFileDisplayedList)
+ ET_Free_Displayed_File_List();
+
+ if (ETCore->ETHistoryFileList)
+ ET_Free_History_File_List();
+
+ if (ETCore->ETArtistAlbumFileList)
+ ET_Free_Artist_Album_File_List();
+
+ // Initialize by security
+ ET_Core_Initialize();
+}
+
+void ET_Core_Destroy (void)
+{
+ // Free attached data
+ ET_Core_Free();
+
+ // Unallocate
+ g_free(ETCore);
+ ETCore = NULL;
+}
+
+
+
+/**************************
+ * Initializing functions *
+ **************************/
+
+/*
+ * Create a new File_Name structure
+ */
+File_Name *ET_File_Name_Item_New (void)
+{
+ File_Name *FileName;
+
+ FileName = g_malloc0(sizeof(File_Name));
+ ET_Initialize_File_Name_Item(FileName);
+
+ return FileName;
+}
+
+
+/*
+ * Create a new File_Tag structure
+ */
+File_Tag *ET_File_Tag_Item_New (void)
+{
+ File_Tag *FileTag;
+
+ FileTag = g_malloc0(sizeof(File_Tag));
+ ET_Initialize_File_Tag_Item(FileTag);
+
+ return FileTag;
+}
+
+
+/*
+ * Create a new File_Info structure
+ */
+ET_File_Info *ET_File_Info_Item_New (void)
+{
+ ET_File_Info *ETFileInfo;
+
+ ETFileInfo = g_malloc0(sizeof(ET_File_Info));
+ ET_Initialize_File_Info_Item(ETFileInfo);
+
+ return ETFileInfo;
+}
+
+
+/*
+ * Create a new ET_File structure
+ */
+ET_File *ET_File_Item_New (void)
+{
+ ET_File *ETFile;
+
+ ETFile = g_malloc0(sizeof(ET_File));
+ ET_Initialize_File_Item(ETFile);
+
+ return ETFile;
+}
+
+
+void ET_Initialize_File_Name_Item (File_Name *FileName)
+{
+ if (FileName)
+ {
+ FileName->key = ET_Undo_Key_New();
+ FileName->saved = FALSE;
+ FileName->value = NULL;
+ FileName->value_utf8 = NULL;
+ FileName->value_ck = NULL;
+ }
+}
+
+
+void ET_Initialize_File_Tag_Item (File_Tag *FileTag)
+{
+ if (FileTag)
+ {
+ FileTag->key = ET_Undo_Key_New();
+ FileTag->saved = FALSE;
+ FileTag->title = NULL;
+ FileTag->artist = NULL;
+ FileTag->album = NULL;
+ FileTag->disc_number = NULL;
+ FileTag->track = NULL;
+ FileTag->track_total = NULL;
+ FileTag->year = NULL;
+ FileTag->genre = NULL;
+ FileTag->comment = NULL;
+ FileTag->composer = NULL;
+ FileTag->orig_artist = NULL;
+ FileTag->copyright = NULL;
+ FileTag->url = NULL;
+ FileTag->encoded_by = NULL;
+ FileTag->picture = NULL;
+ FileTag->other = NULL;
+ }
+}
+
+
+void ET_Initialize_File_Info_Item (ET_File_Info *ETFileInfo)
+{
+ if (ETFileInfo)
+ {
+ ETFileInfo->mpc_profile = NULL;
+ ETFileInfo->mpc_version = NULL;
+ }
+}
+
+
+void ET_Initialize_File_Item (ET_File *ETFile)
+{
+ if (ETFile)
+ {
+ ETFile->IndexKey = 0;
+ ETFile->ETFileKey = 0;
+ ETFile->ETFileDescription = NULL;
+ ETFile->ETFileInfo = NULL;
+ ETFile->FileNameCur = NULL;
+ ETFile->FileNameNew = NULL;
+ ETFile->FileNameList = NULL;
+ ETFile->FileNameListBak = NULL;
+ ETFile->FileTag = NULL;
+ ETFile->FileTagList = NULL;
+ ETFile->FileTagListBak = NULL;
+ }
+}
+
+
+/* Key for each item of ETFileList */
+guint ET_File_Key_New (void)
+{
+ static guint ETFileKey = 0;
+ return ++ETFileKey;
+}
+
+/* Key for Undo */
+guint ET_Undo_Key_New (void)
+{
+ static guint ETUndoKey = 0;
+ return ++ETUndoKey;
+}
+
+
+
+
+/**********************************
+ * File adding/removing functions *
+ **********************************/
+
+/*
+ * ET_Add_File_To_File_List: Add a file to the "main" list. And get all informations of the file.
+ * The filename passed in should be in raw format, only convert it to UTF8 when displaying it.
+ */
+GList *ET_Add_File_To_File_List (gchar *filename)
+{
+ ET_File_Description *ETFileDescription;
+ ET_File *ETFile;
+ File_Name *FileName;
+ File_Tag *FileTag;
+ ET_File_Info *ETFileInfo;
+ gchar *ETFileExtension;
+ guint ETFileKey;
+ guint undo_key;
+ gchar *filename_utf8 = filename_to_display(filename);
+
+ if (!filename)
+ return ETCore->ETFileList;
+
+ /* Primary Key for this file */
+ ETFileKey = ET_File_Key_New();
+
+ /* Get description of the file */
+ ETFileDescription = ET_Get_File_Description(filename);
+
+ /* Get real extension of the file (keeping the case) */
+ ETFileExtension = g_strdup(ET_Get_File_Extension(filename));
+
+ /* Fill the File_Name structure for FileNameList */
+ FileName = ET_File_Name_Item_New();
+ FileName->saved = TRUE; /* The file hasn't been changed, so it's saved */
+ FileName->value = filename;
+ FileName->value_utf8 = filename_utf8;
+ FileName->value_ck = g_utf8_collate_key_for_filename(filename_utf8, -1);
+
+ /* Fill the File_Tag structure for FileTagList */
+ FileTag = ET_File_Tag_Item_New();
+ FileTag->saved = TRUE; /* The file hasn't been changed, so it's saved */
+
+ switch (ETFileDescription->TagType)
+ {
+#ifdef ENABLE_MP3
+ case ID3_TAG:
+ Id3tag_Read_File_Tag(filename,FileTag);
+ break;
+#endif
+#ifdef ENABLE_OGG
+ case OGG_TAG:
+ Ogg_Tag_Read_File_Tag(filename,FileTag);
+ break;
+#endif
+#ifdef ENABLE_FLAC
+ case FLAC_TAG:
+ Flac_Tag_Read_File_Tag(filename,FileTag);
+ break;
+#endif
+ case APE_TAG:
+ Ape_Tag_Read_File_Tag(filename,FileTag);
+ break;
+#ifdef ENABLE_MP4
+ case MP4_TAG:
+ Mp4tag_Read_File_Tag(filename,FileTag);
+ break;
+#endif
+#ifdef ENABLE_WAVPACK
+ case WAVPACK_TAG:
+ Wavpack_Tag_Read_File_Tag(filename, FileTag);
+ break;
+#endif
+ case UNKNOWN_TAG:
+ default:
+ Log_Print("FileTag: Undefined tag type (%d) for file %s",ETFileDescription->TagType,filename_utf8);
+ break;
+ }
+
+ /* Fill the ET_File_Info structure */
+ ETFileInfo = ET_File_Info_Item_New ();
+
+ switch (ETFileDescription->FileType)
+ {
+#ifdef ENABLE_MP3
+ case MP3_FILE:
+ case MP2_FILE:
+ Mpeg_Header_Read_File_Info(filename,ETFileInfo);
+ break;
+#endif
+#ifdef ENABLE_OGG
+ case OGG_FILE:
+ Ogg_Header_Read_File_Info(filename,ETFileInfo);
+ break;
+#endif
+#ifdef ENABLE_SPEEX
+ case SPEEX_FILE:
+ Speex_Header_Read_File_Info(filename,ETFileInfo);
+ break;
+#endif
+#ifdef ENABLE_FLAC
+ case FLAC_FILE:
+ Flac_Header_Read_File_Info(filename,ETFileInfo);
+ break;
+#endif
+ case MPC_FILE:
+ Mpc_Header_Read_File_Info(filename,ETFileInfo);
+ break;
+ case MAC_FILE:
+ Mac_Header_Read_File_Info(filename,ETFileInfo);
+ break;
+#ifdef ENABLE_WAVPACK
+ case WAVPACK_FILE:
+ Wavpack_Header_Read_File_Info(filename, ETFileInfo);
+ break;
+#endif
+#ifdef ENABLE_MP4
+ case MP4_FILE:
+ Mp4_Header_Read_File_Info(filename,ETFileInfo);
+ break;
+#endif
+ case UNKNOWN_FILE:
+ default:
+ Log_Print("ETFileInfo: Undefined file type (%d) for file %s",ETFileDescription->FileType,filename_utf8);
+ ET_Read_File_Info(filename,ETFileInfo); // To get at least the file size
+ break;
+ }
+
+ /* Attach all data defined above to this ETFile item */
+ ETFile = ET_File_Item_New();
+ ETFile->IndexKey = 0; // Will be renumered after...
+ ETFile->ETFileKey = ETFileKey;
+ ETFile->ETFileDescription = ETFileDescription;
+ ETFile->ETFileExtension = ETFileExtension;
+ ETFile->FileNameList = g_list_append(NULL,FileName);
+ ETFile->FileNameCur = ETFile->FileNameList;
+ ETFile->FileNameNew = ETFile->FileNameList;
+ ETFile->FileTagList = g_list_append(NULL,FileTag);
+ ETFile->FileTag = ETFile->FileTagList;
+ ETFile->ETFileInfo = ETFileInfo;
+
+ /* Add the item to the "main list" */
+ ETCore->ETFileList = g_list_append(ETCore->ETFileList,ETFile);
+
+
+ /*
+ * Process the filename and tag to generate undo if needed...
+ * The undo key must be the same for FileName and FileTag => changed in the same time
+ */
+ undo_key = ET_Undo_Key_New();
+
+ FileName = ET_File_Name_Item_New();
+ FileName->key = undo_key;
+ ET_Save_File_Name_Internal(ETFile,FileName);
+
+ FileTag = ET_File_Tag_Item_New();
+ FileTag->key = undo_key;
+ ET_Save_File_Tag_Internal(ETFile,FileTag);
+
+ /*
+ * Generate undo for the file and the main undo list.
+ * If no changes detected, FileName and FileTag item are deleted.
+ */
+ ET_Manage_Changes_Of_File_Data(ETFile,FileName,FileTag);
+
+ /* Add the item to the ArtistAlbum list (placed here to take advantage of previous changes) */
+ //ET_Add_File_To_Artist_Album_File_List(ETFile);
+
+ //ET_Debug_Print_File_List(ETCore->ETFileList,__FILE__,__LINE__,__FUNCTION__);
+ return ETCore->ETFileList;
+}
+
+gboolean ET_Create_Artist_Album_File_List (void)
+{
+ GList *ETFileList;
+
+ if (ETCore->ETArtistAlbumFileList)
+ ET_Free_Artist_Album_File_List();
+
+ ETFileList = g_list_first(ETCore->ETFileList);
+ while (ETFileList)
+ {
+ ET_File *ETFile = (ET_File *)ETFileList->data;
+ ET_Add_File_To_Artist_Album_File_List(ETFile);
+ ETFileList = ETFileList->next;
+ }
+ //ET_Debug_Print_Artist_Album_List(__FILE__,__LINE__,__FUNCTION__);
+ return TRUE;
+}
+/*
+ * The ETArtistAlbumFileList contains 3 levels of lists to sort the ETFile by artist then by album :
+ * - "ETArtistAlbumFileList" list is a list of "ArtistList" items,
+ * - "ArtistList" list is a list of "AlbumList" items,
+ * - "AlbumList" list is a list of ETFile items.
+ * Note : use the function ET_Debug_Print_Artist_Album_List(...) to understand how it works, it needed...
+ */
+gboolean ET_Add_File_To_Artist_Album_File_List (ET_File *ETFile)
+{
+ if (ETFile)
+ {
+ gchar *ETFile_Artist = ((File_Tag *)ETFile->FileTag->data)->artist; // Artist value of the ETFile passed in parameter
+ gchar *ETFile_Album = ((File_Tag *)ETFile->FileTag->data)->album; // Album value of the ETFile passed in parameter
+ gchar *etfile_artist = NULL;
+ gchar *etfile_album = NULL;
+ GList *ArtistList = NULL;
+ GList *AlbumList = NULL;
+ GList *etfilelist = NULL;
+ ET_File *etfile = NULL;
+
+
+ ArtistList = ETCore->ETArtistAlbumFileList;
+ while (ArtistList)
+ {
+ AlbumList = (GList *)ArtistList->data; /* Take the first item */
+ if (AlbumList
+ && (etfilelist = (GList *)AlbumList->data) /* Take the first item */
+ && (etfile = (ET_File *)etfilelist->data) /* Take the first etfile item */
+ && ((File_Tag *)etfile->FileTag->data) != NULL )
+ {
+ etfile_artist = ((File_Tag *)etfile->FileTag->data)->artist;
+ }else
+ {
+ etfile_artist = NULL;
+ }
+
+ if ( (etfile_artist && ETFile_Artist && strcmp(etfile_artist,ETFile_Artist)==0)
+ || (!etfile_artist && !ETFile_Artist) ) // The "artist" values correspond?
+ {
+ // The "ArtistList" item was found!
+ while (AlbumList)
+ {
+ if ( (etfilelist = (GList *)AlbumList->data)
+ && (etfile = (ET_File *)etfilelist->data)
+ && ((File_Tag *)etfile->FileTag->data) != NULL )
+ {
+ etfile_album = ((File_Tag *)etfile->FileTag->data)->album;
+ }else
+ {
+ etfile_album = NULL;
+ }
+
+ if ( (etfile_album && ETFile_Album && strcmp(etfile_album,ETFile_Album)==0)
+ || (!etfile_album && !ETFile_Album) ) // The "album" values correspond?
+ {
+ // The "AlbumList" item was found!
+ // Add the ETFile to this AlbumList item
+ //g_print(">>> add to etfile list (%s)\n",g_path_get_basename(((File_Name *)ETFile->FileNameCur->data)->value));
+ AlbumList->data = (gpointer) g_list_append((GList *)AlbumList->data,ETFile);
+ AlbumList->data = (gpointer) g_list_sort((GList *)AlbumList->data,(GCompareFunc)ET_Comp_Func_Sort_Etfile_Item_By_Ascending_Filename);
+ return TRUE;
+ }
+ AlbumList = AlbumList->next;
+ }
+ // The "AlbumList" item was NOT found! => Add a new "AlbumList" item (+...) item to the "ArtistList" list
+ etfilelist = g_list_append(NULL,ETFile);
+ //g_print(">>> add new album (%s)\n",g_path_get_basename(((File_Name *)ETFile->FileNameCur->data)->value));
+ ArtistList->data = (gpointer) g_list_append((GList *)ArtistList->data,etfilelist);
+ ArtistList->data = (gpointer) g_list_sort((GList *)ArtistList->data,(GCompareFunc)ET_Comp_Func_Sort_Album_Item_By_Ascending_Album);
+ return TRUE;
+ }
+ ArtistList = ArtistList->next;
+ }
+ // The "ArtistList" item was NOT found! => Add a new "ArtistList" to the main list (=ETArtistAlbumFileList)
+ etfilelist = g_list_append(NULL,ETFile);
+ AlbumList = g_list_append(NULL,etfilelist);
+ //g_print(">>> add new artist (%s)(etfile:%x AlbumList:%x)\n",g_path_get_basename(((File_Name *)ETFile->FileNameCur->data)->value),etfilelist,AlbumList);
+ ETCore->ETArtistAlbumFileList = g_list_append(ETCore->ETArtistAlbumFileList,AlbumList);
+ // Sort the list by ascending Artist
+ ETCore->ETArtistAlbumFileList = g_list_sort(ETCore->ETArtistAlbumFileList,(GCompareFunc)ET_Comp_Func_Sort_Artist_Item_By_Ascending_Artist);
+
+ return TRUE;
+ }else
+ {
+ return FALSE;
+ }
+}
+
+
+
+/*
+ * Delete the corresponding file and free the allocated data. Return TRUE if deleted.
+ */
+gboolean ET_Remove_File_From_File_List (ET_File *ETFile)
+{
+ GList *ETFileList = NULL; // Item containing the ETFile to delete... (in ETCore->ETFileList)
+ GList *ETFileDisplayedList = NULL; // Item containing the ETFile to delete... (in ETCore->ETFileDisplayedList)
+
+ // Remove infos of the file
+ ETCore->ETFileDisplayedList_TotalSize -= ((ET_File_Info *)ETFile->ETFileInfo)->size;
+ ETCore->ETFileDisplayedList_TotalDuration -= ((ET_File_Info *)ETFile->ETFileInfo)->duration;
+
+ // Find the ETFileList containing the ETFile item
+ ETFileDisplayedList = g_list_find(g_list_first(ETCore->ETFileDisplayedList),ETFile);
+ ETFileList = g_list_find(g_list_first(ETCore->ETFileList),ETFile);
+
+ // Note : this ETFileList must be used only for ETCore->ETFileDisplayedList, and not ETCore->ETFileDisplayed
+ if (ETCore->ETFileDisplayedList == ETFileDisplayedList)
+ {
+ if (ETFileList->next)
+ ETCore->ETFileDisplayedList = ETFileDisplayedList->next;
+ else if (ETFileList->prev)
+ ETCore->ETFileDisplayedList = ETFileDisplayedList->prev;
+ else
+ ETCore->ETFileDisplayedList = NULL;
+ }
+ // If the current displayed file is just removing, it will be unable to display it again!
+ if (ETCore->ETFileDisplayed == ETFile)
+ {
+ if (ETCore->ETFileDisplayedList)
+ ETCore->ETFileDisplayed = (ET_File *)ETCore->ETFileDisplayedList->data;
+ else
+ ETCore->ETFileDisplayed = (ET_File *)NULL;
+ }
+
+ // Remove the file from the ETFileList list
+ ETCore->ETFileList = g_list_first(g_list_remove_link(g_list_first(ETCore->ETFileList),ETFileList));
+
+ // Remove the file from the ETArtistAlbumList list
+ ET_Remove_File_From_Artist_Album_List(ETFile);
+ //ET_Debug_Print_Artist_Album_List(__FILE__,__LINE__,__FUNCTION__);
+
+ // Remove the file from the ETFileDisplayedList list (if not already done)
+ if ( (ETFileDisplayedList = g_list_find(ETCore->ETFileDisplayedList,ETFile)) )
+ {
+ ETCore->ETFileDisplayedList = g_list_first(g_list_remove_link(g_list_first(ETCore->ETFileDisplayedList),ETFileDisplayedList));
+ }
+
+ // Free data of the file
+ ET_Free_File_List_Item(ETFile);
+ if (ETFileList)
+ g_list_free(ETFileList);
+ if (ETFileDisplayedList)
+ g_list_free(ETFileDisplayedList);
+
+ // Recalculate length of ETFileDisplayedList list
+ ET_Displayed_File_List_Get_Length();
+
+ // To number the ETFile in the list
+ ET_Displayed_File_List_Number();
+
+ // Displaying...
+ if (ETCore->ETFileDisplayedList)
+ {
+ if (ETCore->ETFileDisplayed)
+ {
+ ET_Displayed_File_List_By_Etfile(ETCore->ETFileDisplayed);
+ }else if (ETCore->ETFileDisplayedList->data)
+ {
+ // Select the new file (synchronize index,...)
+ ET_Displayed_File_List_By_Etfile((ET_File *)ETCore->ETFileDisplayedList->data);
+ }
+ }else
+ {
+ // Reinit the tag and file area
+ Clear_File_Entry_Field();
+ Clear_Header_Fields();
+ Clear_Tag_Entry_Fields();
+ gtk_label_set_text(GTK_LABEL(FileIndex),"0/0:");
+ Update_Command_Buttons_Sensivity();
+ }
+
+ return TRUE;
+}
+
+
+/*
+ * Delete the corresponding file (allocated data was previously freed!). Return TRUE if deleted.
+ */
+gboolean ET_Remove_File_From_Artist_Album_List (ET_File *ETFile)
+{
+ GList *ArtistList;
+ GList *AlbumList;
+ GList *etfilelist;
+ ET_File *etfile;
+
+
+ // Search the ETFile in the list...
+ ArtistList = ETCore->ETArtistAlbumFileList;
+ while (ArtistList && ETFile)
+ {
+ AlbumList = g_list_first((GList *)ArtistList->data);
+ while (AlbumList)
+ {
+ etfilelist = g_list_first((GList *)AlbumList->data);
+ while (etfilelist)
+ {
+ etfile = (ET_File *)etfilelist->data;
+ if (ETFile == etfile) // The ETFile to delete was found!
+ {
+ etfilelist = g_list_remove(etfilelist,ETFile);
+ if (etfilelist) // Delete from AlbumList
+ {
+ AlbumList->data = (gpointer) g_list_first(etfilelist);
+ }else
+ {
+ AlbumList = g_list_remove(AlbumList,AlbumList->data);
+ if (AlbumList) // Delete from ArtistList
+ {
+ ArtistList->data = (gpointer) g_list_first(AlbumList);
+ }else
+ {
+ ETCore->ETArtistAlbumFileList = g_list_remove(ArtistList,ArtistList->data); // Delete from the main list
+ if (ETCore->ETArtistAlbumFileList)
+ ETCore->ETArtistAlbumFileList = g_list_first(ETCore->ETArtistAlbumFileList);
+ return TRUE;
+ }
+ return TRUE;
+ }
+ return TRUE;
+ }
+ etfilelist = etfilelist->next;
+ }
+ AlbumList = AlbumList->next;
+ }
+ ArtistList = ArtistList->next;
+ }
+ //ET_Debug_Print_Artist_Album_List(__FILE__,__LINE__,__FUNCTION__);
+ return FALSE; // ETFile is NUL, or not found in the list
+}
+
+
+
+/**************************
+ * File sorting functions *
+ **************************/
+
+/*
+ * Sort the 'ETFileDisplayedList' following the 'Sorting_Type'
+ * Note : Add also new sorting in 'Browser_List_Sort_Func'
+ */
+void ET_Sort_Displayed_File_List (ET_Sorting_Type Sorting_Type)
+{
+ ETCore->ETFileDisplayedList = ET_Sort_File_List(ETCore->ETFileDisplayedList,Sorting_Type);
+}
+/*
+ * Sort an 'ETFileList'
+ */
+GList *ET_Sort_File_List (GList *ETFileList, ET_Sorting_Type Sorting_Type)
+{
+ // Important to rewind before
+ GList *etfilelist = g_list_first(ETFileList);
+
+ // Save sorting mode (note: needed when called from UI)
+ SORTING_FILE_MODE = Sorting_Type;
+
+ // Sort...
+ switch (Sorting_Type)
+ {
+ case SORTING_UNKNOWN:
+ case SORTING_BY_ASCENDING_FILENAME:
+ etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Ascending_Filename);
+ break;
+ case SORTING_BY_DESCENDING_FILENAME:
+ etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Descending_Filename);
+ break;
+ case SORTING_BY_ASCENDING_TRACK_NUMBER:
+ etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Ascending_Track_Number);
+ break;
+ case SORTING_BY_DESCENDING_TRACK_NUMBER:
+ etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Descending_Track_Number);
+ break;
+ case SORTING_BY_ASCENDING_CREATION_DATE:
+ etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Ascending_Creation_Date);
+ break;
+ case SORTING_BY_DESCENDING_CREATION_DATE:
+ etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Descending_Creation_Date);
+ break;
+ case SORTING_BY_ASCENDING_TITLE:
+ etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Ascending_Title);
+ break;
+ case SORTING_BY_DESCENDING_TITLE:
+ etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Descending_Title);
+ break;
+ case SORTING_BY_ASCENDING_ARTIST:
+ etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Ascending_Artist);
+ break;
+ case SORTING_BY_DESCENDING_ARTIST:
+ etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Descending_Artist);
+ break;
+ case SORTING_BY_ASCENDING_ALBUM:
+ etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Ascending_Album);
+ break;
+ case SORTING_BY_DESCENDING_ALBUM:
+ etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Descending_Album);
+ break;
+ case SORTING_BY_ASCENDING_YEAR:
+ etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Ascending_Year);
+ break;
+ case SORTING_BY_DESCENDING_YEAR:
+ etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Descending_Year);
+ break;
+ case SORTING_BY_ASCENDING_GENRE:
+ etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Ascending_Genre);
+ break;
+ case SORTING_BY_DESCENDING_GENRE:
+ etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Descending_Genre);
+ break;
+ case SORTING_BY_ASCENDING_COMMENT:
+ etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Ascending_Comment);
+ break;
+ case SORTING_BY_DESCENDING_COMMENT:
+ etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Descending_Comment);
+ break;
+ case SORTING_BY_ASCENDING_COMPOSER:
+ etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Ascending_Composer);
+ break;
+ case SORTING_BY_DESCENDING_COMPOSER:
+ etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Descending_Composer);
+ break;
+ case SORTING_BY_ASCENDING_ORIG_ARTIST:
+ etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Ascending_Orig_Artist);
+ break;
+ case SORTING_BY_DESCENDING_ORIG_ARTIST:
+ etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Descending_Orig_Artist);
+ break;
+ case SORTING_BY_ASCENDING_COPYRIGHT:
+ etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Ascending_Copyright);
+ break;
+ case SORTING_BY_DESCENDING_COPYRIGHT:
+ etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Descending_Copyright);
+ break;
+ case SORTING_BY_ASCENDING_URL:
+ etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Ascending_Url);
+ break;
+ case SORTING_BY_DESCENDING_URL:
+ etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Descending_Url);
+ break;
+ case SORTING_BY_ASCENDING_ENCODED_BY:
+ etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Ascending_Encoded_By);
+ break;
+ case SORTING_BY_DESCENDING_ENCODED_BY:
+ etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Descending_Encoded_By);
+ break;
+ case SORTING_BY_ASCENDING_FILE_TYPE:
+ etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Ascending_File_Type);
+ break;
+ case SORTING_BY_DESCENDING_FILE_TYPE:
+ etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Descending_File_Type);
+ break;
+ case SORTING_BY_ASCENDING_FILE_SIZE:
+ etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Ascending_File_Size);
+ break;
+ case SORTING_BY_DESCENDING_FILE_SIZE:
+ etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Descending_File_Size);
+ break;
+ case SORTING_BY_ASCENDING_FILE_DURATION:
+ etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Ascending_File_Duration);
+ break;
+ case SORTING_BY_DESCENDING_FILE_DURATION:
+ etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Descending_File_Duration);
+ break;
+ case SORTING_BY_ASCENDING_FILE_BITRATE:
+ etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Ascending_File_Bitrate);
+ break;
+ case SORTING_BY_DESCENDING_FILE_BITRATE:
+ etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Descending_File_Bitrate);
+ break;
+ case SORTING_BY_ASCENDING_FILE_SAMPLERATE:
+ etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Ascending_File_Samplerate);
+ break;
+ case SORTING_BY_DESCENDING_FILE_SAMPLERATE:
+ etfilelist = g_list_sort(etfilelist,(GCompareFunc)ET_Comp_Func_Sort_File_By_Descending_File_Samplerate);
+ break;
+ }
+ //ETFileList = g_list_first(etfilelist);
+ return g_list_first(etfilelist);
+}
+
+
+/*
+ * Sort the list of files following the 'Sorting_Type' value. The new sorting is displayed in the UI.
+ */
+void ET_Sort_Displayed_File_List_And_Update_UI (ET_Sorting_Type Sorting_Type)
+{
+ if (!ETCore->ETFileList) return;
+
+ ET_Save_File_Data_From_UI(ETCore->ETFileDisplayed);
+
+ /* Sort the list */
+ ET_Sort_Displayed_File_List(Sorting_Type);
+
+ /* To number the ETFile in the list */
+ ET_Displayed_File_List_Number();
+
+ /* Reload files in browser list */
+ ET_Displayed_File_List_By_Etfile(ETCore->ETFileDisplayed); // Just to update 'ETFileDisplayedList'
+ Browser_List_Select_File_By_Etfile(ETCore->ETFileDisplayed,TRUE);
+ ET_Display_File_Data_To_UI(ETCore->ETFileDisplayed);
+
+ Browser_List_Refresh_Sort();
+ Update_Command_Buttons_Sensivity();
+}
+
+
+/*
+ * Comparison function for sorting by ascending file name.
+ */
+gint ET_Comp_Func_Sort_File_By_Ascending_Filename (ET_File *ETFile1, ET_File *ETFile2)
+{
+ gchar *file1_ck = ((File_Name *)((GList *)ETFile1->FileNameCur)->data)->value_ck;
+ gchar *file2_ck = ((File_Name *)((GList *)ETFile2->FileNameCur)->data)->value_ck;
+ // !!!! : Must be the same rules as "Cddb_Track_List_Sort_Func" to be
+ // able to sort in the same order files in cddb and in the file list.
+ return SORTING_FILE_CASE_SENSITIVE ? strcmp(file1_ck,file2_ck) : strcasecmp(file1_ck,file2_ck);
+}
+
+/*
+ * Comparison function for sorting by descending file name.
+ */
+gint ET_Comp_Func_Sort_File_By_Descending_Filename (ET_File *ETFile1, ET_File *ETFile2)
+{
+ return ET_Comp_Func_Sort_File_By_Ascending_Filename(ETFile2,ETFile1);
+}
+
+
+/*
+ * Comparison function for sorting by ascending track number.
+ */
+gint ET_Comp_Func_Sort_File_By_Ascending_Track_Number (ET_File *ETFile1, ET_File *ETFile2)
+{
+ gint track1, track2;
+
+ if ( !ETFile1->FileTag->data || !((File_Tag *)ETFile1->FileTag->data)->track )
+ track1 = 0;
+ else
+ track1 = atoi( ((File_Tag *)ETFile1->FileTag->data)->track );
+
+ if ( !ETFile2->FileTag->data || !((File_Tag *)ETFile2->FileTag->data)->track )
+ track2 = 0;
+ else
+ track2 = atoi( ((File_Tag *)ETFile2->FileTag->data)->track );
+
+ // Second criterion
+ if (track1 == track2)
+ return ET_Comp_Func_Sort_File_By_Ascending_Filename(ETFile1,ETFile2);
+
+ // First criterion
+ return (track1 - track2);
+}
+
+/*
+ * Comparison function for sorting by descending track number.
+ */
+gint ET_Comp_Func_Sort_File_By_Descending_Track_Number (ET_File *ETFile1, ET_File *ETFile2)
+{
+ return ET_Comp_Func_Sort_File_By_Ascending_Track_Number(ETFile2,ETFile1);
+}
+
+
+/*
+ * Comparison function for sorting by ascending creation date.
+ */
+gint ET_Comp_Func_Sort_File_By_Ascending_Creation_Date (ET_File *ETFile1, ET_File *ETFile2)
+{
+ struct stat statbuf1;
+ struct stat statbuf2;
+ gchar *filename1 = ((File_Name *)ETFile1->FileNameCur->data)->value;
+ gchar *filename2 = ((File_Name *)ETFile2->FileNameCur->data)->value;
+
+ lstat(filename1, &statbuf1);
+ lstat(filename2, &statbuf2);
+
+ // Second criterion
+ if (statbuf1.st_ctime == statbuf2.st_ctime)
+ return ET_Comp_Func_Sort_File_By_Ascending_Filename(ETFile1,ETFile2);
+
+ // First criterion
+ return (statbuf1.st_ctime - statbuf2.st_ctime); // Creation date
+ //return (statbuf1.st_mtime - statbuf2.st_mtime); // Modification date
+}
+
+/*
+ * Comparison function for sorting by descending creation date.
+ */
+gint ET_Comp_Func_Sort_File_By_Descending_Creation_Date (ET_File *ETFile1, ET_File *ETFile2)
+{
+ return ET_Comp_Func_Sort_File_By_Ascending_Creation_Date(ETFile2,ETFile1);
+}
+
+
+/*
+ * Comparison function for sorting by ascending title.
+ */
+gint ET_Comp_Func_Sort_File_By_Ascending_Title (ET_File *ETFile1, ET_File *ETFile2)
+{
+ // Compare pointers just in case they are the same (e.g. both are NULL)
+ if ((ETFile1->FileTag->data == ETFile2->FileTag->data)
+ || (((File_Tag *)ETFile1->FileTag->data)->title == ((File_Tag *)ETFile2->FileTag->data)->title))
+ return 0;
+
+ if ( !ETFile1->FileTag->data || !((File_Tag *)ETFile1->FileTag->data)->title )
+ return -1;
+ if ( !ETFile2->FileTag->data || !((File_Tag *)ETFile2->FileTag->data)->title )
+ return 1;
+
+ if (SORTING_FILE_CASE_SENSITIVE)
+ {
+ if ( strcmp(((File_Tag *)ETFile1->FileTag->data)->title,((File_Tag *)ETFile2->FileTag->data)->title) == 0 )
+ // Second criterion
+ return ET_Comp_Func_Sort_File_By_Ascending_Filename(ETFile1,ETFile2);
+ else
+ // First criterion
+ return strcmp(((File_Tag *)ETFile1->FileTag->data)->title,((File_Tag *)ETFile2->FileTag->data)->title);
+ }else
+ {
+ if ( strcasecmp(((File_Tag *)ETFile1->FileTag->data)->title,((File_Tag *)ETFile2->FileTag->data)->title) == 0 )
+ // Second criterion
+ return ET_Comp_Func_Sort_File_By_Ascending_Filename(ETFile1,ETFile2);
+ else
+ // First criterion
+ return strcasecmp(((File_Tag *)ETFile1->FileTag->data)->title,((File_Tag *)ETFile2->FileTag->data)->title);
+ }
+}
+
+/*
+ * Comparison function for sorting by descending title.
+ */
+gint ET_Comp_Func_Sort_File_By_Descending_Title (ET_File *ETFile1, ET_File *ETFile2)
+{
+ return ET_Comp_Func_Sort_File_By_Ascending_Title(ETFile2,ETFile1);
+}
+
+
+/*
+ * Comparison function for sorting by ascending artist.
+ */
+gint ET_Comp_Func_Sort_File_By_Ascending_Artist (ET_File *ETFile1, ET_File *ETFile2)
+{
+ // Compare pointers just in case they are the same (e.g. both are NULL)
+ if ((ETFile1->FileTag->data == ETFile2->FileTag->data)
+ || (((File_Tag *)ETFile1->FileTag->data)->artist == ((File_Tag *)ETFile2->FileTag->data)->artist))
+ return 0;
+
+ if ( !ETFile1->FileTag->data || !((File_Tag *)ETFile1->FileTag->data)->artist )
+ return -1;
+ if ( !ETFile2->FileTag->data || !((File_Tag *)ETFile2->FileTag->data)->artist )
+ return 1;
+
+ if (SORTING_FILE_CASE_SENSITIVE)
+ {
+ if ( strcmp(((File_Tag *)ETFile1->FileTag->data)->artist,((File_Tag *)ETFile2->FileTag->data)->artist) == 0 )
+ // Second criterion
+ return ET_Comp_Func_Sort_File_By_Ascending_Filename(ETFile1,ETFile2);
+ else
+ // First criterion
+ return strcmp(((File_Tag *)ETFile1->FileTag->data)->artist,((File_Tag *)ETFile2->FileTag->data)->artist);
+ }else
+ {
+ if ( strcasecmp(((File_Tag *)ETFile1->FileTag->data)->artist,((File_Tag *)ETFile2->FileTag->data)->artist) == 0 )
+ // Second criterion
+ return ET_Comp_Func_Sort_File_By_Ascending_Filename(ETFile1,ETFile2);
+ else
+ // First criterion
+ return strcasecmp(((File_Tag *)ETFile1->FileTag->data)->artist,((File_Tag *)ETFile2->FileTag->data)->artist);
+ }
+}
+
+/*
+ * Comparison function for sorting by descending artist.
+ */
+gint ET_Comp_Func_Sort_File_By_Descending_Artist (ET_File *ETFile1, ET_File *ETFile2)
+{
+ return ET_Comp_Func_Sort_File_By_Ascending_Album(ETFile2,ETFile1);
+}
+
+
+/*
+ * Comparison function for sorting by ascending album.
+ */
+gint ET_Comp_Func_Sort_File_By_Ascending_Album (ET_File *ETFile1, ET_File *ETFile2)
+{
+ // Compare pointers just in case they are the same (e.g. both are NULL)
+ if ((ETFile1->FileTag->data == ETFile2->FileTag->data)
+ || (((File_Tag *)ETFile1->FileTag->data)->album == ((File_Tag *)ETFile2->FileTag->data)->album))
+ return 0;
+
+ if ( !ETFile1->FileTag->data || !((File_Tag *)ETFile1->FileTag->data)->album )
+ return -1;
+ if ( !ETFile2->FileTag->data || !((File_Tag *)ETFile2->FileTag->data)->album )
+ return 1;
+
+ if (SORTING_FILE_CASE_SENSITIVE)
+ {
+ if ( strcmp(((File_Tag *)ETFile1->FileTag->data)->album,((File_Tag *)ETFile2->FileTag->data)->album) == 0 )
+ // Second criterion
+ return ET_Comp_Func_Sort_File_By_Ascending_Filename(ETFile1,ETFile2);
+ else
+ // First criterion
+ return strcmp(((File_Tag *)ETFile1->FileTag->data)->album,((File_Tag *)ETFile2->FileTag->data)->album);
+ }else
+ {
+ if ( strcasecmp(((File_Tag *)ETFile1->FileTag->data)->album,((File_Tag *)ETFile2->FileTag->data)->album) == 0 )
+ // Second criterion
+ return ET_Comp_Func_Sort_File_By_Ascending_Filename(ETFile1,ETFile2);
+ else
+ // First criterion
+ return strcasecmp(((File_Tag *)ETFile1->FileTag->data)->album,((File_Tag *)ETFile2->FileTag->data)->album);
+ }
+}
+
+/*
+ * Comparison function for sorting by descending album.
+ */
+gint ET_Comp_Func_Sort_File_By_Descending_Album (ET_File *ETFile1, ET_File *ETFile2)
+{
+ return ET_Comp_Func_Sort_File_By_Ascending_Album(ETFile2,ETFile1);
+}
+
+
+/*
+ * Comparison function for sorting by ascending year.
+ */
+gint ET_Comp_Func_Sort_File_By_Ascending_Year (ET_File *ETFile1, ET_File *ETFile2)
+{
+ gint year1, year2;
+
+ if ( !ETFile1->FileTag->data || !((File_Tag *)ETFile1->FileTag->data)->year )
+ year1 = 0;
+ else
+ year1 = atoi( ((File_Tag *)ETFile1->FileTag->data)->year );
+
+ if ( !ETFile2->FileTag->data || !((File_Tag *)ETFile2->FileTag->data)->year )
+ year2 = 0;
+ else
+ year2 = atoi( ((File_Tag *)ETFile2->FileTag->data)->year );
+
+ // Second criterion
+ if (year1 == year2)
+ return ET_Comp_Func_Sort_File_By_Ascending_Filename(ETFile1,ETFile2);
+
+ // First criterion
+ return (year1 - year2);
+}
+
+/*
+ * Comparison function for sorting by descending year.
+ */
+gint ET_Comp_Func_Sort_File_By_Descending_Year (ET_File *ETFile1, ET_File *ETFile2)
+{
+ return ET_Comp_Func_Sort_File_By_Ascending_Year(ETFile2,ETFile1);
+}
+
+
+/*
+ * Comparison function for sorting by ascending genre.
+ */
+gint ET_Comp_Func_Sort_File_By_Ascending_Genre (ET_File *ETFile1, ET_File *ETFile2)
+{
+ // Compare pointers just in case they are the same (e.g. both are NULL)
+ if ((ETFile1->FileTag->data == ETFile2->FileTag->data)
+ || (((File_Tag *)ETFile1->FileTag->data)->genre == ((File_Tag *)ETFile2->FileTag->data)->genre))
+ return 0;
+
+ if ( !ETFile1->FileTag->data || !((File_Tag *)ETFile1->FileTag->data)->genre ) return -1;
+ if ( !ETFile2->FileTag->data || !((File_Tag *)ETFile2->FileTag->data)->genre ) return 1;
+
+ if (SORTING_FILE_CASE_SENSITIVE)
+ {
+ if ( strcmp(((File_Tag *)ETFile1->FileTag->data)->genre,((File_Tag *)ETFile2->FileTag->data)->genre) == 0 )
+ // Second criterion
+ return ET_Comp_Func_Sort_File_By_Ascending_Filename(ETFile1,ETFile2);
+ else
+ // First criterion
+ return strcmp(((File_Tag *)ETFile1->FileTag->data)->genre,((File_Tag *)ETFile2->FileTag->data)->genre);
+ }else
+ {
+ if ( strcasecmp(((File_Tag *)ETFile1->FileTag->data)->genre,((File_Tag *)ETFile2->FileTag->data)->genre) == 0 )
+ // Second criterion
+ return ET_Comp_Func_Sort_File_By_Ascending_Filename(ETFile1,ETFile2);
+ else
+ // First criterion
+ return strcasecmp(((File_Tag *)ETFile1->FileTag->data)->genre,((File_Tag *)ETFile2->FileTag->data)->genre);
+ }
+}
+
+/*
+ * Comparison function for sorting by descending genre.
+ */
+gint ET_Comp_Func_Sort_File_By_Descending_Genre (ET_File *ETFile1, ET_File *ETFile2)
+{
+ return ET_Comp_Func_Sort_File_By_Ascending_Genre(ETFile2,ETFile1);
+}
+
+
+/*
+ * Comparison function for sorting by ascending comment.
+ */
+gint ET_Comp_Func_Sort_File_By_Ascending_Comment (ET_File *ETFile1, ET_File *ETFile2)
+{
+ // Compare pointers just in case they are the same (e.g. both are NULL)
+ if ((ETFile1->FileTag->data == ETFile2->FileTag->data)
+ || (((File_Tag *)ETFile1->FileTag->data)->comment == ((File_Tag *)ETFile2->FileTag->data)->comment))
+ return 0;
+
+ if ( !ETFile1->FileTag->data || !((File_Tag *)ETFile1->FileTag->data)->comment )
+ return -1;
+ if ( !ETFile2->FileTag->data || !((File_Tag *)ETFile2->FileTag->data)->comment )
+ return 1;
+
+ if (SORTING_FILE_CASE_SENSITIVE)
+ {
+ if ( strcmp(((File_Tag *)ETFile1->FileTag->data)->comment,((File_Tag *)ETFile2->FileTag->data)->comment) == 0 )
+ // Second criterion
+ return ET_Comp_Func_Sort_File_By_Ascending_Filename(ETFile1,ETFile2);
+ else
+ // First criterion
+ return strcmp(((File_Tag *)ETFile1->FileTag->data)->comment,((File_Tag *)ETFile2->FileTag->data)->comment);
+ }else
+ {
+ if ( strcasecmp(((File_Tag *)ETFile1->FileTag->data)->comment,((File_Tag *)ETFile2->FileTag->data)->comment) == 0 )
+ // Second criterion
+ return ET_Comp_Func_Sort_File_By_Ascending_Filename(ETFile1,ETFile2);
+ else
+ // First criterion
+ return strcasecmp(((File_Tag *)ETFile1->FileTag->data)->comment,((File_Tag *)ETFile2->FileTag->data)->comment);
+ }
+}
+
+/*
+ * Comparison function for sorting by descending comment.
+ */
+gint ET_Comp_Func_Sort_File_By_Descending_Comment (ET_File *ETFile1, ET_File *ETFile2)
+{
+ return ET_Comp_Func_Sort_File_By_Ascending_Comment(ETFile2,ETFile1);
+}
+
+
+/*
+ * Comparison function for sorting by ascending composer.
+ */
+gint ET_Comp_Func_Sort_File_By_Ascending_Composer (ET_File *ETFile1, ET_File *ETFile2)
+{
+ // Compare pointers just in case they are the same (e.g. both are NULL)
+ if ((ETFile1->FileTag->data == ETFile2->FileTag->data)
+ || (((File_Tag *)ETFile1->FileTag->data)->composer == ((File_Tag *)ETFile2->FileTag->data)->composer))
+ return 0;
+
+ if ( !ETFile1->FileTag->data || !((File_Tag *)ETFile1->FileTag->data)->composer )
+ return -1;
+ if ( !ETFile2->FileTag->data || !((File_Tag *)ETFile2->FileTag->data)->composer )
+ return 1;
+
+ if (SORTING_FILE_CASE_SENSITIVE)
+ {
+ if ( strcmp(((File_Tag *)ETFile1->FileTag->data)->composer,((File_Tag *)ETFile2->FileTag->data)->composer) == 0 )
+ // Second criterion
+ return ET_Comp_Func_Sort_File_By_Ascending_Filename(ETFile1,ETFile2);
+ else
+ // First criterion
+ return strcmp(((File_Tag *)ETFile1->FileTag->data)->composer,((File_Tag *)ETFile2->FileTag->data)->composer);
+ }else
+ {
+ if ( strcasecmp(((File_Tag *)ETFile1->FileTag->data)->composer,((File_Tag *)ETFile2->FileTag->data)->composer) == 0 )
+ // Second criterion
+ return ET_Comp_Func_Sort_File_By_Ascending_Filename(ETFile1,ETFile2);
+ else
+ // First criterion
+ return strcasecmp(((File_Tag *)ETFile1->FileTag->data)->composer,((File_Tag *)ETFile2->FileTag->data)->composer);
+ }
+}
+
+/*
+ * Comparison function for sorting by descending composer.
+ */
+gint ET_Comp_Func_Sort_File_By_Descending_Composer (ET_File *ETFile1, ET_File *ETFile2)
+{
+ return ET_Comp_Func_Sort_File_By_Ascending_Composer(ETFile2,ETFile1);
+}
+
+
+/*
+ * Comparison function for sorting by ascending original artist.
+ */
+gint ET_Comp_Func_Sort_File_By_Ascending_Orig_Artist (ET_File *ETFile1, ET_File *ETFile2)
+{
+ // Compare pointers just in case they are the same (e.g. both are NULL)
+ if ((ETFile1->FileTag->data == ETFile2->FileTag->data)
+ || (((File_Tag *)ETFile1->FileTag->data)->orig_artist == ((File_Tag *)ETFile2->FileTag->data)->orig_artist))
+ return 0;
+
+ if ( !ETFile1->FileTag->data || !((File_Tag *)ETFile1->FileTag->data)->orig_artist )
+ return -1;
+ if ( !ETFile2->FileTag->data || !((File_Tag *)ETFile2->FileTag->data)->orig_artist )
+ return 1;
+
+ if (SORTING_FILE_CASE_SENSITIVE)
+ {
+ if ( strcmp(((File_Tag *)ETFile1->FileTag->data)->orig_artist,((File_Tag *)ETFile2->FileTag->data)->orig_artist) == 0 )
+ // Second criterion
+ return ET_Comp_Func_Sort_File_By_Ascending_Filename(ETFile1,ETFile2);
+ else
+ // First criterion
+ return strcmp(((File_Tag *)ETFile1->FileTag->data)->orig_artist,((File_Tag *)ETFile2->FileTag->data)->orig_artist);
+ }else
+ {
+ if ( strcasecmp(((File_Tag *)ETFile1->FileTag->data)->orig_artist,((File_Tag *)ETFile2->FileTag->data)->orig_artist) == 0 )
+ // Second criterion
+ return ET_Comp_Func_Sort_File_By_Ascending_Filename(ETFile1,ETFile2);
+ else
+ // First criterion
+ return strcasecmp(((File_Tag *)ETFile1->FileTag->data)->orig_artist,((File_Tag *)ETFile2->FileTag->data)->orig_artist);
+ }
+}
+
+/*
+ * Comparison function for sorting by descending original artist.
+ */
+gint ET_Comp_Func_Sort_File_By_Descending_Orig_Artist (ET_File *ETFile1, ET_File *ETFile2)
+{
+ return ET_Comp_Func_Sort_File_By_Ascending_Orig_Artist(ETFile2,ETFile1);
+}
+
+
+/*
+ * Comparison function for sorting by ascending copyright.
+ */
+gint ET_Comp_Func_Sort_File_By_Ascending_Copyright (ET_File *ETFile1, ET_File *ETFile2)
+{
+ // Compare pointers just in case they are the same (e.g. both are NULL)
+ if ((ETFile1->FileTag->data == ETFile2->FileTag->data)
+ || (((File_Tag *)ETFile1->FileTag->data)->copyright == ((File_Tag *)ETFile2->FileTag->data)->copyright))
+ return 0;
+
+ if ( !ETFile1->FileTag->data || !((File_Tag *)ETFile1->FileTag->data)->copyright )
+ return -1;
+ if ( !ETFile2->FileTag->data || !((File_Tag *)ETFile2->FileTag->data)->copyright )
+ return 1;
+
+ if (SORTING_FILE_CASE_SENSITIVE)
+ {
+ if ( strcmp(((File_Tag *)ETFile1->FileTag->data)->copyright,((File_Tag *)ETFile2->FileTag->data)->copyright) == 0 )
+ // Second criterion
+ return ET_Comp_Func_Sort_File_By_Ascending_Filename(ETFile1,ETFile2);
+ else
+ // First criterion
+ return strcmp(((File_Tag *)ETFile1->FileTag->data)->copyright,((File_Tag *)ETFile2->FileTag->data)->copyright);
+ }else
+ {
+ if ( strcasecmp(((File_Tag *)ETFile1->FileTag->data)->copyright,((File_Tag *)ETFile2->FileTag->data)->copyright) == 0 )
+ // Second criterion
+ return ET_Comp_Func_Sort_File_By_Ascending_Filename(ETFile1,ETFile2);
+ else
+ // First criterion
+ return strcasecmp(((File_Tag *)ETFile1->FileTag->data)->copyright,((File_Tag *)ETFile2->FileTag->data)->copyright);
+ }
+}
+
+/*
+ * Comparison function for sorting by descending copyright.
+ */
+gint ET_Comp_Func_Sort_File_By_Descending_Copyright (ET_File *ETFile1, ET_File *ETFile2)
+{
+ return ET_Comp_Func_Sort_File_By_Ascending_Copyright(ETFile2,ETFile1);
+}
+
+
+/*
+ * Comparison function for sorting by ascending URL.
+ */
+gint ET_Comp_Func_Sort_File_By_Ascending_Url (ET_File *ETFile1, ET_File *ETFile2)
+{
+ // Compare pointers just in case they are the same (e.g. both are NULL)
+ if ((ETFile1->FileTag->data == ETFile2->FileTag->data)
+ || (((File_Tag *)ETFile1->FileTag->data)->url == ((File_Tag *)ETFile2->FileTag->data)->url))
+ return 0;
+
+ if ( !ETFile1->FileTag->data || !((File_Tag *)ETFile1->FileTag->data)->url )
+ return -1;
+ if ( !ETFile2->FileTag->data || !((File_Tag *)ETFile2->FileTag->data)->url )
+ return 1;
+
+ if (SORTING_FILE_CASE_SENSITIVE)
+ {
+ if ( strcmp(((File_Tag *)ETFile1->FileTag->data)->url,((File_Tag *)ETFile2->FileTag->data)->url) == 0 )
+ // Second criterion
+ return ET_Comp_Func_Sort_File_By_Ascending_Filename(ETFile1,ETFile2);
+ else
+ // First criterion
+ return strcmp(((File_Tag *)ETFile1->FileTag->data)->url,((File_Tag *)ETFile2->FileTag->data)->url);
+ }else
+ {
+ if ( strcasecmp(((File_Tag *)ETFile1->FileTag->data)->url,((File_Tag *)ETFile2->FileTag->data)->url) == 0 )
+ // Second criterion
+ return ET_Comp_Func_Sort_File_By_Ascending_Filename(ETFile1,ETFile2);
+ else
+ // First criterion
+ return strcasecmp(((File_Tag *)ETFile1->FileTag->data)->url,((File_Tag *)ETFile2->FileTag->data)->url);
+ }
+}
+
+/*
+ * Comparison function for sorting by descending URL.
+ */
+gint ET_Comp_Func_Sort_File_By_Descending_Url (ET_File *ETFile1, ET_File *ETFile2)
+{
+ return ET_Comp_Func_Sort_File_By_Ascending_Url(ETFile2,ETFile1);
+}
+
+
+/*
+ * Comparison function for sorting by ascending encoded by.
+ */
+gint ET_Comp_Func_Sort_File_By_Ascending_Encoded_By (ET_File *ETFile1, ET_File *ETFile2)
+{
+ // Compare pointers just in case they are the same (e.g. both are NULL)
+ if ((ETFile1->FileTag->data == ETFile2->FileTag->data)
+ || (((File_Tag *)ETFile1->FileTag->data)->encoded_by == ((File_Tag *)ETFile2->FileTag->data)->encoded_by))
+ return 0;
+
+ if ( !ETFile1->FileTag->data || !((File_Tag *)ETFile1->FileTag->data)->encoded_by )
+ return -1;
+ if ( !ETFile2->FileTag->data || !((File_Tag *)ETFile2->FileTag->data)->encoded_by )
+ return 1;
+
+ if (SORTING_FILE_CASE_SENSITIVE)
+ {
+ if ( strcmp(((File_Tag *)ETFile1->FileTag->data)->encoded_by,((File_Tag *)ETFile2->FileTag->data)->encoded_by) == 0 )
+ // Second criterion
+ return ET_Comp_Func_Sort_File_By_Ascending_Filename(ETFile1,ETFile2);
+ else
+ // First criterion
+ return strcmp(((File_Tag *)ETFile1->FileTag->data)->encoded_by,((File_Tag *)ETFile2->FileTag->data)->encoded_by);
+ }else
+ {
+ if ( strcasecmp(((File_Tag *)ETFile1->FileTag->data)->encoded_by,((File_Tag *)ETFile2->FileTag->data)->encoded_by) == 0 )
+ // Second criterion
+ return ET_Comp_Func_Sort_File_By_Ascending_Filename(ETFile1,ETFile2);
+ else
+ // First criterion
+ return strcasecmp(((File_Tag *)ETFile1->FileTag->data)->encoded_by,((File_Tag *)ETFile2->FileTag->data)->encoded_by);
+ }
+}
+
+/*
+ * Comparison function for sorting by descendingencoded by.
+ */
+gint ET_Comp_Func_Sort_File_By_Descending_Encoded_By (ET_File *ETFile1, ET_File *ETFile2)
+{
+ return ET_Comp_Func_Sort_File_By_Ascending_Encoded_By(ETFile2,ETFile1);
+}
+
+
+/*
+ * Comparison function for sorting by ascending file type (mp3, ogg, ...).
+ */
+gint ET_Comp_Func_Sort_File_By_Ascending_File_Type (ET_File *ETFile1, ET_File *ETFile2)
+{
+ if ( !ETFile1->ETFileDescription ) return -1;
+ if ( !ETFile2->ETFileDescription ) return 1;
+
+ // Second criterion
+ if (ETFile1->ETFileDescription->FileType == ETFile2->ETFileDescription->FileType)
+ return ET_Comp_Func_Sort_File_By_Ascending_Filename(ETFile1,ETFile2);
+
+ // First criterion
+ return (ETFile1->ETFileDescription->FileType - ETFile2->ETFileDescription->FileType);
+}
+
+/*
+ * Comparison function for sorting by descending file type (mp3, ogg, ...).
+ */
+gint ET_Comp_Func_Sort_File_By_Descending_File_Type (ET_File *ETFile1, ET_File *ETFile2)
+{
+ return ET_Comp_Func_Sort_File_By_Ascending_File_Type(ETFile2,ETFile1);
+}
+
+
+/*
+ * Comparison function for sorting by ascending file size.
+ */
+gint ET_Comp_Func_Sort_File_By_Ascending_File_Size (ET_File *ETFile1, ET_File *ETFile2)
+{
+ if ( !ETFile1->ETFileInfo ) return -1;
+ if ( !ETFile2->ETFileInfo ) return 1;
+
+ // Second criterion
+ if (ETFile1->ETFileInfo->size == ETFile2->ETFileInfo->size)
+ return ET_Comp_Func_Sort_File_By_Ascending_Filename(ETFile1,ETFile2);
+
+ // First criterion
+ return (ETFile1->ETFileInfo->size - ETFile2->ETFileInfo->size);
+}
+
+/*
+ * Comparison function for sorting by descending file size.
+ */
+gint ET_Comp_Func_Sort_File_By_Descending_File_Size (ET_File *ETFile1, ET_File *ETFile2)
+{
+ return ET_Comp_Func_Sort_File_By_Ascending_File_Size(ETFile2,ETFile1);
+}
+
+
+/*
+ * Comparison function for sorting by ascending file duration.
+ */
+gint ET_Comp_Func_Sort_File_By_Ascending_File_Duration (ET_File *ETFile1, ET_File *ETFile2)
+{
+ if ( !ETFile1->ETFileInfo ) return -1;
+ if ( !ETFile2->ETFileInfo ) return 1;
+
+ // Second criterion
+ if (ETFile1->ETFileInfo->duration == ETFile2->ETFileInfo->duration)
+ return ET_Comp_Func_Sort_File_By_Ascending_Filename(ETFile1,ETFile2);
+
+ // First criterion
+ return (ETFile1->ETFileInfo->duration - ETFile2->ETFileInfo->duration);
+}
+
+/*
+ * Comparison function for sorting by descending file duration.
+ */
+gint ET_Comp_Func_Sort_File_By_Descending_File_Duration (ET_File *ETFile1, ET_File *ETFile2)
+{
+ return ET_Comp_Func_Sort_File_By_Ascending_File_Duration(ETFile2,ETFile1);
+}
+
+
+/*
+ * Comparison function for sorting by ascending file bitrate.
+ */
+gint ET_Comp_Func_Sort_File_By_Ascending_File_Bitrate (ET_File *ETFile1, ET_File *ETFile2)
+{
+ if ( !ETFile1->ETFileInfo ) return -1;
+ if ( !ETFile2->ETFileInfo ) return 1;
+
+ // Second criterion
+ if (ETFile1->ETFileInfo->bitrate == ETFile2->ETFileInfo->bitrate)
+ return ET_Comp_Func_Sort_File_By_Ascending_Filename(ETFile1,ETFile2);
+
+ // First criterion
+ return (ETFile1->ETFileInfo->bitrate - ETFile2->ETFileInfo->bitrate);
+}
+
+/*
+ * Comparison function for sorting by descending file bitrate.
+ */
+gint ET_Comp_Func_Sort_File_By_Descending_File_Bitrate (ET_File *ETFile1, ET_File *ETFile2)
+{
+ return ET_Comp_Func_Sort_File_By_Ascending_File_Bitrate(ETFile2,ETFile1);
+}
+
+
+/*
+ * Comparison function for sorting by ascending file samplerate.
+ */
+gint ET_Comp_Func_Sort_File_By_Ascending_File_Samplerate (ET_File *ETFile1, ET_File *ETFile2)
+{
+ if ( !ETFile1->ETFileInfo ) return -1;
+ if ( !ETFile2->ETFileInfo ) return 1;
+
+ // Second criterion
+ if (ETFile1->ETFileInfo->samplerate == ETFile2->ETFileInfo->samplerate)
+ return ET_Comp_Func_Sort_File_By_Ascending_Filename(ETFile1,ETFile2);
+
+ // First criterion
+ return (ETFile1->ETFileInfo->samplerate - ETFile2->ETFileInfo->samplerate);
+}
+
+/*
+ * Comparison function for sorting by descending file samplerate.
+ */
+gint ET_Comp_Func_Sort_File_By_Descending_File_Samplerate (ET_File *ETFile1, ET_File *ETFile2)
+{
+ return ET_Comp_Func_Sort_File_By_Ascending_File_Samplerate(ETFile2,ETFile1);
+}
+
+
+/*
+ * Comparison function for sorting by ascending IndexKey (used mainly to have the ETFileSelectionList already sorted.)
+ */
+gint ET_Comp_Func_Sort_File_By_Ascending_Index_Key (ET_File *ETFile1, ET_File *ETFile2)
+{
+ return (ETFile1->IndexKey - ETFile2->IndexKey);
+}
+
+
+/*
+ * Comparison function for sorting by ascending artist in the ArtistAlbumList.
+ */
+gint ET_Comp_Func_Sort_Artist_Item_By_Ascending_Artist (GList *AlbumList1, GList *AlbumList2)
+{
+ GList *etfilelist1 = NULL, *etfilelist2 = NULL;
+ ET_File *etfile1 = NULL, *etfile2 = NULL;
+ gchar *etfile1_artist, *etfile2_artist;
+
+ if (!AlbumList1
+ || !(etfilelist1 = (GList *)AlbumList1->data)
+ || !(etfile1 = (ET_File *)etfilelist1->data)
+ || !(etfile1_artist = ((File_Tag *)etfile1->FileTag->data)->artist) )
+ return -1;
+
+ if (!AlbumList2
+ || !(etfilelist2 = (GList *)AlbumList2->data)
+ || !(etfile2 = (ET_File *)etfilelist2->data)
+ || !(etfile2_artist = ((File_Tag *)etfile2->FileTag->data)->artist) )
+ return 1;
+
+ //if (SORTING_FILE_CASE_SENSITIVE)
+ // return strcmp(etfile1_artist,etfile2_artist);
+ //else
+ return strcasecmp(etfile1_artist,etfile2_artist);
+}
+
+/*
+ * Comparison function for sorting by ascending album in the ArtistAlbumList.
+ */
+gint ET_Comp_Func_Sort_Album_Item_By_Ascending_Album (GList *etfilelist1, GList *etfilelist2)
+{
+ ET_File *etfile1, *etfile2;
+ gchar *etfile1_album, *etfile2_album;
+
+ if (!etfilelist1
+ || !(etfile1 = (ET_File *)etfilelist1->data)
+ || !(etfile1_album = ((File_Tag *)etfile1->FileTag->data)->album) )
+ return -1;
+
+ if (!etfilelist2
+ || !(etfile2 = (ET_File *)etfilelist2->data)
+ || !(etfile2_album = ((File_Tag *)etfile2->FileTag->data)->album) )
+ return 1;
+
+ //if (SORTING_FILE_CASE_SENSITIVE)
+ // return strcmp(etfile1_album,etfile2_album);
+ //else
+ return strcasecmp(etfile1_album,etfile2_album);
+}
+
+/*
+ * Comparison function for sorting etfile in the ArtistAlbumList.
+ * FIX ME : should use the default sorting!
+ */
+gint ET_Comp_Func_Sort_Etfile_Item_By_Ascending_Filename (ET_File *ETFile1, ET_File *ETFile2)
+{
+
+ if (!ETFile1) return -1;
+ if (!ETFile2) return 1;
+
+ return ET_Comp_Func_Sort_File_By_Ascending_Filename(ETFile1,ETFile2);
+}
+
+
+
+
+/***************************
+ * List handling functions *
+ ***************************/
+
+/*
+ * Returns the first item of the "displayed list"
+ */
+GList *ET_Displayed_File_List_First (void)
+{
+ ETCore->ETFileDisplayedList = g_list_first(ETCore->ETFileDisplayedList);
+ return ETCore->ETFileDisplayedList;
+}
+
+/*
+ * Returns the previous item of the "displayed list". When no more item, it returns NULL.
+ */
+GList *ET_Displayed_File_List_Previous (void)
+{
+ if (ETCore->ETFileDisplayedList && ETCore->ETFileDisplayedList->prev)
+ return ETCore->ETFileDisplayedList = ETCore->ETFileDisplayedList->prev;
+ else
+ return NULL;
+}
+
+/*
+ * Returns the next item of the "displayed list".
+ * When no more item, it returns NULL to don't "overwrite" the list.
+ */
+GList *ET_Displayed_File_List_Next (void)
+{
+ if (ETCore->ETFileDisplayedList && ETCore->ETFileDisplayedList->next)
+ return ETCore->ETFileDisplayedList = ETCore->ETFileDisplayedList->next;
+ else
+ return NULL;
+}
+
+/*
+ * Returns the last item of the "displayed list"
+ */
+GList *ET_Displayed_File_List_Last (void)
+{
+ ETCore->ETFileDisplayedList = g_list_last(ETCore->ETFileDisplayedList);
+ return ETCore->ETFileDisplayedList;
+}
+
+/*
+ * Returns the item of the "displayed list" which correspond to the given 'ETFile' (used into browser list).
+ */
+GList *ET_Displayed_File_List_By_Etfile (ET_File *ETFile)
+{
+ GList *etfilelist;
+
+ etfilelist = ET_Displayed_File_List_First();
+ while (ETFile && etfilelist)
+ {
+ if ( ETFile == (ET_File *)etfilelist->data )
+ break;
+ etfilelist = ET_Displayed_File_List_Next();
+ }
+ ETCore->ETFileDisplayedList = etfilelist; // To "save" the position like in ET_File_List_Next... (not very good - FIX ME)
+ return etfilelist;
+}
+
+/*
+ * Returns the item of the "displayed list" which have the position 'pos_in_list' in the list (used into CDDB result list).
+ * Range for 'pos_in_list' is 1 to ETFileDisplayedList_Length.
+ */
+GList *ET_Displayed_File_List_By_Position (gulong pos_in_list)
+{
+ GList *etfilelist;
+ guint i = 1;
+
+ etfilelist = ET_Displayed_File_List_First();
+ while (etfilelist)
+ {
+ if (i == pos_in_list)
+ break;
+ etfilelist = ET_Displayed_File_List_Next();
+ i++;
+ }
+ ETCore->ETFileDisplayedList = etfilelist; // To "save" the position like in ET_Displayed_File_List_Next... (not very good - FIX ME)
+ return etfilelist;
+}
+
+/*
+ * Just returns the current item of the "main list"
+ */
+/*GList *ET_Displayed_File_List_Current (void)
+{
+ return ETCore->ETFileDisplayedList;
+ //return ETCore->ETFileDisplayedListPtr;
+}*/
+
+/*
+ * Renumber the list of displayed files (IndexKey) from 1 to n
+ */
+void ET_Displayed_File_List_Number (void)
+{
+ GList *list = NULL;
+ guint i = 1;
+
+ list = g_list_first(ETCore->ETFileDisplayedList);
+ while (list)
+ {
+ ((ET_File *)list->data)->IndexKey = i++;
+ list = list->next;
+ }
+}
+
+/*
+ * Returns the length of the list of displayed files
+ */
+guint ET_Displayed_File_List_Get_Length (void)
+{
+ GList *list = NULL;
+
+ list = g_list_first(ETCore->ETFileDisplayedList);
+ ETCore->ETFileDisplayedList_Length = g_list_length(list);
+ return ETCore->ETFileDisplayedList_Length;
+}
+
+/*
+ * Load the list of displayed files (calculate length, size, ...)
+ * It contains part (filtrated : view by artists and albums) or full ETCore->ETFileList list
+ */
+gboolean ET_Set_Displayed_File_List (GList *ETFileList)
+{
+ GList *list = NULL;
+
+ ETCore->ETFileDisplayedList = g_list_first(ETFileList);
+
+ //ETCore->ETFileDisplayedListPtr = ETCore->ETFileDisplayedList;
+ ETCore->ETFileDisplayedList_Length = ET_Displayed_File_List_Get_Length();
+ ETCore->ETFileDisplayedList_TotalSize = 0;
+ ETCore->ETFileDisplayedList_TotalDuration = 0;
+
+ // Get size and duration of files in the list
+ list = ETCore->ETFileDisplayedList;
+ while (list)
+ {
+ ETCore->ETFileDisplayedList_TotalSize += ((ET_File_Info *)((ET_File *)list->data)->ETFileInfo)->size;
+ ETCore->ETFileDisplayedList_TotalDuration += ((ET_File_Info *)((ET_File *)list->data)->ETFileInfo)->duration;
+ list = list->next;
+ }
+
+ // Sort the file list
+ ET_Sort_Displayed_File_List(SORTING_FILE_MODE);
+
+ // Should renums ETCore->ETFileDisplayedList only!
+ ET_Displayed_File_List_Number();
+
+ return TRUE;
+}
+
+
+
+/*********************
+ * Freeing functions *
+ *********************/
+
+/*
+ * Frees the full main list of files: GList *ETFileList and reinitialize it.
+ */
+gboolean ET_Free_File_List (void)
+{
+ GList *list = NULL;
+
+ if (!ETCore || !ETCore->ETFileList) return FALSE;
+
+ list = g_list_last(ETCore->ETFileList);
+ while (list)
+ {
+ ET_Free_File_List_Item((ET_File *)list->data);
+ if (!list->prev) break;
+ list = list->prev;
+ }
+
+ g_list_free(list);
+ ETCore->ETFileList = (GList *)NULL;
+ return TRUE;
+}
+
+
+/*
+ * Frees one item of the full main list of files.
+ */
+gboolean ET_Free_File_List_Item (ET_File *ETFile)
+{
+ if (ETFile)
+ {
+ /* Frees the lists */
+ ET_Free_File_Name_List(ETFile->FileNameList);
+ ET_Free_File_Name_List(ETFile->FileNameListBak);
+ ET_Free_File_Tag_List (ETFile->FileTagList);
+ ET_Free_File_Tag_List (ETFile->FileTagListBak);
+ /* Frees infos of ETFileInfo */
+ ET_Free_File_Info_Item (ETFile->ETFileInfo);
+ g_free(ETFile->ETFileExtension);
+ g_free(ETFile);
+ ETFile = NULL;
+ }
+ return TRUE;
+}
+
+
+/*
+ * Frees the full list: GList *FileNameList.
+ */
+gboolean ET_Free_File_Name_List (GList *FileNameList)
+{
+ GList *list;
+
+ if (!FileNameList) return FALSE;
+
+ list = g_list_last(FileNameList);
+ while (list)
+ {
+ if ( (File_Name *)list->data )
+ ET_Free_File_Name_Item( (File_Name *)list->data );
+
+ if (!list->prev) break;
+ list = list->prev;
+ }
+ g_list_free(list);
+ FileNameList = (GList *)NULL;
+ return TRUE;
+}
+
+
+/*
+ * Frees a File_Name item.
+ */
+gboolean ET_Free_File_Name_Item (File_Name *FileName)
+{
+ if (!FileName) return FALSE;
+
+ g_free(FileName->value);
+ g_free(FileName->value_utf8);
+ g_free(FileName->value_ck);
+ g_free(FileName);
+ FileName = (File_Name *)NULL;
+ return TRUE;
+}
+
+
+/*
+ * Frees the full list: GList *TagList.
+ */
+gboolean ET_Free_File_Tag_List (GList *FileTagList)
+{
+ GList *list;
+
+ if (!FileTagList) return FALSE;
+
+ list = g_list_last(FileTagList);
+ while (list)
+ {
+ if ( (File_Tag *)list->data )
+ ET_Free_File_Tag_Item( (File_Tag *)list->data );
+
+ if (!list->prev) break;
+ list = list->prev;
+ }
+ g_list_free(list);
+ FileTagList = (GList *)NULL;
+ return TRUE;
+}
+
+
+/*
+ * Frees the list of 'other' field in a File_Tag item (contains attached gchar data).
+ */
+gboolean ET_Free_File_Tag_Item_Other_Field (File_Tag *FileTag)
+{
+ GList *other = FileTag->other;
+
+ while (other)
+ {
+ g_free(other->data);
+
+ if (!other->next) break;
+ other = other->next;
+ }
+ g_list_free(FileTag->other);
+
+ return TRUE;
+}
+
+
+/*
+ * Frees a File_Tag item.
+ */
+gboolean ET_Free_File_Tag_Item (File_Tag *FileTag)
+{
+ if (!FileTag) return FALSE;
+
+ g_free(FileTag->title);
+ g_free(FileTag->artist);
+ g_free(FileTag->album);
+ g_free(FileTag->disc_number);
+ g_free(FileTag->year);
+ g_free(FileTag->track);
+ g_free(FileTag->track_total);
+ g_free(FileTag->genre);
+ g_free(FileTag->comment);
+ g_free(FileTag->composer);
+ g_free(FileTag->orig_artist);
+ g_free(FileTag->copyright);
+ g_free(FileTag->url);
+ g_free(FileTag->encoded_by);
+ Picture_Free(FileTag->picture);
+ // Free list of other fields
+ ET_Free_File_Tag_Item_Other_Field(FileTag);
+
+ g_free(FileTag);
+ FileTag = (File_Tag *)NULL;
+ return TRUE;
+}
+
+
+/*
+ * Frees a File_Info item.
+ */
+gboolean ET_Free_File_Info_Item (ET_File_Info *ETFileInfo)
+{
+ if (!ETFileInfo) return FALSE;
+
+ g_free(ETFileInfo->mpc_profile);
+ g_free(ETFileInfo->mpc_version);
+
+ g_free(ETFileInfo);
+ ETFileInfo = (ET_File_Info *)NULL;
+ return TRUE;
+}
+
+
+/*
+ * History list contains only pointers, so no data to free except the history structure.
+ */
+gboolean ET_Free_History_File_List (void)
+{
+ GList *list;
+
+ if (!ETCore || !ETCore->ETHistoryFileList) return FALSE;
+
+ ETCore->ETHistoryFileList = g_list_first(ETCore->ETHistoryFileList);
+ list = ETCore->ETHistoryFileList;
+ while (list)
+ {
+ g_free( (ET_History_File *)list->data );
+
+ if (!list->next) break;
+ list = list->next;
+ }
+ g_list_free(ETCore->ETHistoryFileList);
+ ETCore->ETHistoryFileList = (GList *)NULL;
+ return TRUE;
+}
+
+/*
+ * "Display" list contains only pointers, so NOTHING to free
+ */
+gboolean ET_Free_Displayed_File_List (void)
+{
+ if (!ETCore || !ETCore->ETFileDisplayedList) return FALSE;
+
+ ETCore->ETFileDisplayedList = g_list_first(ETCore->ETFileDisplayedList);
+ ETCore->ETFileDisplayedList = (GList *)NULL;
+
+ return TRUE;
+}
+
+
+/*
+ * ArtistAlbum list contains 3 levels of lists
+ */
+gboolean ET_Free_Artist_Album_File_List (void)
+{
+ GList *ArtistList;
+ GList *AlbumList;
+ GList *etfilelist;
+
+ if (!ETCore || !ETCore->ETArtistAlbumFileList) return FALSE;
+
+ ArtistList = ETCore->ETArtistAlbumFileList;
+ while (ArtistList)
+ {
+ AlbumList = (GList *)ArtistList->data;
+ while (AlbumList)
+ {
+ etfilelist = (GList *)AlbumList->data;
+ if (etfilelist)
+ g_list_free(etfilelist);
+ AlbumList = AlbumList->next;
+ }
+ if (ArtistList->data) // Free AlbumList list
+ g_list_free((GList *)ArtistList->data);
+
+ ArtistList = ArtistList->next;
+ }
+ if (ETCore->ETArtistAlbumFileList)
+ g_list_free(ETCore->ETArtistAlbumFileList);
+
+ ETCore->ETArtistAlbumFileList = (GList *)NULL;
+
+ return TRUE;
+}
+
+
+
+
+/*********************
+ * Copying functions *
+ *********************/
+
+/*
+ * Duplicate the 'other' list
+ */
+gboolean ET_Copy_File_Tag_Item_Other_Field (ET_File *ETFile, File_Tag *FileTag)
+{
+ File_Tag *FileTagCur;
+ GList *list = NULL;
+
+ FileTagCur = (File_Tag *)(ETFile->FileTag)->data;
+ list = FileTagCur->other;
+ while (list)
+ {
+ FileTag->other = g_list_append(FileTag->other,g_strdup((gchar *)list->data));
+ list = list->next;
+ }
+ return TRUE;
+}
+
+
+/*
+ * Copy data of the File_Tag structure (of ETFile) to the FileTag item.
+ * Reallocate data if not null.
+ */
+gboolean ET_Copy_File_Tag_Item (ET_File *ETFile, File_Tag *FileTag)
+{
+ File_Tag *FileTagCur;
+
+ if (!ETFile || !ETFile->FileTag || !(File_Tag *)(ETFile->FileTag)->data || !FileTag)
+ return FALSE;
+
+ /* The data to duplicate to FileTag */
+ FileTagCur = (File_Tag *)(ETFile->FileTag)->data;
+ // Key for the item, may be overwritten
+ FileTag->key = ET_Undo_Key_New();
+
+ if (FileTagCur->title)
+ {
+ FileTag->title = g_strdup(FileTagCur->title);
+ }else
+ {
+ g_free(FileTag->title);
+ FileTag->title = NULL;
+ }
+
+ if (FileTagCur->artist)
+ {
+ FileTag->artist = g_strdup(FileTagCur->artist);
+ }else
+ {
+ g_free(FileTag->artist);
+ FileTag->artist = NULL;
+ }
+
+ if (FileTagCur->album)
+ {
+ FileTag->album = g_strdup(FileTagCur->album);
+ }else
+ {
+ g_free(FileTag->album);
+ FileTag->album = NULL;
+ }
+
+ if (FileTagCur->disc_number)
+ {
+ FileTag->disc_number = g_strdup(FileTagCur->disc_number);
+ }else
+ {
+ g_free(FileTag->disc_number);
+ FileTag->disc_number = NULL;
+ }
+
+ if (FileTagCur->year)
+ {
+ FileTag->year = g_strdup(FileTagCur->year);
+ }else
+ {
+ g_free(FileTag->year);
+ FileTag->year = NULL;
+ }
+
+ if (FileTagCur->track)
+ {
+ FileTag->track = g_strdup(FileTagCur->track);
+ }else
+ {
+ g_free(FileTag->track);
+ FileTag->track = NULL;
+ }
+
+ if (FileTagCur->track_total)
+ {
+ FileTag->track_total = g_strdup(FileTagCur->track_total);
+ }else
+ {
+ g_free(FileTag->track_total);
+ FileTag->track_total = NULL;
+ }
+
+ if (FileTagCur->genre)
+ {
+ FileTag->genre = g_strdup(FileTagCur->genre);
+ }else
+ {
+ g_free(FileTag->genre);
+ FileTag->genre = NULL;
+ }
+
+ if (FileTagCur->comment)
+ {
+ FileTag->comment = g_strdup(FileTagCur->comment);
+ }else
+ {
+ g_free(FileTag->comment);
+ FileTag->comment = NULL;
+ }
+
+ if (FileTagCur->composer)
+ {
+ FileTag->composer = g_strdup(FileTagCur->composer);
+ }else
+ {
+ g_free(FileTag->composer);
+ FileTag->composer = NULL;
+ }
+
+ if (FileTagCur->orig_artist)
+ {
+ FileTag->orig_artist = g_strdup(FileTagCur->orig_artist);
+ }else
+ {
+ g_free(FileTag->orig_artist);
+ FileTag->orig_artist = NULL;
+ }
+
+ if (FileTagCur->copyright)
+ {
+ FileTag->copyright = g_strdup(FileTagCur->copyright);
+ }else
+ {
+ g_free(FileTag->copyright);
+ FileTag->copyright = NULL;
+ }
+
+ if (FileTagCur->url)
+ {
+ FileTag->url = g_strdup(FileTagCur->url);
+ }else
+ {
+ g_free(FileTag->url);
+ FileTag->url = NULL;
+ }
+
+ if (FileTagCur->encoded_by)
+ {
+ FileTag->encoded_by = g_strdup(FileTagCur->encoded_by);
+ }else
+ {
+ g_free(FileTag->encoded_by);
+ FileTag->encoded_by = NULL;
+ }
+
+ if (FileTagCur->picture)
+ {
+ if (FileTag->picture)
+ Picture_Free(FileTag->picture);
+ FileTag->picture = Picture_Copy(FileTagCur->picture);
+ }else if (FileTag->picture)
+ {
+ Picture_Free(FileTag->picture);
+ FileTag->picture = NULL;
+ }
+
+ if (FileTagCur->other)
+ {
+ ET_Copy_File_Tag_Item_Other_Field(ETFile,FileTag);
+ }else
+ {
+ ET_Free_File_Tag_Item_Other_Field (FileTag);
+ }
+
+ return TRUE;
+}
+
+
+/*
+ * Set the value of a field of a FileName item (for ex, value of FileName->value)
+ * Must be used only for the 'gchar *' components (Only used for the filename)
+ */
+gboolean ET_Set_Field_File_Name_Item (gchar **FileNameField, gchar *value)
+{
+ return ET_Set_Field_File_Tag_Item(FileNameField,value);
+}
+
+
+/*
+ * Fill content of a FileName item according to the filename passed in argument (UTF-8 filename or not)
+ * Calculate also the collate key.
+ */
+gboolean ET_Set_Filename_File_Name_Item (File_Name *FileName, gchar *filename_utf8, gchar *filename)
+{
+ if (!FileName)
+ return FALSE;
+
+ if (filename_utf8 && filename)
+ {
+ FileName->value_utf8 = g_strdup(filename_utf8);
+ FileName->value = g_strdup(filename);
+ FileName->value_ck = g_utf8_collate_key_for_filename(FileName->value_utf8, -1);
+ }else if (filename_utf8)
+ {
+ FileName->value_utf8 = g_strdup(filename_utf8);
+ FileName->value = filename_from_display(filename_utf8);
+ FileName->value_ck = g_utf8_collate_key_for_filename(FileName->value_utf8, -1);
+ }else if (filename)
+ {
+ FileName->value_utf8 = filename_to_display(filename);;
+ FileName->value = g_strdup(filename);
+ FileName->value_ck = g_utf8_collate_key_for_filename(FileName->value_utf8, -1);
+ }else
+ {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+/*
+ * Set the value of a field of a FileTag item (for ex, value of FileTag->title)
+ * Must be used only for the 'gchar *' components
+ */
+gboolean ET_Set_Field_File_Tag_Item (gchar **FileTagField, gchar *value)
+{
+ if (!FileTagField) return FALSE;
+
+ if (*FileTagField != NULL)
+ {
+ g_free(*FileTagField);
+ *FileTagField = NULL;
+ }
+
+ if (value != NULL)
+ {
+ if (g_utf8_strlen(value, -1) > 0)
+ *FileTagField = g_strdup(value);
+ else
+ *FileTagField = NULL;
+ }
+
+ return TRUE;
+}
+
+
+/*
+ * Set the value of a field of a FileTag Picture item.
+ */
+gboolean ET_Set_Field_File_Tag_Picture (Picture **FileTagField, Picture *pic)
+{
+ if (!FileTagField) return FALSE;
+
+ if (*FileTagField != NULL)
+ {
+ Picture_Free((Picture *)*FileTagField);
+ *FileTagField = NULL;
+ }
+
+ if (pic)
+ *FileTagField = Picture_Copy(pic);
+
+ return TRUE;
+}
+
+
+/************************
+ * Displaying functions *
+ ************************/
+
+/*
+ * Display informations of the file (Position + Header + Tag) to the user interface.
+ * Before doing it, it saves data of the file currently displayed
+ */
+void ET_Display_File_Data_To_UI (ET_File *ETFile)
+{
+ ET_File_Description *ETFileDescription;
+ gchar *cur_filename;
+ gchar *cur_filename_utf8;
+ gchar *msg;
+
+ if (!ETFile) return;
+
+ cur_filename = ((File_Name *)((GList *)ETFile->FileNameCur)->data)->value;
+ cur_filename_utf8 = ((File_Name *)((GList *)ETFile->FileNameCur)->data)->value_utf8;
+ ETFileDescription = ETFile->ETFileDescription;
+
+ /* Save the current displayed file */
+ ETCore->ETFileDisplayed = ETFile;
+
+ /* Display position in list + show/hide icon if file writable/read_only (cur_filename) */
+ ET_Display_File_And_List_Status_To_UI(ETFile);
+
+ /* Display filename (and his path) (value in FileNameNew) */
+ ET_Display_Filename_To_UI(ETFile);
+
+ /* Display tag data */
+ switch (ETFileDescription->TagType)
+ {
+#ifdef ENABLE_MP3
+ case ID3_TAG:
+ gtk_frame_set_label(GTK_FRAME(TagFrame),_("ID3 Tag"));
+ ET_Display_File_Tag_To_UI(ETFile);
+ break;
+#endif
+#ifdef ENABLE_OGG
+ case OGG_TAG:
+ gtk_frame_set_label(GTK_FRAME(TagFrame),_("Ogg Vorbis Tag"));
+ ET_Display_File_Tag_To_UI(ETFile);
+ break;
+#endif
+#ifdef ENABLE_FLAC
+ case FLAC_TAG:
+ gtk_frame_set_label(GTK_FRAME(TagFrame),_("FLAC Vorbis Tag"));
+ ET_Display_File_Tag_To_UI(ETFile);
+ break;
+#endif
+ case APE_TAG:
+ gtk_frame_set_label(GTK_FRAME(TagFrame),_("APE Tag"));
+ ET_Display_File_Tag_To_UI(ETFile);
+ break;
+#ifdef ENABLE_MP4
+ case MP4_TAG:
+ gtk_frame_set_label(GTK_FRAME(TagFrame),_("MP4/M4A/AAC Tag"));
+ ET_Display_File_Tag_To_UI(ETFile);
+ break;
+#endif
+#ifdef ENABLE_WAVPACK
+ case WAVPACK_TAG:
+ gtk_frame_set_label(GTK_FRAME(TagFrame),_("Wavpack Tag"));
+ ET_Display_File_Tag_To_UI(ETFile);
+ break;
+#endif
+ case UNKNOWN_TAG:
+ default:
+ gtk_frame_set_label(GTK_FRAME(TagFrame),_("Tag"));
+ ET_Display_File_Tag_To_UI(ETFile); // To reinit screen
+ Log_Print("FileTag: Undefined tag type %d for file %s.",ETFileDescription->TagType,cur_filename_utf8);
+ break;
+ }
+
+ /* Display controls in tag area */
+ Tag_Area_Display_Controls(ETFile);
+
+ /* Display file data, header data and file type */
+ switch (ETFileDescription->FileType)
+ {
+#ifdef ENABLE_MP3
+ case MP3_FILE:
+ gtk_frame_set_label(GTK_FRAME(FileFrame),_("MP3 File"));
+ Mpeg_Header_Display_File_Info_To_UI(cur_filename,ETFile->ETFileInfo);
+ break;
+ case MP2_FILE:
+ gtk_frame_set_label(GTK_FRAME(FileFrame),_("MP2 File"));
+ Mpeg_Header_Display_File_Info_To_UI(cur_filename,ETFile->ETFileInfo);
+ break;
+#endif
+#ifdef ENABLE_OGG
+ case OGG_FILE:
+ gtk_frame_set_label(GTK_FRAME(FileFrame),_("Ogg Vorbis File"));
+ Ogg_Header_Display_File_Info_To_UI(cur_filename,ETFile->ETFileInfo);
+ break;
+#endif
+#ifdef ENABLE_SPEEX
+ case SPEEX_FILE:
+ gtk_frame_set_label(GTK_FRAME(FileFrame),_("Speex File"));
+ Ogg_Header_Display_File_Info_To_UI(cur_filename,ETFile->ETFileInfo);
+ break;
+#endif
+#ifdef ENABLE_FLAC
+ case FLAC_FILE:
+ gtk_frame_set_label(GTK_FRAME(FileFrame),_("FLAC File"));
+ Flac_Header_Display_File_Info_To_UI(cur_filename,ETFile->ETFileInfo);
+ break;
+#endif
+ case MPC_FILE:
+ gtk_frame_set_label(GTK_FRAME(FileFrame),_("MusePack File"));
+ Mpc_Header_Display_File_Info_To_UI(cur_filename,ETFile->ETFileInfo);
+ break;
+ case MAC_FILE:
+ gtk_frame_set_label(GTK_FRAME(FileFrame),_("Monkey's Audio File"));
+ Mac_Header_Display_File_Info_To_UI(cur_filename,ETFile->ETFileInfo);
+ break;
+#ifdef ENABLE_MP4
+ case MP4_FILE:
+ gtk_frame_set_label(GTK_FRAME(FileFrame),_("MP4/AAC File"));
+ Mp4_Header_Display_File_Info_To_UI(cur_filename,ETFile->ETFileInfo);
+ break;
+#endif
+#ifdef ENABLE_WAVPACK
+ case WAVPACK_FILE:
+ gtk_frame_set_label(GTK_FRAME(FileFrame),_("Wavpack File"));
+ Wavpack_Header_Display_File_Info_To_UI(cur_filename,ETFile->ETFileInfo);
+ break;
+#endif
+ case UNKNOWN_FILE:
+ default:
+ gtk_frame_set_label(GTK_FRAME(FileFrame),_("File"));
+ // Default displaying
+ ET_Display_File_Info_To_UI(ETFile->ETFileInfo);
+ Log_Print("ETFileInfo: Undefined file type %d for file %s.",ETFileDescription->FileType,cur_filename_utf8);
+ break;
+ }
+
+ msg = g_strdup_printf(_("File: '%s'"), cur_filename_utf8);
+ Statusbar_Message(msg,FALSE);
+ g_free(msg);
+}
+
+
+/*
+ * Toggle visibility of the small status icon if filename is read-only or not found.
+ * Show the position of the current file in the list, by using the index and list length.
+ */
+void ET_Display_File_And_List_Status_To_UI (ET_File *ETFile)
+{
+ FILE *file;
+ gchar *text;
+ gchar *cur_filename;
+
+ if (!ETFile) return;
+
+ cur_filename = ((File_Name *)((GList *)ETFile->FileNameCur)->data)->value;
+
+ /* Show/hide 'AccessStatusIcon' */
+ if ( (file=fopen(cur_filename,"r+b"))!=NULL )
+ {
+ gtk_widget_hide(ReadOnlyStatusIconBox);
+ gtk_widget_hide(BrokenStatusIconBox);
+ fclose(file);
+ }else
+ {
+ switch(errno)
+ {
+ case EACCES: /* Permission denied */
+ case EROFS: /* Read-only file system */
+ /* Read only file */
+ gtk_widget_show_all(ReadOnlyStatusIconBox);
+ gtk_widget_hide(BrokenStatusIconBox);
+ break;
+ case ENOENT: /* No such file or directory */
+ default:
+ /* File not found */
+ gtk_widget_show_all(BrokenStatusIconBox);
+ gtk_widget_hide(ReadOnlyStatusIconBox);
+ }
+ }
+
+ /* Show position of current file in list */
+ text = g_strdup_printf("%d/%d:",ETFile->IndexKey,ETCore->ETFileDisplayedList_Length);
+ gtk_label_set_text(GTK_LABEL(FileIndex),text);
+ g_free(text);
+}
+
+void ET_Display_Filename_To_UI (ET_File *ETFile)
+{
+ gchar *pos;
+ gchar *new_filename_utf8;
+ gchar *basename_utf8;
+ gchar *dirname_utf8;
+ gchar *text;
+
+ if (!ETFile) return;
+
+ new_filename_utf8 = ((File_Name *)((GList *)ETFile->FileNameNew)->data)->value_utf8;
+
+ /*
+ * Set filename into FileEntry
+ */
+ basename_utf8 = g_path_get_basename(new_filename_utf8);
+
+ // Remove the extension
+ if ((pos=g_utf8_strrchr(basename_utf8, -1, '.'))!=NULL)
+ *pos = 0;
+ gtk_entry_set_text(GTK_ENTRY(FileEntry),basename_utf8);
+ /*FIX ME : gchar *tmp = ET_Utf8_Validate_Full_String(basename_utf8);
+ g_free(tmp);*/
+ g_free(basename_utf8);
+ // Justify to the left text into FileEntry
+ gtk_editable_set_position(GTK_EDITABLE(FileEntry),0);
+
+ /*
+ * Set the path to the file into BrowserEntry (dirbrowser)
+ */
+ dirname_utf8 = g_path_get_dirname(new_filename_utf8);
+ Browser_Entry_Set_Text(dirname_utf8);
+
+ // And refresh the number of files in this directory
+ text = g_strdup_printf(_("%u file(s)"),ET_Get_Number_Of_Files_In_Directory(dirname_utf8));
+ Browser_Label_Set_Text(text);
+ g_free(dirname_utf8);
+ g_free(text);
+}
+
+
+/*
+ * Display all tag infos (tags fields) into entry boxes of the user interface.
+ * These data have the same structure for all files.
+ */
+gboolean ET_Display_File_Tag_To_UI (ET_File *ETFile)
+{
+ File_Tag *FileTag = NULL;
+ //GtkTextBuffer *textbuffer;
+
+ if (!ETFile || !ETFile->FileTag)
+ {
+ // Reset all tag entries
+ Clear_Tag_Entry_Fields();
+ //Tag_Area_Set_Sensitive(FALSE);
+ return FALSE;
+ }
+
+ //Tag_Area_Set_Sensitive(TRUE); // Causes displaying problem when saving files
+
+ FileTag = (File_Tag *)(ETFile->FileTag->data);
+
+ /* Show title */
+ if (FileTag && FileTag->title)
+ {
+ gchar *tmp = ET_Utf8_Validate_Full_String(FileTag->title);
+ gtk_entry_set_text(GTK_ENTRY(TitleEntry), tmp);
+ g_free(tmp);
+ }else
+ gtk_entry_set_text(GTK_ENTRY(TitleEntry),"");
+
+ /* Show artist */
+ if (FileTag && FileTag->artist)
+ {
+ gchar *tmp = ET_Utf8_Validate_Full_String(FileTag->artist);
+ gtk_entry_set_text(GTK_ENTRY(ArtistEntry), tmp);
+ g_free(tmp);
+ }else
+ gtk_entry_set_text(GTK_ENTRY(ArtistEntry),"");
+
+ /* Show album */
+ if (FileTag && FileTag->album)
+ {
+ gchar *tmp = ET_Utf8_Validate_Full_String(FileTag->album);
+ gtk_entry_set_text(GTK_ENTRY(AlbumEntry), tmp);
+ g_free(tmp);
+ }else
+ gtk_entry_set_text(GTK_ENTRY(AlbumEntry),"");
+
+ /* Show disc_number */
+ if (FileTag && FileTag->disc_number)
+ {
+ gchar *tmp = ET_Utf8_Validate_Full_String(FileTag->disc_number);
+ gtk_entry_set_text(GTK_ENTRY(DiscNumberEntry), tmp);
+ g_free(tmp);
+ }else
+ gtk_entry_set_text(GTK_ENTRY(DiscNumberEntry),"");
+
+ /* Show year */
+ if (FileTag && FileTag->year)
+ {
+ gchar *tmp = ET_Utf8_Validate_Full_String(FileTag->year);
+ gtk_entry_set_text(GTK_ENTRY(YearEntry),tmp);
+ g_free(tmp);
+ }else
+ gtk_entry_set_text(GTK_ENTRY(YearEntry),"");
+
+ /* Show track */
+ if (FileTag && FileTag->track)
+ {
+ gchar *tmp = ET_Utf8_Validate_Full_String(FileTag->track);
+ gtk_entry_set_text(GTK_ENTRY(GTK_BIN(TrackEntryCombo)->child),tmp);
+ g_free(tmp);
+ }else
+ gtk_entry_set_text(GTK_ENTRY(GTK_BIN(TrackEntryCombo)->child),"");
+
+ /* Show number of tracks on the album */
+ if (FileTag && FileTag->track_total)
+ {
+ gchar *tmp = ET_Utf8_Validate_Full_String(FileTag->track_total);
+ gtk_entry_set_text(GTK_ENTRY(TrackTotalEntry),tmp);
+ g_free(tmp);
+ }else
+ gtk_entry_set_text(GTK_ENTRY(TrackTotalEntry),"");
+
+ /* Show genre */
+ if (FileTag && FileTag->genre)
+ {
+ gchar *tmp = ET_Utf8_Validate_Full_String(FileTag->genre);
+ gtk_entry_set_text(GTK_ENTRY(GTK_BIN(GenreCombo)->child), tmp);
+ g_free(tmp);
+ }else
+ gtk_entry_set_text(GTK_ENTRY(GTK_BIN(GenreCombo)->child),"");
+
+ /* Show comment */
+ //textbuffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(CommentView));
+ if (FileTag && FileTag->comment)
+ {
+ gchar *tmp = ET_Utf8_Validate_Full_String(FileTag->comment);
+ //gtk_text_buffer_set_text(GTK_TEXT_BUFFER(textbuffer), FileTag->comment, -1);
+ gtk_entry_set_text(GTK_ENTRY(CommentEntry), tmp);
+ g_free(tmp);
+ }else
+ //gtk_text_buffer_set_text(GTK_TEXT_BUFFER(textbuffer), "", -1);
+ gtk_entry_set_text(GTK_ENTRY(CommentEntry),"");
+
+ /* Show composer */
+ if (FileTag && FileTag->composer)
+ {
+ gchar *tmp = ET_Utf8_Validate_Full_String(FileTag->composer);
+ gtk_entry_set_text(GTK_ENTRY(ComposerEntry), tmp);
+ g_free(tmp);
+ }else
+ gtk_entry_set_text(GTK_ENTRY(ComposerEntry),"");
+
+ /* Show original artist */
+ if (FileTag && FileTag->orig_artist)
+ {
+ gchar *tmp = ET_Utf8_Validate_Full_String(FileTag->orig_artist);
+ gtk_entry_set_text(GTK_ENTRY(OrigArtistEntry), tmp);
+ g_free(tmp);
+ }else
+ gtk_entry_set_text(GTK_ENTRY(OrigArtistEntry),"");
+
+ /* Show copyright */
+ if (FileTag && FileTag->copyright)
+ {
+ gchar *tmp = ET_Utf8_Validate_Full_String(FileTag->copyright);
+ gtk_entry_set_text(GTK_ENTRY(CopyrightEntry), tmp);
+ g_free(tmp);
+ }else
+ gtk_entry_set_text(GTK_ENTRY(CopyrightEntry),"");
+
+ /* Show URL */
+ if (FileTag && FileTag->url)
+ {
+ gchar *tmp = ET_Utf8_Validate_Full_String(FileTag->url);
+ gtk_entry_set_text(GTK_ENTRY(URLEntry), tmp);
+ g_free(tmp);
+ }else
+ gtk_entry_set_text(GTK_ENTRY(URLEntry),"");
+
+ /* Show Encoded by */
+ if (FileTag && FileTag->encoded_by)
+ {
+ gchar *tmp = ET_Utf8_Validate_Full_String(FileTag->encoded_by);
+ gtk_entry_set_text(GTK_ENTRY(EncodedByEntry), tmp);
+ g_free(tmp);
+ }else
+ gtk_entry_set_text(GTK_ENTRY(EncodedByEntry),"");
+
+ /* Show picture */
+ PictureEntry_Clear();
+ if (FileTag && FileTag->picture)
+ {
+ Picture *pic = FileTag->picture;
+ guint nbr_pic = 0;
+ GtkWidget *page;
+ gchar *string;
+
+ PictureEntry_Update(FileTag->picture, 0);
+
+ // Count the number of items
+ while (pic)
+ {
+ nbr_pic++;
+ pic = pic->next;
+ }
+
+ // Get page "Pictures" of the notebook
+ page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(TagNoteBook),1);
+ string = g_strdup_printf(_("Pictures (%d)"),nbr_pic);
+ // Update the notebook tab
+ gtk_notebook_set_tab_label_text(GTK_NOTEBOOK(TagNoteBook),page,string);
+ // Update the notebook menu
+ gtk_notebook_set_menu_label_text(GTK_NOTEBOOK(TagNoteBook),page,string);
+ g_free(string);
+
+ }else
+ {
+ GtkWidget *page;
+ // Get page "Pictures" of the notebook
+ page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(TagNoteBook),1);
+ // Update the notebook tab
+ gtk_notebook_set_tab_label_text(GTK_NOTEBOOK(TagNoteBook),page,"Pictures");
+ // Update the notebook menu
+ gtk_notebook_set_menu_label_text(GTK_NOTEBOOK(TagNoteBook),page,"Pictures");
+ }
+
+ return TRUE;
+}
+
+
+/*
+ * "Default" way to display File Info to the user interface.
+ */
+gboolean ET_Display_File_Info_To_UI(ET_File_Info *ETFileInfo)
+{
+ gchar *text;
+ gchar *time = NULL;
+ gchar *time1 = NULL;
+ gchar *size = NULL;
+ gchar *size1 = NULL;
+
+ /* MPEG, Layer versions */
+ text = g_strdup_printf("%d, Layer %d",ETFileInfo->version,ETFileInfo->layer);
+ gtk_label_set_text(GTK_LABEL(VersionValueLabel),text);
+ g_free(text);
+
+ /* Bitrate */
+ text = g_strdup_printf(_("%d kb/s"),ETFileInfo->bitrate);
+ gtk_label_set_text(GTK_LABEL(BitrateValueLabel),text);
+ g_free(text);
+
+ /* Samplerate */
+ text = g_strdup_printf(_("%d Hz"),ETFileInfo->samplerate);
+ gtk_label_set_text(GTK_LABEL(SampleRateValueLabel),text);
+ g_free(text);
+
+ /* Mode */
+ text = g_strdup_printf("%d",ETFileInfo->mode);
+ gtk_label_set_text(GTK_LABEL(ModeValueLabel),text);
+ g_free(text);
+
+ /* Size */
+ size = Convert_Size(ETFileInfo->size);
+ size1 = Convert_Size(ETCore->ETFileDisplayedList_TotalSize);
+ text = g_strdup_printf("%s (%s)",size,size1);
+ gtk_label_set_text(GTK_LABEL(SizeValueLabel),text);
+ g_free(size);
+ g_free(size1);
+ g_free(text);
+
+ /* Duration */
+ time = Convert_Duration(ETFileInfo->duration);
+ time1 = Convert_Duration(ETCore->ETFileDisplayedList_TotalDuration);
+ text = g_strdup_printf("%s (%s)",time,time1);
+ gtk_label_set_text(GTK_LABEL(DurationValueLabel),text);
+ g_free(time);
+ g_free(time1);
+ g_free(text);
+
+ return TRUE;
+}
+
+
+
+
+/********************
+ * Saving functions *
+ ********************/
+
+/*
+ * Save informations of the file, contained into the entries of the user interface, in the list.
+ * An undo key is generated to be used for filename and tag if there are changed is the same time.
+ * Filename and Tag.
+ */
+void ET_Save_File_Data_From_UI (ET_File *ETFile)
+{
+ ET_File_Description *ETFileDescription;
+ File_Name *FileName;
+ File_Tag *FileTag;
+ guint undo_key;
+ gchar *cur_filename_utf8;
+
+ if (!ETFile ||
+ !ETFile->FileNameCur ||
+ !ETFile->FileNameCur->data)
+ return;
+
+ cur_filename_utf8 = ((File_Name *)((GList *)ETFile->FileNameCur)->data)->value_utf8;
+ ETFileDescription = ETFile->ETFileDescription;
+ undo_key = ET_Undo_Key_New();
+
+
+ /*
+ * Save filename and generate undo for filename
+ */
+ FileName = g_malloc0(sizeof(File_Name));
+ ET_Initialize_File_Name_Item(FileName);
+ FileName->key = undo_key;
+ ET_Save_File_Name_From_UI(ETFile,FileName); // Used for all files!
+
+ /*
+ * Save tag data and generate undo for tag
+ */
+ FileTag = g_malloc0(sizeof(File_Tag));
+ ET_Initialize_File_Tag_Item(FileTag);
+ FileTag->key = undo_key;
+
+ switch (ETFileDescription->TagType)
+ {
+#ifdef ENABLE_MP3
+ case ID3_TAG:
+#endif
+#ifdef ENABLE_OGG
+ case OGG_TAG:
+#endif
+#ifdef ENABLE_FLAC
+ case FLAC_TAG:
+#endif
+#ifdef ENABLE_MP4
+ case MP4_TAG:
+#endif
+#ifdef ENABLE_WAVPACK
+ case WAVPACK_TAG:
+#endif
+ case APE_TAG:
+ ET_Save_File_Tag_From_UI(FileTag);
+ ET_Copy_File_Tag_Item_Other_Field(ETFile,FileTag);
+ break;
+ case UNKNOWN_TAG:
+ default:
+ Log_Print("FileTag: Undefined tag type %d for file %s.",ETFileDescription->TagType,cur_filename_utf8);
+ break;
+ }
+
+ /*
+ * Generate undo for the file and the main undo list.
+ * If no changes detected, FileName and FileTag item are deleted.
+ */
+ ET_Manage_Changes_Of_File_Data(ETFile,FileName,FileTag);
+
+ /* Refresh file into browser list */
+ Browser_List_Refresh_File_In_List(ETFile);
+}
+
+
+/*
+ * Save displayed filename into list if it had been changed. Generates also an history list for undo/redo.
+ * - ETFile : the current etfile that we want to save,
+ * - FileName : where is 'temporary' saved the new filename.
+ *
+ * Note : it builds new filename (with path) from strings encoded into file system
+ * encoding, not UTF-8 (it preserves file system encoding of parent directories).
+ */
+gboolean ET_Save_File_Name_From_UI (ET_File *ETFile, File_Name *FileName)
+{
+ gchar *filename_new = NULL;
+ gchar *dirname = NULL;
+ gchar *filename;
+ gchar *filename_utf8;
+ gchar *extension;
+
+ if (!ETFile || !FileName)
+ return FALSE;
+
+ filename_utf8 = (gchar *)gtk_entry_get_text(GTK_ENTRY(FileEntry));
+ filename = filename_from_display(filename_utf8);
+ if (!filename)
+ {
+ // If translation fails...
+ GtkWidget *msgbox;
+ gchar *msg;
+ gchar *filename_escaped_utf8 = g_strescape(filename_utf8, NULL);
+ msg = g_strdup_printf(_("Could not convert filename : '%s'\n"
+ "into system filename encoding\n"
+ "(Try setting the environment variable G_FILENAME_ENCODING)."), filename_escaped_utf8);
+ msgbox = msg_box_new(_("Filename translation"),msg,GTK_STOCK_DIALOG_ERROR,BUTTON_OK,0);
+ msg_box_hide_check_button(MSG_BOX(msgbox));
+ msg_box_run(MSG_BOX(msgbox));
+ gtk_widget_destroy(msgbox);
+ g_free(msg);
+ g_free(filename);
+ g_free(filename_escaped_utf8);
+ return FALSE;
+ }
+
+ // Get the current path to the file
+ dirname = g_path_get_dirname( ((File_Name *)ETFile->FileNameNew->data)->value );
+
+ // Convert filename extension (lower or upper)
+ extension = ET_File_Name_Format_Extension(ETFile);
+
+ // Check length of filename (limit ~255 characters)
+ //ET_File_Name_Check_Length(ETFile,filename);
+
+ // Filename (in file system encoding!)
+ if (filename && strlen(filename)>0)
+ {
+ // Regenerate the new filename (without path)
+ filename_new = g_strconcat(filename,extension,NULL);
+ }else
+ {
+ // Keep the 'last' filename (if a 'blank' filename was entered in the fileentry for ex)...
+ filename_new = g_path_get_basename( ((File_Name *)ETFile->FileNameNew->data)->value );
+ }
+ g_free(extension);
+ g_free(filename);
+
+ // Check if new filename seems to be correct
+ if ( !filename_new || strlen(filename_new) <= strlen(ETFile->ETFileDescription->Extension) )
+ {
+ FileName->value = NULL;
+ FileName->value_utf8 = NULL;
+ FileName->value_ck = NULL;
+
+ g_free(filename_new);
+ g_free(dirname);
+ return FALSE;
+ }
+
+ // Convert the illegal characters
+ ET_File_Name_Convert_Character(filename_new); // FIX ME : should be in UTF8?
+
+ // Set the new file name (in file system encoding)
+ FileName->value = g_strconcat(dirname,G_DIR_SEPARATOR_S,filename_new,NULL);
+ // Set the new file name (in UTF-8 encoding)
+ FileName->value_utf8 = filename_to_display(FileName->value);
+ // Calculates collate key
+ FileName->value_ck = g_utf8_collate_key_for_filename(FileName->value_utf8, -1);
+
+ g_free(filename_new);
+ g_free(dirname);
+ return TRUE;
+}
+
+
+/*
+ * Do the same thing of ET_Save_File_Name_From_UI, but without getting the
+ * data from the UI.
+ */
+gboolean ET_Save_File_Name_Internal (ET_File *ETFile, File_Name *FileName)
+{
+ gchar *filename_new = NULL;
+ gchar *dirname = NULL;
+ gchar *filename;
+ gchar *extension;
+ gchar *pos;
+
+
+ if (!ETFile || !FileName)
+ return FALSE;
+
+ // Get the current path to the file
+ dirname = g_path_get_dirname( ((File_Name *)ETFile->FileNameNew->data)->value );
+
+ // Get the name of file (and rebuild it with extension with a 'correct' case)
+ filename = g_path_get_basename( ((File_Name *)ETFile->FileNameNew->data)->value );
+
+ // Remove the extension
+ if ((pos=strrchr(filename, '.'))!=NULL)
+ *pos = 0;
+
+ // Convert filename extension (lower/upper)
+ extension = ET_File_Name_Format_Extension(ETFile);
+
+ // Check length of filename
+ //ET_File_Name_Check_Length(ETFile,filename);
+
+ // Regenerate the new filename (without path)
+ filename_new = g_strconcat(filename,extension,NULL);
+ g_free(extension);
+ g_free(filename);
+
+ // Check if new filename seems to be correct
+ if (filename_new)
+ {
+ // Convert the illegal characters
+ ET_File_Name_Convert_Character(filename_new);
+
+ // Set the new file name (in file system encoding)
+ FileName->value = g_strconcat(dirname,G_DIR_SEPARATOR_S,filename_new,NULL);
+ // Set the new file name (in UTF-8 encoding)
+ FileName->value_utf8 = filename_to_display(FileName->value);
+ // Calculate collate key
+ FileName->value_ck = g_utf8_collate_key_for_filename(FileName->value_utf8, -1);
+
+ g_free(filename_new);
+ g_free(dirname);
+ return TRUE;
+ }else
+ {
+ FileName->value = NULL;
+ FileName->value_utf8 = NULL;
+ FileName->value_ck = NULL;
+
+ g_free(filename_new);
+ g_free(dirname);
+ return FALSE;
+ }
+}
+
+
+/*
+ * Load values, entered into entries of the UI, into a File_Tag structure which had been newly created.
+ */
+gboolean ET_Save_File_Tag_From_UI (File_Tag *FileTag)
+{
+ gchar *buffer = NULL;
+ //GtkTextBuffer *textbuffer;
+ //GtkTextIter start_iter;
+ //GtkTextIter end_iter;
+
+ if (!FileTag)
+ return FALSE;
+
+ /* Title */
+ buffer = g_strdup(gtk_entry_get_text(GTK_ENTRY(TitleEntry)));
+ Strip_String(buffer);
+
+ if ( g_utf8_strlen(buffer, -1) > 0 )
+ FileTag->title = buffer;
+ else
+ {
+ FileTag->title = NULL;
+ g_free(buffer);
+ }
+
+ /* Artist */
+ buffer = g_strdup(gtk_entry_get_text(GTK_ENTRY(ArtistEntry)));
+ Strip_String(buffer);
+
+ if ( g_utf8_strlen(buffer, -1) > 0 )
+ FileTag->artist = buffer;
+ else
+ {
+ FileTag->artist = NULL;
+ g_free(buffer);
+ }
+
+ /* Album */
+ buffer = g_strdup(gtk_entry_get_text(GTK_ENTRY(AlbumEntry)));
+ Strip_String(buffer);
+
+ if ( g_utf8_strlen(buffer, -1) > 0 )
+ FileTag->album = buffer;
+ else
+ {
+ FileTag->album = NULL;
+ g_free(buffer);
+ }
+
+ /* Disc Number */
+ buffer = g_strdup(gtk_entry_get_text(GTK_ENTRY(DiscNumberEntry)));
+ Strip_String(buffer);
+
+ if ( g_utf8_strlen(buffer, -1) > 0 )
+ FileTag->disc_number = buffer;
+ else
+ {
+ FileTag->disc_number = NULL;
+ g_free(buffer);
+ }
+
+ /* Year */
+ buffer = g_strdup(gtk_entry_get_text(GTK_ENTRY(YearEntry)));
+ Strip_String(buffer);
+
+ if ( g_utf8_strlen(buffer, -1) > 0 )
+ FileTag->year = buffer;
+ else
+ {
+ FileTag->year = NULL;
+ g_free(buffer);
+ }
+
+ /* Track */
+ buffer = g_strdup(gtk_entry_get_text(GTK_ENTRY(GTK_BIN(TrackEntryCombo)->child)));
+ Strip_String(buffer);
+
+ if ( g_utf8_strlen(buffer, -1) > 0 )
+ {
+ if (NUMBER_TRACK_FORMATED) {
+ FileTag->track = g_strdup_printf("%.*d",NUMBER_TRACK_FORMATED_SPIN_BUTTON,atoi(buffer));
+ g_free(buffer);
+ } else
+ FileTag->track = buffer;
+ } else
+ {
+ FileTag->track = NULL;
+ g_free(buffer);
+ }
+
+ /* Track Total */
+ buffer = g_strdup(gtk_entry_get_text(GTK_ENTRY(TrackTotalEntry)));
+ Strip_String(buffer);
+
+ if ( g_utf8_strlen(buffer, -1) > 0 )
+ {
+ if (NUMBER_TRACK_FORMATED)
+ {
+ FileTag->track_total = g_strdup_printf("%.*d",NUMBER_TRACK_FORMATED_SPIN_BUTTON,atoi(buffer));
+ g_free(buffer);
+ } else
+ FileTag->track_total = buffer;
+ } else
+ {
+ FileTag->track_total = NULL;
+ g_free(buffer);
+ }
+
+ /* Genre */
+ buffer = g_strdup(gtk_entry_get_text(GTK_ENTRY(GTK_BIN(GenreCombo)->child)));
+ Strip_String(buffer);
+
+ if ( g_utf8_strlen(buffer, -1) > 0 )
+ FileTag->genre = buffer;
+ else
+ {
+ FileTag->genre = NULL;
+ g_free(buffer);
+ }
+
+ /* Comment */
+ //textbuffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(CommentView));
+ //gtk_text_buffer_get_bounds(GTK_TEXT_BUFFER(textbuffer),&start_iter,&end_iter);
+ //buffer = gtk_text_buffer_get_text(GTK_TEXT_BUFFER(textbuffer),&start_iter,&end_iter,TRUE);
+ buffer = g_strdup(gtk_entry_get_text(GTK_ENTRY(CommentEntry)));
+ Strip_String(buffer);
+
+ if ( g_utf8_strlen(buffer, -1) > 0 )
+ FileTag->comment = buffer;
+ else
+ {
+ FileTag->comment = NULL;
+ g_free(buffer);
+ }
+
+ /* Composer */
+ buffer = g_strdup(gtk_entry_get_text(GTK_ENTRY(ComposerEntry)));
+ Strip_String(buffer);
+
+ if ( g_utf8_strlen(buffer, -1) > 0 )
+ FileTag->composer = buffer;
+ else
+ {
+ FileTag->composer = NULL;
+ g_free(buffer);
+ }
+
+ /* Original Artist */
+ buffer = g_strdup(gtk_entry_get_text(GTK_ENTRY(OrigArtistEntry)));
+ Strip_String(buffer);
+
+ if ( g_utf8_strlen(buffer, -1) > 0 )
+ FileTag->orig_artist = buffer;
+ else
+ {
+ FileTag->orig_artist = NULL;
+ g_free(buffer);
+ }
+
+ /* Copyright */
+ buffer = g_strdup(gtk_entry_get_text(GTK_ENTRY(CopyrightEntry)));
+ Strip_String(buffer);
+
+ if ( g_utf8_strlen(buffer, -1) > 0 )
+ FileTag->copyright = buffer;
+ else
+ {
+ FileTag->copyright = NULL;
+ g_free(buffer);
+ }
+
+ /* URL */
+ buffer = g_strdup(gtk_entry_get_text(GTK_ENTRY(URLEntry)));
+ Strip_String(buffer);
+
+ if ( g_utf8_strlen(buffer, -1) > 0 )
+ FileTag->url = buffer;
+ else
+ {
+ FileTag->url = NULL;
+ g_free(buffer);
+ }
+
+ /* Encoded by */
+ buffer = g_strdup(gtk_entry_get_text(GTK_ENTRY(EncodedByEntry)));
+ Strip_String(buffer);
+
+ if ( g_utf8_strlen(buffer, -1) > 0 )
+ FileTag->encoded_by = buffer;
+ else
+ {
+ FileTag->encoded_by = NULL;
+ g_free(buffer);
+ }
+
+ /* Picture */
+ {
+ Picture *pic, *prev_pic = NULL;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ if (FileTag->picture)
+ {
+ Picture_Free(FileTag->picture);
+ FileTag->picture = NULL;
+ }
+
+ model = gtk_tree_view_get_model(GTK_TREE_VIEW(PictureEntryView));
+ if (gtk_tree_model_get_iter_first(model, &iter))
+ {
+ do
+ {
+ gtk_tree_model_get(model, &iter, PICTURE_COLUMN_DATA, &pic,-1);
+ pic = Picture_Copy_One(pic);
+ if (!FileTag->picture)
+ FileTag->picture = pic;
+ else
+ prev_pic->next = pic;
+ prev_pic = pic;
+ } while (gtk_tree_model_iter_next(model, &iter));
+ }
+ }
+
+ return TRUE;
+}
+
+
+/*
+ * Do the same thing of ET_Save_File_Tag_From_UI without getting the data from the UI.
+ */
+gboolean ET_Save_File_Tag_Internal (ET_File *ETFile, File_Tag *FileTag)
+{
+ File_Tag *FileTagCur;
+
+
+ if (!ETFile || !ETFile->FileTag || !FileTag)
+ return FALSE;
+
+ FileTagCur = (File_Tag *)ETFile->FileTag->data;
+
+ /* Title */
+ if ( FileTagCur->title && g_utf8_strlen(FileTagCur->title, -1)>0 )
+ {
+ FileTag->title = g_strdup(FileTagCur->title);
+ Strip_String(FileTag->title);
+ } else
+ {
+ FileTag->title = NULL;
+ }
+
+ /* Artist */
+ if ( FileTagCur->artist && g_utf8_strlen(FileTagCur->artist, -1)>0 )
+ {
+ FileTag->artist = g_strdup(FileTagCur->artist);
+ Strip_String(FileTag->artist);
+ } else
+ {
+ FileTag->artist = NULL;
+ }
+
+
+ /* Album */
+ if ( FileTagCur->album && g_utf8_strlen(FileTagCur->album, -1)>0 )
+ {
+ FileTag->album = g_strdup(FileTagCur->album);
+ Strip_String(FileTag->album);
+ } else
+ {
+ FileTag->album = NULL;
+ }
+
+
+ /* Disc Number */
+ if ( FileTagCur->disc_number && g_utf8_strlen(FileTagCur->disc_number, -1)>0 )
+ {
+ FileTag->disc_number = g_strdup(FileTagCur->disc_number);
+ Strip_String(FileTag->disc_number);
+ } else
+ {
+ FileTag->disc_number = NULL;
+ }
+
+
+ /* Year */
+ if ( FileTagCur->year && g_utf8_strlen(FileTagCur->year, -1)>0 )
+ {
+ FileTag->year = g_strdup(FileTagCur->year);
+ Strip_String(FileTag->year);
+ } else
+ {
+ FileTag->year = NULL;
+ }
+
+
+ /* Track */
+ if ( FileTagCur->track && g_utf8_strlen(FileTagCur->track, -1)>0 )
+ {
+ gchar *tmp_str;
+
+ if (NUMBER_TRACK_FORMATED)
+ FileTag->track = g_strdup_printf("%.*d",NUMBER_TRACK_FORMATED_SPIN_BUTTON,atoi(FileTagCur->track));
+ else
+ FileTag->track = g_strdup(FileTagCur->track);
+ // This field must contain only digits
+ tmp_str = FileTag->track;
+ while (isdigit((guchar)*tmp_str)) tmp_str++;
+ *tmp_str = 0;
+ Strip_String(FileTag->track);
+ } else
+ {
+ FileTag->track = NULL;
+ }
+
+
+ /* Track Total */
+ if ( FileTagCur->track_total && g_utf8_strlen(FileTagCur->track_total, -1)>0 )
+ {
+ if (NUMBER_TRACK_FORMATED)
+ FileTag->track_total = g_strdup_printf("%.*d",NUMBER_TRACK_FORMATED_SPIN_BUTTON,atoi(FileTagCur->track_total));
+ else
+ FileTag->track_total = g_strdup(FileTagCur->track_total);
+ Strip_String(FileTag->track_total);
+ } else
+ {
+ FileTag->track_total = NULL;
+ }
+
+
+ /* Genre */
+ if ( FileTagCur->genre && g_utf8_strlen(FileTagCur->genre, -1)>0 )
+ {
+ FileTag->genre = g_strdup(FileTagCur->genre);
+ Strip_String(FileTag->genre);
+ } else
+ {
+ FileTag->genre = NULL;
+ }
+
+
+ /* Comment */
+ if ( FileTagCur->comment && g_utf8_strlen(FileTagCur->comment, -1)>0 )
+ {
+ FileTag->comment = g_strdup(FileTagCur->comment);
+ Strip_String(FileTag->comment);
+ } else
+ {
+ FileTag->comment = NULL;
+ }
+
+
+ /* Composer */
+ if ( FileTagCur->composer && g_utf8_strlen(FileTagCur->composer, -1)>0 )
+ {
+ FileTag->composer = g_strdup(FileTagCur->composer);
+ Strip_String(FileTag->composer);
+ } else
+ {
+ FileTag->composer = NULL;
+ }
+
+
+ /* Original Artist */
+ if ( FileTagCur->orig_artist && g_utf8_strlen(FileTagCur->orig_artist, -1)>0 )
+ {
+ FileTag->orig_artist = g_strdup(FileTagCur->orig_artist);
+ Strip_String(FileTag->orig_artist);
+ } else
+ {
+ FileTag->orig_artist = NULL;
+ }
+
+
+ /* Copyright */
+ if ( FileTagCur->copyright && g_utf8_strlen(FileTagCur->copyright, -1)>0 )
+ {
+ FileTag->copyright = g_strdup(FileTagCur->copyright);
+ Strip_String(FileTag->copyright);
+ } else
+ {
+ FileTag->copyright = NULL;
+ }
+
+
+ /* URL */
+ if ( FileTagCur->url && g_utf8_strlen(FileTagCur->url, -1)>0 )
+ {
+ FileTag->url = g_strdup(FileTagCur->url);
+ Strip_String(FileTag->url);
+ } else
+ {
+ FileTag->url = NULL;
+ }
+
+
+ /* Encoded by */
+ if ( FileTagCur->encoded_by && g_utf8_strlen(FileTagCur->encoded_by, -1)>0 )
+ {
+ FileTag->encoded_by = g_strdup(FileTagCur->encoded_by);
+ Strip_String(FileTag->encoded_by);
+ } else
+ {
+ FileTag->encoded_by = NULL;
+ }
+
+
+ /* Picture */
+ if(FileTagCur->picture)
+ {
+ if (FileTag->picture)
+ Picture_Free(FileTag->picture);
+ FileTag->picture = Picture_Copy(FileTagCur->picture);
+ }else if (FileTag->picture)
+ {
+ Picture_Free(FileTag->picture);
+ FileTag->picture = NULL;
+ }
+
+ return TRUE;
+}
+
+
+/*
+ * Save data contained into File_Tag structure to the file on hard disk.
+ */
+gboolean ET_Save_File_Tag_To_HD (ET_File *ETFile)
+{
+ ET_File_Description *ETFileDescription;
+ gchar *cur_filename;
+ gchar *cur_filename_utf8;
+ gboolean state;
+ struct stat statbuf;
+ struct utimbuf utimbufbuf;
+ gboolean file_set_properties;
+
+ if (!ETFile) return FALSE;
+
+ cur_filename = ((File_Name *)(ETFile->FileNameCur)->data)->value;
+ cur_filename_utf8 = ((File_Name *)(ETFile->FileNameCur)->data)->value_utf8;
+
+ ETFileDescription = ETFile->ETFileDescription;
+
+ // Save permissions of the file (cause they may change with files on NFS)
+ if ( stat(cur_filename,&statbuf)!=-1 )
+ file_set_properties = TRUE;
+ else
+ file_set_properties = FALSE;
+
+ switch (ETFileDescription->TagType)
+ {
+#ifdef ENABLE_MP3
+ case ID3_TAG:
+ state = Id3tag_Write_File_Tag(ETFile);
+ break;
+#endif
+#ifdef ENABLE_OGG
+ case OGG_TAG:
+ state = Ogg_Tag_Write_File_Tag(ETFile);
+ break;
+#endif
+#ifdef ENABLE_FLAC
+ case FLAC_TAG:
+ state = Flac_Tag_Write_File_Tag(ETFile);
+ break;
+#endif
+ case APE_TAG:
+ state = Ape_Tag_Write_File_Tag(ETFile);
+ break;
+#ifdef ENABLE_MP4
+ case MP4_TAG:
+ state = Mp4tag_Write_File_Tag(ETFile);
+ break;
+#endif
+#ifdef ENABLE_WAVPACK
+ case WAVPACK_TAG:
+ state = Wavpack_Tag_Write_File_Tag(ETFile);
+ break;
+#endif
+ case UNKNOWN_TAG:
+ default:
+ Log_Print("Saving to HD: Undefined function for tag type '%d' (file %s).",
+ ETFileDescription->TagType,cur_filename_utf8);
+ state = FALSE;
+ break;
+ }
+
+ // Update properties for the file
+ if ( file_set_properties == TRUE )
+ {
+#ifndef WIN32
+ chmod(cur_filename,statbuf.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO));
+ chown(cur_filename,statbuf.st_uid,statbuf.st_gid);
+#endif
+ if (PRESERVE_MODIFICATION_TIME)
+ {
+ utimbufbuf.actime = statbuf.st_atime; // Last access time
+ utimbufbuf.modtime = statbuf.st_mtime; // Last modification time
+ utime(cur_filename,&utimbufbuf);
+ }
+ }
+
+ if (state==TRUE)
+ {
+ ET_Mark_File_Tag_As_Saved(ETFile);
+ return TRUE;
+ }else
+ {
+ return FALSE;
+ }
+}
+
+/*
+ * Function used to update path of filenames into list after renaming a parent directory
+ * (for ex: "/mp3/old_path/file.mp3" to "/mp3/new_path/file.mp3"
+ */
+void ET_Update_Directory_Name_Into_File_List (gchar* last_path, gchar *new_path)
+{
+ GList *filelist;
+ ET_File *file;
+ GList *filenamelist;
+ gchar *filename;
+ gchar *last_path_tmp;
+
+ if (!ETCore->ETFileList || !last_path || !new_path ||
+ strlen(last_path) < 1 || strlen(new_path) < 1 )
+ return;
+
+ // Add '/' to end of path to avoid ambiguity between a directory and a filename...
+ if (last_path[strlen(last_path)-1]==G_DIR_SEPARATOR)
+ last_path_tmp = g_strdup(last_path);
+ else
+ last_path_tmp = g_strconcat(last_path,G_DIR_SEPARATOR_S,NULL);
+
+ filelist = g_list_first(ETCore->ETFileList);
+ while (filelist)
+ {
+ if ( (file=filelist->data) && (filenamelist=file->FileNameList) )
+ {
+ while (filenamelist)
+ {
+ File_Name *FileName = (File_Name *)filenamelist->data;
+
+ if ( FileName && (filename=FileName->value) )
+ {
+ // Replace path of filename
+ if ( strncmp(filename,last_path_tmp,strlen(last_path_tmp))==0 )
+ {
+ gchar *filename_tmp;
+
+ // Create the new filename
+ filename_tmp = g_strconcat(new_path,
+ (new_path[strlen(new_path)-1]==G_DIR_SEPARATOR) ? "" : G_DIR_SEPARATOR_S,
+ &filename[strlen(last_path_tmp)],NULL);
+
+ // Replace the file name (in file system encoding)
+ g_free(FileName->value);
+ FileName->value = filename_tmp;
+ // Replace the file name (in file system encoding)
+ g_free(FileName->value_utf8);
+ FileName->value_utf8 = filename_to_display(FileName->value);
+ // Recalculate the collate key
+ g_free(FileName->value_ck);
+ FileName->value_ck = g_utf8_collate_key_for_filename(FileName->value_utf8, -1);
+ }
+ }
+ filenamelist = g_list_next(filenamelist);
+ }
+ }
+ filelist = g_list_next(filelist);
+ }
+
+ g_free(last_path_tmp);
+}
+
+
+
+/***********************
+ * Undo/Redo functions *
+ ***********************/
+
+/*
+ * Check if 'FileName' and 'FileTag' differ with those of 'ETFile'.
+ * Manage undo feature for the ETFile and the main undo list.
+ */
+gboolean ET_Manage_Changes_Of_File_Data (ET_File *ETFile, File_Name *FileName, File_Tag *FileTag)
+{
+ gboolean undo_added = FALSE;
+
+ if (!ETFile) return FALSE;
+
+ /*
+ * Detect changes of filename and generate the filename undo list
+ */
+ if (FileName)
+ {
+ if ( ETFile->FileNameNew && ET_Detect_Changes_Of_File_Name( (File_Name *)(ETFile->FileNameNew)->data,FileName )==TRUE )
+ {
+ ET_Add_File_Name_To_List(ETFile,FileName);
+ undo_added |= TRUE;
+ }else
+ {
+ ET_Free_File_Name_Item(FileName);
+ }
+ }
+
+ /*
+ * Detect changes in tag data and generate the tag undo list
+ */
+ if (FileTag)
+ {
+ if ( ETFile->FileTag && ET_Detect_Changes_Of_File_Tag( (File_Tag *)(ETFile->FileTag)->data,FileTag )==TRUE )
+ {
+ ET_Add_File_Tag_To_List(ETFile,FileTag);
+ undo_added |= TRUE;
+ }else
+ {
+ ET_Free_File_Tag_Item(FileTag);
+ }
+ }
+
+ /*
+ * Generate main undo (file history of modifications)
+ */
+ if (undo_added)
+ ET_Add_File_To_History_List(ETFile);
+
+ return TRUE; //undo_added;
+}
+
+
+/*
+ * Compares two File_Name items :
+ * - returns TRUE if there aren't the same
+ * - else returns FALSE
+ */
+gboolean ET_Detect_Changes_Of_File_Name (File_Name *FileName1, File_Name *FileName2)
+{
+ gchar *filename1_ck;
+ gchar *filename2_ck;
+
+ if ( (!FileName1 && !FileName2)
+ || (!FileName1->value && !FileName2->value) )
+ return FALSE;
+
+ if ( ( FileName1 && !FileName2)
+ || (!FileName1 && FileName2)
+ || ( FileName1->value && !FileName2->value)
+ || (!FileName1->value && FileName2->value) )
+ return TRUE;
+
+ // Compare collate keys (with FileName->value converted to UTF-8 as it contains raw data)
+ filename1_ck = FileName1->value_ck;
+ filename2_ck = FileName2->value_ck;
+
+ // Filename changed ? (we check path + file)
+ if ( strcmp(filename1_ck,filename2_ck) != 0 )
+ {
+ return TRUE;
+ }else
+ {
+ // No changes
+ return FALSE;
+ }
+}
+
+/*
+ * Compares two File_Tag items and returns TRUE if there aren't the same.
+ * Notes:
+ * - if field is '' or NULL => will be removed
+ */
+gboolean ET_Detect_Changes_Of_File_Tag (File_Tag *FileTag1, File_Tag *FileTag2)
+{
+ Picture *pic1;
+ Picture *pic2;
+
+ if ( !FileTag1 && !FileTag2 )
+ return FALSE;
+
+ if ( ( FileTag1 && !FileTag2)
+ || (!FileTag1 && FileTag2) )
+ return TRUE;
+
+ /* Title */
+ if ( FileTag1->title && !FileTag2->title && g_utf8_strlen(FileTag1->title, -1)>0 ) return TRUE;
+ if (!FileTag1->title && FileTag2->title && g_utf8_strlen(FileTag2->title, -1)>0 ) return TRUE;
+ if ( FileTag1->title && FileTag2->title && g_utf8_collate(FileTag1->title,FileTag2->title)!=0 ) return TRUE;
+
+ /* Artist */
+ if ( FileTag1->artist && !FileTag2->artist && g_utf8_strlen(FileTag1->artist, -1)>0 ) return TRUE;
+ if (!FileTag1->artist && FileTag2->artist && g_utf8_strlen(FileTag2->artist, -1)>0 ) return TRUE;
+ if ( FileTag1->artist && FileTag2->artist && g_utf8_collate(FileTag1->artist,FileTag2->artist)!=0 ) return TRUE;
+
+ /* Album */
+ if ( FileTag1->album && !FileTag2->album && g_utf8_strlen(FileTag1->album, -1)>0 ) return TRUE;
+ if (!FileTag1->album && FileTag2->album && g_utf8_strlen(FileTag2->album, -1)>0 ) return TRUE;
+ if ( FileTag1->album && FileTag2->album && g_utf8_collate(FileTag1->album,FileTag2->album)!=0 ) return TRUE;
+
+ /* Disc Number */
+ if ( FileTag1->disc_number && !FileTag2->disc_number && g_utf8_strlen(FileTag1->disc_number, -1)>0 ) return TRUE;
+ if (!FileTag1->disc_number && FileTag2->disc_number && g_utf8_strlen(FileTag2->disc_number, -1)>0 ) return TRUE;
+ if ( FileTag1->disc_number && FileTag2->disc_number && g_utf8_collate(FileTag1->disc_number,FileTag2->disc_number)!=0 ) return TRUE;
+
+ /* Year */
+ if ( FileTag1->year && !FileTag2->year && g_utf8_strlen(FileTag1->year, -1)>0 ) return TRUE;
+ if (!FileTag1->year && FileTag2->year && g_utf8_strlen(FileTag2->year, -1)>0 ) return TRUE;
+ if ( FileTag1->year && FileTag2->year && g_utf8_collate(FileTag1->year,FileTag2->year)!=0 ) return TRUE;
+
+ /* Track */
+ if ( FileTag1->track && !FileTag2->track && g_utf8_strlen(FileTag1->track, -1)>0 ) return TRUE;
+ if (!FileTag1->track && FileTag2->track && g_utf8_strlen(FileTag2->track, -1)>0 ) return TRUE;
+ if ( FileTag1->track && FileTag2->track && g_utf8_collate(FileTag1->track,FileTag2->track)!=0 ) return TRUE;
+
+ /* Track Total */
+ if ( FileTag1->track_total && !FileTag2->track_total && g_utf8_strlen(FileTag1->track_total, -1)>0 ) return TRUE;
+ if (!FileTag1->track_total && FileTag2->track_total && g_utf8_strlen(FileTag2->track_total, -1)>0 ) return TRUE;
+ if ( FileTag1->track_total && FileTag2->track_total && g_utf8_collate(FileTag1->track_total,FileTag2->track_total)!=0 ) return TRUE;
+
+ /* Genre */
+ if ( FileTag1->genre && !FileTag2->genre && g_utf8_strlen(FileTag1->genre, -1)>0 ) return TRUE;
+ if (!FileTag1->genre && FileTag2->genre && g_utf8_strlen(FileTag2->genre, -1)>0 ) return TRUE;
+ if ( FileTag1->genre && FileTag2->genre && g_utf8_collate(FileTag1->genre,FileTag2->genre)!=0 ) return TRUE;
+
+ /* Comment */
+ if ( FileTag1->comment && !FileTag2->comment && g_utf8_strlen(FileTag1->comment, -1)>0 ) return TRUE;
+ if (!FileTag1->comment && FileTag2->comment && g_utf8_strlen(FileTag2->comment, -1)>0 ) return TRUE;
+ if ( FileTag1->comment && FileTag2->comment && g_utf8_collate(FileTag1->comment,FileTag2->comment)!=0 ) return TRUE;
+
+ /* Composer */
+ if ( FileTag1->composer && !FileTag2->composer && g_utf8_strlen(FileTag1->composer, -1)>0 ) return TRUE;
+ if (!FileTag1->composer && FileTag2->composer && g_utf8_strlen(FileTag2->composer, -1)>0 ) return TRUE;
+ if ( FileTag1->composer && FileTag2->composer && g_utf8_collate(FileTag1->composer,FileTag2->composer)!=0 ) return TRUE;
+
+ /* Original artist */
+ if ( FileTag1->orig_artist && !FileTag2->orig_artist && g_utf8_strlen(FileTag1->orig_artist, -1)>0 ) return TRUE;
+ if (!FileTag1->orig_artist && FileTag2->orig_artist && g_utf8_strlen(FileTag2->orig_artist, -1)>0 ) return TRUE;
+ if ( FileTag1->orig_artist && FileTag2->orig_artist && g_utf8_collate(FileTag1->orig_artist,FileTag2->orig_artist)!=0 ) return TRUE;
+
+ /* Copyright */
+ if ( FileTag1->copyright && !FileTag2->copyright && g_utf8_strlen(FileTag1->copyright, -1)>0 ) return TRUE;
+ if (!FileTag1->copyright && FileTag2->copyright && g_utf8_strlen(FileTag2->copyright, -1)>0 ) return TRUE;
+ if ( FileTag1->copyright && FileTag2->copyright && g_utf8_collate(FileTag1->copyright,FileTag2->copyright)!=0 ) return TRUE;
+
+ /* URL */
+ if ( FileTag1->url && !FileTag2->url && g_utf8_strlen(FileTag1->url, -1)>0 ) return TRUE;
+ if (!FileTag1->url && FileTag2->url && g_utf8_strlen(FileTag2->url, -1)>0 ) return TRUE;
+ if ( FileTag1->url && FileTag2->url && g_utf8_collate(FileTag1->url,FileTag2->url)!=0 ) return TRUE;
+
+ /* Encoded by */
+ if ( FileTag1->encoded_by && !FileTag2->encoded_by && g_utf8_strlen(FileTag1->encoded_by, -1)>0 ) return TRUE;
+ if (!FileTag1->encoded_by && FileTag2->encoded_by && g_utf8_strlen(FileTag2->encoded_by, -1)>0 ) return TRUE;
+ if ( FileTag1->encoded_by && FileTag2->encoded_by && g_utf8_collate(FileTag1->encoded_by,FileTag2->encoded_by)!=0 ) return TRUE;
+
+ /* Picture */
+ pic1 = FileTag1->picture;
+ pic2 = FileTag2->picture;
+
+ for(;;)
+ {
+ if( (pic1 && !pic2) || (!pic1 && pic2) )
+ return TRUE;
+ if (!pic1 || !pic2)
+ break; // => no changes
+ //if (!pic1->data || !pic2->data)
+ // break; // => no changes
+
+ if (pic1->data && !pic2->data)
+ return TRUE;
+ if (!pic1->data && pic2->data)
+ return TRUE;
+ if (pic1->size != pic2->size
+ || memcmp(pic1->data, pic2->data, pic1->size))
+ return TRUE;
+ if (pic1->type != pic2->type)
+ return TRUE;
+ if (pic1->description && !pic2->description
+ && g_utf8_strlen(pic1->description, -1)>0 )
+ return TRUE;
+ if (!pic1->description && pic2->description
+ && g_utf8_strlen(pic2->description, -1)>0 )
+ return TRUE;
+ if (pic1->description && pic2->description
+ && g_utf8_collate(pic1->description, pic2->description)!=0 )
+ return TRUE;
+
+ pic1 = pic1->next;
+ pic2 = pic2->next;
+ }
+
+ return FALSE; /* No changes */
+}
+
+
+/*
+ * Add a FileName item to the history list of ETFile
+ */
+gboolean ET_Add_File_Name_To_List (ET_File *ETFile, File_Name *FileName)
+{
+ GList *cut_list = NULL;
+
+ if (!ETFile || !FileName)
+ return FALSE;
+
+ /* How it works : Cut the FileNameList list after the current item,
+ * and appends it to the FileNameListBak list for saving the data.
+ * Then appends the new item to the FileNameList list */
+ if (ETFile->FileNameList)
+ {
+ cut_list = ETFile->FileNameNew->next; // Cut after the current item...
+ ETFile->FileNameNew->next = NULL;
+ }
+ if (cut_list)
+ cut_list->prev = NULL;
+
+ /* Add the new item to the list */
+ ETFile->FileNameList = g_list_append(ETFile->FileNameList,FileName);
+ /* Set the current item to use */
+ ETFile->FileNameNew = g_list_last(ETFile->FileNameList);
+ /* Backup list */
+ /* FIX ME! Keep only the saved item */
+ ETFile->FileNameListBak = g_list_concat(ETFile->FileNameListBak,cut_list);
+
+ return TRUE;
+}
+
+/*
+ * Add a FileTag item to the history list of ETFile
+ */
+gboolean ET_Add_File_Tag_To_List (ET_File *ETFile, File_Tag *FileTag)
+{
+ GList *cut_list = NULL;
+
+ if (!ETFile || !FileTag)
+ return FALSE;
+
+ if (ETFile->FileTag)
+ {
+ cut_list = ETFile->FileTag->next; // Cut after the current item...
+ ETFile->FileTag->next = NULL;
+ }
+ if (cut_list)
+ cut_list->prev = NULL;
+
+ /* Add the new item to the list */
+ ETFile->FileTagList = g_list_append(ETFile->FileTagList,FileTag);
+ /* Set the current item to use */
+ ETFile->FileTag = g_list_last(ETFile->FileTagList);
+ /* Backup list */
+ ETFile->FileTagListBak = g_list_concat(ETFile->FileTagListBak,cut_list);
+
+ return TRUE;
+}
+
+/*
+ * Add a ETFile item to the main undo list of files
+ */
+gboolean ET_Add_File_To_History_List (ET_File *ETFile)
+{
+ ET_History_File *ETHistoryFile;
+
+ if (!ETFile) return FALSE;
+
+ ETHistoryFile = g_malloc0(sizeof(ET_History_File));
+ ETHistoryFile->ETFile = ETFile;
+
+ /* The undo list must contains one item before the 'first undo' data */
+ if (!ETCore->ETHistoryFileList)
+ ETCore->ETHistoryFileList = g_list_append(ETCore->ETHistoryFileList,g_malloc0(sizeof(ET_History_File)));
+
+ /* Add the item to the list (cut end of list from the current element) */
+ ETCore->ETHistoryFileList = g_list_append(ETCore->ETHistoryFileList,ETHistoryFile);
+ ETCore->ETHistoryFileList = g_list_last(ETCore->ETHistoryFileList);
+
+ return TRUE;
+}
+
+
+/*
+ * Applies one undo to the ETFile data (to reload the previous data).
+ * Returns TRUE if an undo had been applied.
+ */
+gboolean ET_Undo_File_Data (ET_File *ETFile)
+{
+ gboolean has_filename_undo_data = FALSE;
+ gboolean has_filetag_undo_data = FALSE;
+ guint filename_key, filetag_key, undo_key;
+
+ if (!ETFile)
+ return FALSE;
+
+ /* Find the valid key */
+ if (ETFile->FileNameNew->prev && ETFile->FileNameNew->data)
+ filename_key = ((File_Name *)ETFile->FileNameNew->data)->key;
+ else
+ filename_key = 0;
+ if (ETFile->FileTag->prev && ETFile->FileTag->data)
+ filetag_key = ((File_Tag *)ETFile->FileTag->data)->key;
+ else
+ filetag_key = 0;
+ // The key to use
+ undo_key = MAX(filename_key,filetag_key);
+
+ /* Undo filename */
+ if (ETFile->FileNameNew->prev && ETFile->FileNameNew->data
+ && (undo_key==((File_Name *)ETFile->FileNameNew->data)->key))
+ {
+ ETFile->FileNameNew = ETFile->FileNameNew->prev;
+ has_filename_undo_data = TRUE; // To indicate that an undo has been applied
+ }
+
+ /* Undo tag data */
+ if (ETFile->FileTag->prev && ETFile->FileTag->data
+ && (undo_key==((File_Tag *)ETFile->FileTag->data)->key))
+ {
+ ETFile->FileTag = ETFile->FileTag->prev;
+ has_filetag_undo_data = TRUE;
+ }
+
+ return has_filename_undo_data | has_filetag_undo_data;
+}
+
+
+/*
+ * Returns TRUE if file contains undo data (filename or tag)
+ */
+gboolean ET_File_Data_Has_Undo_Data (ET_File *ETFile)
+{
+ gboolean has_filename_undo_data = FALSE;
+ gboolean has_filetag_undo_data = FALSE;
+
+ if (!ETFile) return FALSE;
+
+ if (ETFile->FileNameNew && ETFile->FileNameNew->prev) has_filename_undo_data = TRUE;
+ if (ETFile->FileTag && ETFile->FileTag->prev) has_filetag_undo_data = TRUE;
+
+ return has_filename_undo_data | has_filetag_undo_data;
+}
+
+
+/*
+ * Applies one redo to the ETFile data. Returns TRUE if a redo had been applied.
+ */
+gboolean ET_Redo_File_Data (ET_File *ETFile)
+{
+ gboolean has_filename_redo_data = FALSE;
+ gboolean has_filetag_redo_data = FALSE;
+ guint filename_key, filetag_key, undo_key;
+
+ if (!ETFile)
+ return FALSE;
+
+ /* Find the valid key */
+ if (ETFile->FileNameNew->next && ETFile->FileNameNew->next->data)
+ filename_key = ((File_Name *)ETFile->FileNameNew->next->data)->key;
+ else
+ filename_key = (guint)~0; // To have the max value for guint
+ if (ETFile->FileTag->next && ETFile->FileTag->next->data)
+ filetag_key = ((File_Tag *)ETFile->FileTag->next->data)->key;
+ else
+ filetag_key = (guint)~0; // To have the max value for guint
+ // The key to use
+ undo_key = MIN(filename_key,filetag_key);
+
+ /* Redo filename */
+ if (ETFile->FileNameNew->next && ETFile->FileNameNew->next->data
+ && (undo_key==((File_Name *)ETFile->FileNameNew->next->data)->key))
+ {
+ ETFile->FileNameNew = ETFile->FileNameNew->next;
+ has_filename_redo_data = TRUE; // To indicate that a redo has been applied
+ }
+
+ /* Redo tag data */
+ if (ETFile->FileTag->next && ETFile->FileTag->next->data
+ && (undo_key==((File_Tag *)ETFile->FileTag->next->data)->key))
+ {
+ ETFile->FileTag = ETFile->FileTag->next;
+ has_filetag_redo_data = TRUE;
+ }
+
+ return has_filename_redo_data | has_filetag_redo_data;
+}
+
+
+/*
+ * Returns TRUE if file contains redo data (filename or tag)
+ */
+gboolean ET_File_Data_Has_Redo_Data (ET_File *ETFile)
+{
+ gboolean has_filename_redo_data = FALSE;
+ gboolean has_filetag_redo_data = FALSE;
+
+ if (!ETFile) return FALSE;
+
+ if (ETFile->FileNameNew && ETFile->FileNameNew->next) has_filename_redo_data = TRUE;
+ if (ETFile->FileTag && ETFile->FileTag->next) has_filetag_redo_data = TRUE;
+
+ return has_filename_redo_data | has_filetag_redo_data;
+}
+
+
+/*
+ * Execute one 'undo' in the main undo list (it selects the last ETFile changed,
+ * before to apply an undo action)
+ */
+ET_File *ET_Undo_History_File_Data (void)
+{
+ ET_File *ETFile;
+ ET_History_File *ETHistoryFile;
+
+ if (!ETCore->ETHistoryFileList || !ET_History_File_List_Has_Undo_Data()) return NULL;
+
+ ETHistoryFile = (ET_History_File *)ETCore->ETHistoryFileList->data;
+ ETFile = (ET_File *)ETHistoryFile->ETFile;
+ ET_Displayed_File_List_By_Etfile(ETFile);
+ ET_Undo_File_Data(ETFile);
+
+ if (ETCore->ETHistoryFileList->prev)
+ ETCore->ETHistoryFileList = ETCore->ETHistoryFileList->prev;
+ return ETFile;
+}
+
+
+/*
+ * Returns TRUE if undo file list contains undo data
+ */
+gboolean ET_History_File_List_Has_Undo_Data (void)
+{
+ if (ETCore->ETHistoryFileList && ETCore->ETHistoryFileList->prev)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+
+/*
+ * Execute one 'redo' in the main undo list
+ */
+ET_File *ET_Redo_History_File_Data (void)
+{
+ ET_File *ETFile;
+ ET_History_File *ETHistoryFile;
+
+ if (!ETCore->ETHistoryFileList || !ET_History_File_List_Has_Redo_Data()) return NULL;
+
+ ETHistoryFile = (ET_History_File *)ETCore->ETHistoryFileList->next->data;
+ ETFile = (ET_File *)ETHistoryFile->ETFile;
+ ET_Displayed_File_List_By_Etfile(ETFile);
+ ET_Redo_File_Data(ETFile);
+
+ if (ETCore->ETHistoryFileList->next)
+ ETCore->ETHistoryFileList = ETCore->ETHistoryFileList->next;
+ return ETFile;
+}
+
+
+/*
+ * Returns TRUE if undo file list contains redo data
+ */
+gboolean ET_History_File_List_Has_Redo_Data (void)
+{
+ if (ETCore->ETHistoryFileList && ETCore->ETHistoryFileList->next)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+
+
+
+/**********************
+ * Checking functions *
+ **********************/
+
+
+/*
+ * Ckecks if the current files had been changed but not saved.
+ * Returns TRUE if the file has been saved.
+ * Returns FALSE if some changes haven't been saved.
+ */
+gboolean ET_Check_If_File_Is_Saved (ET_File *ETFile)
+{
+ File_Tag *FileTag = NULL;
+ File_Name *FileNameNew = NULL;
+
+ if (!ETFile) return TRUE;
+
+ if (ETFile->FileTag)
+ FileTag = ETFile->FileTag->data;
+ if (ETFile->FileNameNew)
+ FileNameNew = ETFile->FileNameNew->data;
+
+ // Check if the tag has been changed
+ if ( FileTag && FileTag->saved != TRUE )
+ return FALSE;
+
+ // Check if name of file has been changed
+ if ( FileNameNew && FileNameNew->saved != TRUE )
+ return FALSE;
+
+ // No changes
+ return TRUE;
+}
+
+
+/*
+ * Ckecks if some files, in the list, had been changed but not saved.
+ * Returns TRUE if all files have been saved.
+ * Returns FALSE if some changes haven't been saved.
+ */
+gboolean ET_Check_If_All_Files_Are_Saved (void)
+{
+ /* Check if some files haven't been saved, if didn't nochange=0 */
+ if (!ETCore->ETFileList)
+ {
+ return TRUE;
+ }else
+ {
+ GList *tmplist = g_list_first(ETCore->ETFileList);
+ while (tmplist)
+ {
+ if ( ET_Check_If_File_Is_Saved((ET_File *)tmplist->data) == FALSE )
+ return FALSE;
+
+ if (!tmplist->next) break;
+ tmplist = g_list_next(tmplist);
+ }
+ return TRUE;
+ }
+}
+
+
+
+
+/*******************
+ * Extra functions *
+ *******************/
+
+/*
+ * Set to TRUE the value of 'FileTag->saved' for the File_Tag item passed in parameter.
+ * And set ALL other values of the list to FALSE.
+ */
+void Set_Saved_Value_Of_File_Tag (File_Tag *FileTag, gboolean saved)
+{
+ if (FileTag) FileTag->saved = saved;
+}
+void ET_Mark_File_Tag_As_Saved (ET_File *ETFile)
+{
+ File_Tag *FileTag;
+ GList *FileTagList;
+
+ FileTag = (File_Tag *)ETFile->FileTag->data; // The current FileTag, to set to TRUE
+ FileTagList = ETFile->FileTagList;
+ g_list_foreach(FileTagList,(GFunc)Set_Saved_Value_Of_File_Tag,FALSE); // All other FileTag set to FALSE
+ FileTag->saved = TRUE; // The current FileTag set to TRUE
+}
+
+
+/*
+ * Set to TRUE the value of 'FileName->saved' for the File_Name item passed in parameter.
+ * And set ALL other values of the list to FALSE.
+ */
+void Set_Saved_Value_Of_File_Name (File_Name *FileName, gboolean saved)
+{
+ if (FileName) FileName->saved = saved;
+}
+void ET_Mark_File_Name_As_Saved (ET_File *ETFile)
+{
+ File_Name *FileNameNew;
+ GList *FileNameList;
+
+ FileNameNew = (File_Name *)ETFile->FileNameNew->data; // The current FileName, to set to TRUE
+ FileNameList = ETFile->FileNameList;
+ g_list_foreach(FileNameList,(GFunc)Set_Saved_Value_Of_File_Tag,FALSE);
+ FileNameNew->saved = TRUE;
+}
+
+
+/*
+ * Currently, it's a way by default to fill file size into ET_File_Info structure
+ */
+gboolean ET_Read_File_Info (gchar *filename, ET_File_Info *ETFileInfo)
+{
+ FILE *file;
+
+ if (!filename || !ETFileInfo)
+ return FALSE;
+
+ if ( (file=fopen(filename,"r"))==NULL )
+ {
+ gchar *filename_utf8 = filename_to_display(filename);
+ Log_Print(_("ERROR while opening file: '%s' (%s)."),filename_utf8,g_strerror(errno));
+ g_free(filename_utf8);
+ return FALSE;
+ }
+ fclose(file);
+
+ ETFileInfo->version = 0;
+ ETFileInfo->bitrate = 0;
+ ETFileInfo->samplerate = 0;
+ ETFileInfo->mode = 0;
+ ETFileInfo->size = Get_File_Size(filename);
+ ETFileInfo->duration = 0;
+
+ return TRUE;
+}
+
+
+/*
+ * This function generates a new file name using path of the old file and the new name
+ * - ETFile -> old_file_name : "/path_to_file/old_name.ext"
+ * - new_file_name_utf8 : "new_name.ext"
+ * Returns "/path_to_file/new_name.ext" into allocated data (in UTF-8)!
+ * Notes :
+ * - filenames (basemane) musn't exceed 255 characters (i.e. : "new_name.ext")
+ * - ogg filename musn't exceed 255-6 characters as we use mkstemp
+ */
+#if 1
+gchar *ET_File_Name_Generate (ET_File *ETFile, gchar *new_file_name_utf8)
+{
+ gchar *dirname_utf8;
+
+ if (ETFile && ETFile->FileNameNew->data && new_file_name_utf8
+ && (dirname_utf8=g_path_get_dirname(((File_Name *)ETFile->FileNameNew->data)->value_utf8)) )
+ {
+ gchar *extension;
+ gchar *new_file_name_path_utf8;
+
+ // Convert filename extension (lower/upper)
+ extension = ET_File_Name_Format_Extension(ETFile);
+
+ // Check length of filename (limit ~255 characters)
+ //ET_File_Name_Check_Length(ETFile,new_file_name_utf8);
+
+ // If filemame starts with /, it's a full filename with path but without extension
+ if (g_path_is_absolute(new_file_name_utf8))
+ {
+ // We just add the extension
+ new_file_name_path_utf8 = g_strconcat(new_file_name_utf8,extension,NULL);
+ }else
+ {
+ // New path (with filename)
+ if ( strcmp(dirname_utf8,G_DIR_SEPARATOR_S)==0 ) // Root directory?
+ new_file_name_path_utf8 = g_strconcat(dirname_utf8,new_file_name_utf8,extension,NULL);
+ else
+ new_file_name_path_utf8 = g_strconcat(dirname_utf8,G_DIR_SEPARATOR_S,new_file_name_utf8,extension,NULL);
+ }
+
+ g_free(extension);
+ return new_file_name_path_utf8; // in UTF-8
+ }else
+ {
+ return NULL;
+ }
+}
+#else
+/* FOR TESTING */
+/* Returns filename in file system encoding */
+gchar *ET_File_Name_Generate (ET_File *ETFile, gchar *new_file_name_utf8)
+{
+ gchar *dirname;
+
+ if (ETFile && ETFile->FileNameNew->data && new_file_name_utf8
+ && (dirname=g_path_get_dirname(((File_Name *)ETFile->FileNameNew->data)->value)) )
+ {
+ gchar *extension;
+ gchar *new_file_name_path;
+ gchar *new_file_name;
+
+ new_file_name = filename_from_display(new_file_name_utf8);
+
+ // Convert filename extension (lower/upper)
+ extension = ET_File_Name_Format_Extension(ETFile);
+
+ // Check length of filename (limit ~255 characters)
+ //ET_File_Name_Check_Length(ETFile,new_file_name_utf8);
+
+ // If filemame starts with /, it's a full filename with path but without extension
+ if (g_path_is_absolute(new_file_name))
+ {
+ // We just add the extension
+ new_file_name_path = g_strconcat(new_file_name,extension,NULL);
+ }else
+ {
+ // New path (with filename)
+ if ( strcmp(dirname,G_DIR_SEPARATOR_S)==0 ) // Root directory?
+ new_file_name_path = g_strconcat(dirname,new_file_name,extension,NULL);
+ else
+ new_file_name_path = g_strconcat(dirname,G_DIR_SEPARATOR_S,new_file_name,extension,NULL);
+ }
+
+ g_free(dirname);
+ g_free(new_file_name);
+ g_free(extension);
+ return new_file_name_path; // in file system encoding
+ }else
+ {
+ return NULL;
+ }
+}
+#endif
+
+
+gchar *ET_File_Name_Format_Extension (ET_File *ETFile)
+{
+ // Convert filename extension (lower/upper/no change)
+ if (FILENAME_EXTENSION_LOWER_CASE)
+ return g_utf8_strdown(ETFile->ETFileDescription->Extension,-1);
+
+ else if (FILENAME_EXTENSION_UPPER_CASE)
+ return g_utf8_strup(ETFile->ETFileDescription->Extension,-1);
+
+ else // FILENAME_EXTENSION_NO_CHANGE
+ return g_strdup(ETFile->ETFileExtension);
+}
+
+
+/*
+ * Check if the basename+extension of the file doesn't exceed following limits :
+ * - ogg filenames musn't exceed 255-6 characters (because we use mkstemp)
+ * - other filenames musn't exceed 255 characters
+ * Parameters:
+ * - 'filename_utf8' filename without the extension
+ */
+void ET_File_Name_Check_Length (ET_File *ETFile, gchar *filename_utf8)
+{
+ ET_File_Description *ETFileDescription;
+ gchar *basename;
+ gint exceed_size;
+
+
+ if (!ETFile || !filename_utf8) return;
+
+ basename = g_path_get_basename(filename_utf8); // If it contains directories...
+
+ ETFileDescription = ETFile->ETFileDescription;
+ switch (ETFileDescription->FileType)
+ {
+#ifdef ENABLE_OGG
+ case OGG_FILE:
+ if ( (exceed_size = (strlen(basename) - 245)) > 0 ) // 255 - 4 (extension) - 6 (mkstemp)
+ {
+ Log_Print(_("The filename '%s' exceeds %d characters and will be truncated!\n"), filename_utf8, 245);
+ filename_utf8[strlen(filename_utf8) - exceed_size] = '\0';
+ }
+ break;
+#endif
+ default:
+ if ( (exceed_size = (strlen(basename) - 251)) > 0 ) // 255 - 4 (extension)
+ {
+ Log_Print(_("The filename '%s' exceeds %d characters and will be truncated!\n"), filename_utf8, 251);
+ filename_utf8[strlen(filename_utf8) - exceed_size] = '\0';
+ }
+ break;
+ }
+ g_free(basename);
+}
+
+
+/*
+ * Used to replace the illegal characters in the filename
+ * Paremeter 'filename' musn't contain the path, else directories separators would be replaced!
+ */
+gboolean ET_File_Name_Convert_Character (gchar *filename_utf8)
+{
+ gchar *character;
+
+ if (!filename_utf8)
+ return FALSE;
+
+ // Convert automatically the directory separator ('/' on LINUX and '\' on WIN32) to '-'.
+ while ( (character=g_utf8_strchr(filename_utf8, -1, G_DIR_SEPARATOR))!=NULL )
+ *character = '-';
+
+#ifdef WIN32
+ // Convert character '\' on WIN32 to '-'.
+ while ( (character=g_utf8_strchr(filename_utf8, -1, '\\'))!=NULL )
+ *character = '-';
+ // Convert character '/' on WIN32 to '-'. (May be converted to '\', after...)
+ while ( (character=g_utf8_strchr(filename_utf8, -1, '/'))!=NULL )
+ *character = '-';
+#endif
+
+ // Convert other illegal characters on FAT32/16 filesystems and ISO9660 and Joliet (CD-ROM filesystems)
+ if (REPLACE_ILLEGAL_CHARACTERS_IN_FILENAME)
+ {
+ // Commented as we display unicode values as "\351" for "é"
+ //while ( (character=g_utf8_strchr(filename_utf8, -1, '\\'))!=NULL )
+ // *character = ',';
+ while ( (character=g_utf8_strchr(filename_utf8, -1, ':'))!=NULL )
+ *character = '-';
+ //while ( (character=g_utf8_strchr(filename_utf8, -1, ';'))!=NULL )
+ // *character = '-';
+ while ( (character=g_utf8_strchr(filename_utf8, -1, '*'))!=NULL )
+ *character = '+';
+ while ( (character=g_utf8_strchr(filename_utf8, -1, '?'))!=NULL )
+ *character = '_';
+ while ( (character=g_utf8_strchr(filename_utf8, -1, '\"'))!=NULL )
+ *character = '\'';
+ while ( (character=g_utf8_strchr(filename_utf8, -1, '<'))!=NULL )
+ *character = '(';
+ while ( (character=g_utf8_strchr(filename_utf8, -1, '>'))!=NULL )
+ *character = ')';
+ while ( (character=g_utf8_strchr(filename_utf8, -1, '|'))!=NULL )
+ *character = '-';
+ }
+ return TRUE;
+}
+
+
+/*
+ * Returns the number of file in the directory of the selected file.
+ * Parameter "path" should be in UTF-8
+ */
+guint ET_Get_Number_Of_Files_In_Directory (gchar *path_utf8)
+{
+ GList *etfilelist;
+ guint count = 0;
+
+ if (!path_utf8) return count;
+
+ etfilelist = g_list_first(ETCore->ETFileList);
+ while (etfilelist)
+ {
+ ET_File *ETFile = (ET_File *)etfilelist->data;
+ gchar *cur_filename_utf8 = ((File_Name *)((GList *)ETFile->FileNameCur)->data)->value_utf8;
+ gchar *dirname_utf8 = g_path_get_dirname(cur_filename_utf8);
+
+ if (g_utf8_collate(dirname_utf8, path_utf8) == 0)
+ count++;
+
+ g_free(dirname_utf8 );
+
+ etfilelist = etfilelist->next;
+ }
+
+ return count;
+}
+
+/*
+ * Try to valide a full string : if some characters are invalid, we skip them to
+ * return a string with only the valid characters.
+ */
+gchar *ET_Utf8_Validate_Full_String (gchar *string_to_validate)
+{
+ gchar *stv;
+ gchar *stv_end;
+ gchar *tmp, *tmp1;
+
+ if (!string_to_validate)
+ return NULL;
+
+ stv = g_strdup(string_to_validate);
+ // Remove all invalid character
+ while ( !g_utf8_validate(stv,-1,(const gchar **)&stv_end) )
+ {
+ // Not UTF-8 validated
+ tmp = tmp1 = stv_end;
+ tmp++;
+ while (*tmp)
+ {
+ if (*tmp)
+ *(tmp1++) = *(tmp++);
+ }
+ *tmp1 = '\0';
+ }
+ /*
+ // Truncate at first invalid character
+ if ( !g_utf8_validate(stv,-1,(const gchar **)&stv_end) )
+ {
+ // Not UTF-8 validated
+ *stv_end = '\0';
+ }*/
+
+ return stv;
+}
+
+/***********************
+ * Debugging functions *
+ ***********************/
+
+void ET_Debug_Print_File (ET_File *ETFile, gchar *file, gint line, gchar *function)
+{
+ GList *etfilelist = NULL;
+
+ if (ETFile)
+ etfilelist = g_list_append(etfilelist,ETFile);
+ ET_Debug_Print_File_List(etfilelist,file,line,function);
+ g_list_free(etfilelist);
+}
+
+
+/*
+ * Functions for DEBUGGING ONLY
+ * ET_Print_File_List => show list of filename
+ * Parameters: ETFileList : the list of files to display
+ * file = __FILE__
+ * line = __LINE__
+ * function = __FUNCTION__
+ */
+void ET_Debug_Print_File_List (GList *ETFileList, gchar *file, gint line, gchar *function)
+{
+ gint efl_item = 1;
+ gint fnl_item = 1;
+ gint ftl_item = 1;
+ gint etfilelist_length;
+ gint filenamelist_length;
+ gint filetaglist_length;
+ GList *etfilelist;
+ GList *filenamelist;
+ GList *filetaglist;
+
+
+ g_print("\n#### File list from %s:%d - start ####\n",file,line);
+ g_print("#### Function : %s ####\n",function);
+ if (ETFileList)
+ etfilelist = g_list_first(ETFileList);
+ else
+ etfilelist = g_list_first(ETCore->ETFileList);
+ etfilelist_length = g_list_length(etfilelist);
+ while (etfilelist)
+ {
+ g_print("#> ETFile %d/%d (%p)\n",efl_item,etfilelist_length,(ET_File *)etfilelist->data);
+ g_print("|--- IndexKey : '%d'\n",((ET_File *)etfilelist->data)->IndexKey);
+ g_print("|--- file_cur : '%s'\n",((File_Name *)((ET_File *)etfilelist->data)->FileNameCur->data)->value_utf8);
+ g_print("|--- file_new : '%s'\n",((File_Name *)((ET_File *)etfilelist->data)->FileNameNew->data)->value_utf8);
+ g_print("|--- saved : '%d'\n",((File_Name *)((ET_File *)etfilelist->data)->FileNameNew->data)->saved);
+
+ filenamelist = g_list_first( ((ET_File *)etfilelist->data)->FileNameList );
+ filenamelist_length = g_list_length(filenamelist);
+ fnl_item = 1;
+ while (filenamelist)
+ {
+ g_print("|--> File_Name : %d/%d %s\n",fnl_item,filenamelist_length,(filenamelist==((ET_File *)etfilelist->data)->FileNameNew)?"<<CURRENT>>":"");
+ g_print("| |-> key : '%d'\n",((File_Name *)filenamelist->data)->key);
+ g_print("| |-> filename: '%s'\n",((File_Name *)filenamelist->data)->value_utf8);
+
+ filenamelist = filenamelist->next;
+ fnl_item++;
+ }
+
+ g_print("|\n");
+
+ filetaglist = g_list_first( ((ET_File *)etfilelist->data)->FileTagList );
+ filetaglist_length = g_list_length(filetaglist);
+ ftl_item = 1;
+ while (filetaglist)
+ {
+ g_print("|--> File_Tag : %d/%d %s\n",ftl_item,filetaglist_length,(filetaglist==((ET_File *)etfilelist->data)->FileTag)?"<<CURRENT>>":"");
+ g_print("| |-> key : '%d'\n",((File_Tag *)filetaglist->data)->key);
+ g_print("| |-> saved : '%d'\n",((File_Tag *)filetaglist->data)->saved);
+ g_print("| |-> title : '%s'\n",((File_Tag *)filetaglist->data)->title);
+ g_print("| |-> artist : '%s'\n",((File_Tag *)filetaglist->data)->artist);
+ g_print("| |-> album : '%s'\n",((File_Tag *)filetaglist->data)->album);
+ g_print("| |-> disc_number : '%s'\n",((File_Tag *)filetaglist->data)->disc_number);
+ g_print("| |-> year : '%s'\n",((File_Tag *)filetaglist->data)->year);
+ g_print("| |-> track : '%s'\n",((File_Tag *)filetaglist->data)->track);
+ g_print("| |-> track_total : '%s'\n",((File_Tag *)filetaglist->data)->track_total);
+ g_print("| |-> genre : '%s'\n",((File_Tag *)filetaglist->data)->genre);
+ g_print("| |-> comment : '%s'\n",((File_Tag *)filetaglist->data)->comment);
+ g_print("| |-> composer : '%s'\n",((File_Tag *)filetaglist->data)->composer);
+ g_print("| |-> orig_artist : '%s'\n",((File_Tag *)filetaglist->data)->orig_artist);
+ g_print("| |-> copyright : '%s'\n",((File_Tag *)filetaglist->data)->copyright);
+ g_print("| |-> url : '%s'\n",((File_Tag *)filetaglist->data)->url);
+ g_print("| |-> encoded_by : '%s'\n",((File_Tag *)filetaglist->data)->encoded_by);
+
+ filetaglist = filetaglist->next;
+ ftl_item++;
+ }
+
+ g_print("|\n");
+
+ etfilelist = etfilelist->next;
+ efl_item++;
+ }
+ g_print("#### File list from %s:%d - end ####\n",file,line);
+}
+
+// Ex : ET_Debug_Print_Artist_Album_List(__FILE__,__LINE__,__FUNCTION__);
+void ET_Debug_Print_Artist_Album_List (gchar *file, gint line, gchar *function)
+{
+ GList *ArtistList;
+ GList *AlbumList;
+ GList *etfilelist;
+ ET_File *etfile;
+ gint artist_item;
+ gint artist_length;
+ gint album_item;
+ gint album_length;
+ gint etfile_item;
+ gint etfile_length;
+
+ g_print("\n#### Artist Album list from %s:%d - start ####\n",file,line);
+ g_print("#### Function : %s ####\n",function);
+ ArtistList = g_list_first(ETCore->ETArtistAlbumFileList);
+ artist_length = g_list_length(ArtistList);
+ artist_item = 1;
+ while (ArtistList)
+ {
+ g_print("#> Artist %d/%d \n",artist_item,artist_length);
+ AlbumList = g_list_first((GList *)ArtistList->data);
+ album_length = g_list_length(AlbumList);
+ album_item = 1;
+ while (AlbumList)
+ {
+ g_print(" |-> Album %d/%d \n",album_item,album_length);
+ etfilelist = g_list_first((GList *)AlbumList->data);
+ etfile_length = g_list_length(etfilelist);
+ etfile_item = 1;
+ while (etfilelist)
+ {
+ etfile = (ET_File *)etfilelist->data;
+ g_print(" | |-> ETFile %d/%d (%p)\n",etfile_item,etfile_length,etfile);
+ g_print(" | | |-> file : '%s'\n",((File_Name *)etfile->FileNameCur->data)->value_utf8);
+ g_print(" | | |-> artist : '%s'\n",((File_Tag *)etfile->FileTag->data)->artist);
+ g_print(" | | |-> album : '%s'\n",((File_Tag *)etfile->FileTag->data)->album);
+
+ etfilelist = etfilelist->next;
+ etfile_item++;
+ }
+
+ AlbumList = AlbumList->next;
+ album_item++;
+ }
+
+ ArtistList = ArtistList->next;
+ artist_item++;
+ }
+ g_print("#### Artist Album list from %s:%d - end ####\n",file,line);
+}
+
+
+#include "time.h"
+void ET_Debug_Print_Time (gchar *msg)
+{
+ struct tm *tms;
+ time_t nowtime;
+ char str[50];
+ //char *pstr = str;
+
+ nowtime = time(NULL);
+ tms = localtime(&nowtime);
+ strftime(str,sizeof(str),"%X",tms); // Time without date in current locale
+ //strftime(str,sizeof(str),"%x",ptr); // Date without time in current locale
+
+ if (msg)
+ {
+ g_print("## %s %s ##\n",msg,str);
+ }else
+ {
+ g_print("## %s ##\n",str);
+ }
+}
diff --git a/src/et_core.h b/src/et_core.h
new file mode 100755
index 0000000..ccc7f76
--- /dev/null
+++ b/src/et_core.h
@@ -0,0 +1,469 @@
+/* et_core.h - 2001/10/21 */
+/*
+ * EasyTAG - Tag editor for MP3 and Ogg Vorbis files
+ * Copyright (C) 2000-2003 Jerome Couderc <easytag@gmail.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef __ET_CORE_H__
+#define __ET_CORE_H__
+
+
+#include <glib.h>
+#include <gdk/gdktypes.h>
+#include <config.h> // For definition of ENABLE_OGG, ...
+
+
+/***************
+ * Declaration *
+ ***************/
+
+#ifndef MAX
+# define MAX(a,b) ((a) > (b) ? (a) : (b))
+#endif
+
+#ifndef MIN
+# define MIN(a,b) ((a) < (b) ? (a) : (b))
+#endif
+#ifndef MIN3
+# define MIN3(x,y,z) (MIN(x,y) < (z) ? MIN(x,y) : (z))
+#endif
+#ifndef MIN4
+# define MIN4(x,y,z,w) (MIN3(x,y,z) < (w) ? MIN3(x,y,z) : (w))
+#endif
+
+
+/*
+ * Colors Used (see declaration into et_core.c)
+ */
+GdkColor LIGHT_BLUE;
+GdkColor RED;
+GdkColor LIGHT_RED;
+GdkColor GREY;
+GdkColor LIGHT_GREY;
+GdkColor YELLOW;
+GdkColor BLACK;
+
+
+/*
+ * Types of sorting (discontinuous value to avoid separators in SortingFileOptionMenu).
+ */
+typedef enum
+{
+ SORTING_BY_ASCENDING_FILENAME,
+ SORTING_BY_DESCENDING_FILENAME,
+ SORTING_BY_ASCENDING_TRACK_NUMBER,
+ SORTING_BY_DESCENDING_TRACK_NUMBER,
+ SORTING_BY_ASCENDING_CREATION_DATE,
+ SORTING_BY_DESCENDING_CREATION_DATE,
+ SORTING_BY_ASCENDING_TITLE,
+ SORTING_BY_DESCENDING_TITLE,
+ SORTING_BY_ASCENDING_ARTIST,
+ SORTING_BY_DESCENDING_ARTIST,
+ SORTING_BY_ASCENDING_ALBUM,
+ SORTING_BY_DESCENDING_ALBUM,
+ SORTING_BY_ASCENDING_YEAR,
+ SORTING_BY_DESCENDING_YEAR,
+ SORTING_BY_ASCENDING_GENRE,
+ SORTING_BY_DESCENDING_GENRE,
+ SORTING_BY_ASCENDING_COMMENT,
+ SORTING_BY_DESCENDING_COMMENT,
+ SORTING_BY_ASCENDING_COMPOSER,
+ SORTING_BY_DESCENDING_COMPOSER,
+ SORTING_BY_ASCENDING_ORIG_ARTIST,
+ SORTING_BY_DESCENDING_ORIG_ARTIST,
+ SORTING_BY_ASCENDING_COPYRIGHT,
+ SORTING_BY_DESCENDING_COPYRIGHT,
+ SORTING_BY_ASCENDING_URL,
+ SORTING_BY_DESCENDING_URL,
+ SORTING_BY_ASCENDING_ENCODED_BY,
+ SORTING_BY_DESCENDING_ENCODED_BY,
+ SORTING_BY_ASCENDING_FILE_TYPE,
+ SORTING_BY_DESCENDING_FILE_TYPE,
+ SORTING_BY_ASCENDING_FILE_SIZE,
+ SORTING_BY_DESCENDING_FILE_SIZE,
+ SORTING_BY_ASCENDING_FILE_DURATION,
+ SORTING_BY_DESCENDING_FILE_DURATION,
+ SORTING_BY_ASCENDING_FILE_BITRATE,
+ SORTING_BY_DESCENDING_FILE_BITRATE,
+ SORTING_BY_ASCENDING_FILE_SAMPLERATE,
+ SORTING_BY_DESCENDING_FILE_SAMPLERATE,
+ SORTING_UNKNOWN
+} ET_Sorting_Type;
+
+
+/*
+ * Types of files
+ */
+typedef enum
+{ // (.ext) is not so popular
+ MP2_FILE = 0, // Mpeg audio Layer 2 : .mp2 (.mpg) (.mpga)
+ MP3_FILE, // Mpeg audio Layer 3 : .mp3 (.mpg) (.mpga)
+ MP4_FILE, // Mpeg audio Layer 4 / AAC : .mp4 (.m4a) (.m4p)
+ OGG_FILE, // Ogg Vorbis audio : .ogg (.ogm)
+ FLAC_FILE, // FLAC (lossless) : .flac .fla
+ MPC_FILE, // MusePack : .mpc .mp+ .mpp
+ MAC_FILE, // Monkey's Audio (lossless) : .ape (.mac)
+ SPEEX_FILE, // Speech audio files : .spx
+ OFR_FILE, // OptimFROG (lossless) : .ofr .ofs
+ WAVPACK_FILE, // Wavpack (lossless) : .wv
+ UNKNOWN_FILE
+} ET_File_Type;
+
+
+/*
+ * Types of tags
+ */
+typedef enum
+{
+ ID3_TAG = 0,
+ OGG_TAG,
+ APE_TAG,
+ FLAC_TAG,
+ MP4_TAG,
+ WAVPACK_TAG,
+ UNKNOWN_TAG
+} ET_Tag_Type;
+
+
+/*
+ * Description of each item of the FileNameList list
+ */
+typedef struct _File_Name File_Name;
+struct _File_Name
+{
+ guint key;
+ gboolean saved; /* Set to TRUE if this filename had been saved */
+ gchar *value; /* The filename containing the full path and the extension of the file */
+ gchar *value_utf8; /* Same than "value", but converted to UTF-8 to avoid multiple call to the convertion function */
+ gchar *value_ck; /* Collate key of "value_utf8" to speed up comparaison */
+};
+
+
+/*
+ * Description of Picture item (see picture.h)
+ */
+typedef struct _Picture Picture;
+struct _Picture
+{
+ gint type;
+ gchar *description;
+ gint width; /* Original width of the picture */
+ gint height; /* Original height of the picture */
+ gulong size; /* Picture size in bits */
+ guchar *data;
+ Picture *next;
+};
+
+
+/*
+ * Description of each item of the TagList list
+ */
+typedef struct _File_Tag File_Tag;
+struct _File_Tag
+{
+ guint key; /* Incremented value */
+ gboolean saved; /* Set to TRUE if this tag had been saved */
+
+ gchar *title; /* Title of track */
+ gchar *artist; /* Artist name */
+ gchar *album; /* Album name */
+ gchar *disc_number; /* Disc number */
+ gchar *year; /* Year of track */
+ gchar *track; /* Position of track in the album */
+ gchar *track_total; /* The number of tracks for the album (ex: 12/20) */
+ gchar *genre; /* Genre of song */
+ gchar *comment; /* Comment */
+ gchar *composer; /* Composer */
+ gchar *orig_artist; /* Original artist / Performer */
+ gchar *copyright; /* Copyright */
+ gchar *url; /* URL */
+ gchar *encoded_by; /* Encoded by */
+ Picture *picture; /* Picture */
+ GList *other; /* List of unsupported fields (used for ogg only) */
+};
+
+
+/*
+ * Structure containing informations of the header of file
+ * Nota: This struct was copied from an "MP3 structure", and will change later.
+ */
+typedef struct _ET_File_Info ET_File_Info;
+struct _ET_File_Info
+{
+ gint version; /* Version of bitstream (mpeg version for mp3, encoder version for ogg) */
+ gint mpeg25; /* Version is MPEG 2.5? */
+ gint layer; /* "MP3 data" */
+ gint bitrate; /* Bitrate (kb/s) */
+ gboolean variable_bitrate; /* Is a VBR file? */
+ gint samplerate; /* Samplerate (Hz) */
+ gint mode; /* Stereo, ... or channels for ogg */
+ gint size; /* The size of file (in bytes) */
+ gint duration; /* The duration of file (in seconds) */
+ gchar *mpc_profile; /* MPC data */
+ gchar *mpc_version; /* MPC data : encoder version (also for Speex) */
+};
+
+
+/*
+ * Structure for descripting supported files
+ */
+typedef struct _ET_File_Description ET_File_Description;
+struct _ET_File_Description
+{
+ ET_File_Type FileType; /* Type of file (ex: MP3) */
+ gchar *Extension; /* Extension (ex: ".mp3") */
+ ET_Tag_Type TagType; /* Type of tag (ex: ID3) */
+};
+
+
+/*
+ * Description of supported files
+ */
+static const ET_File_Description ETFileDescription[] =
+{
+#ifdef ENABLE_MP3
+ {MP3_FILE, ".mp3", ID3_TAG},
+ {MP2_FILE, ".mp2", ID3_TAG},
+#endif
+#ifdef ENABLE_OGG
+ {OGG_FILE, ".ogg", OGG_TAG},
+#endif
+#ifdef ENABLE_SPEEX
+ {SPEEX_FILE, ".spx", OGG_TAG}, // Implemented by Pierre Dumuid
+#endif
+#ifdef ENABLE_FLAC
+ {FLAC_FILE, ".flac", FLAC_TAG},
+ {FLAC_FILE, ".fla", FLAC_TAG},
+#endif
+ {MPC_FILE, ".mpc", APE_TAG}, // Implemented by Artur Polaczynski
+ {MPC_FILE, ".mp+", APE_TAG}, // Implemented by Artur Polaczynski
+ {MPC_FILE, ".mpp", APE_TAG}, // Implemented by Artur Polaczynski
+ {MAC_FILE, ".ape", APE_TAG}, // Implemented by Artur Polaczynski
+ {MAC_FILE, ".mac", APE_TAG}, // Implemented by Artur Polaczynski
+ {OFR_FILE, ".ofr", APE_TAG},
+ {OFR_FILE, ".ofs", APE_TAG},
+#ifdef ENABLE_MP4
+ {MP4_FILE, ".mp4", MP4_TAG}, // Implemented by Michael Ihde
+ {MP4_FILE, ".m4a", MP4_TAG}, // Implemented by Michael Ihde
+ {MP4_FILE, ".m4p", MP4_TAG}, // Implemented by Michael Ihde
+#endif
+#ifdef ENABLE_WAVPACK
+ {WAVPACK_FILE, ".wv", WAVPACK_TAG}, // Implemented by Maarten Maathuis
+#endif
+ {UNKNOWN_FILE, "", UNKNOWN_TAG} /* This item must be placed to the end! */
+};
+
+/* Calculate the last index of the previous tab */
+#define ET_FILE_DESCRIPTION_SIZE ( sizeof(ETFileDescription)/sizeof(ETFileDescription[0]) - 1 )
+
+
+/*
+ * Description of each item of the ETFileList list
+ */
+typedef struct _ET_File ET_File;
+struct _ET_File
+{
+ guint IndexKey; /* Value used to display the position in the list (and in the BrowserList) - Must be renumered after resorting the list - This value varies when resorting list */
+
+ guint ETFileKey; /* Primary key to identify each item of the list (no longer used?) */
+
+ ET_File_Description *ETFileDescription;
+ gchar *ETFileExtension; /* Real extension of the file (keeping the case) (should be placed in ETFileDescription?) */
+ ET_File_Info *ETFileInfo; /* Header infos: bitrate, duration, ... */
+
+ GList *FileNameCur; /* Points to item of FileNameList that represents the current value of filename state (i.e. file on hard disk) */
+ GList *FileNameNew; /* Points to item of FileNameList that represents the new value of filename state */
+ GList *FileNameList; /* Contains the history of changes about the file name */
+ GList *FileNameListBak; /* Contains items of FileNameList removed by 'undo' procedure but have data currently saved (for example, when you save your last changes, make some 'undo', then make new changes) */
+
+ GList *FileTag; /* Points to the current item used of FileTagList */
+ GList *FileTagList; /* Contains the history of changes about file tag data */
+ GList *FileTagListBak; /* Contains items of FileTagList removed by 'undo' procedure but have data currently saved */
+
+};
+
+
+
+/*
+ * Description of each item of the ETHistoryFileList list
+ */
+typedef struct _ET_History_File ET_History_File;
+struct _ET_History_File
+{
+ ET_File *ETFile; /* Pointer to item of ETFileList changed */
+};
+
+
+
+/*
+ * Description of all variables, lists needed by EasyTAG
+ */
+typedef struct _ET_Core ET_Core;
+struct _ET_Core
+{
+ // The main list of files
+ GList *ETFileList; // List of ALL FILES (ET_File) loaded in the directory and sub-directories (List of ET_File) (This list musn't be altered, and points always to the first item)
+
+ // The list of files organized by artist then album
+ GList *ETArtistAlbumFileList;
+
+
+ // Displayed list (part of the main list of files displayed in BrowserList) (used when displaying by Artist & Album)
+ GList *ETFileDisplayedList; // List of files displayed (List of ET_File from ETFileList / ATArtistAlbumFileList) | !! May not point to the first item!!
+ GList *ETFileDisplayedListPtr; // Pointer to the current item of ETFileDisplayedList used with ET_Displayed_File_List_First, ET_Displayed_File_List_Previous
+ guint ETFileDisplayedList_Length; // Contains the length of the displayed list
+ gfloat ETFileDisplayedList_TotalSize; // Total of the size of files in displayed list (in bytes)
+ gulong ETFileDisplayedList_TotalDuration; // Total of duration of files in displayed list (in seconds)
+
+ // Displayed item
+ ET_File *ETFileDisplayed; // Pointer to the current ETFile displayed in EasyTAG (may be NULL)
+
+
+ // History list
+ GList *ETHistoryFileList; // History list of files changes for undo/redo actions
+};
+
+ET_Core *ETCore; // Main pointer to structure needed by EasyTAG
+
+
+
+/**************
+ * Prototypes *
+ **************/
+
+void ET_Core_Create (void);
+void ET_Core_Initialize (void);
+void ET_Core_Free (void);
+void ET_Core_Destroy (void);
+
+gboolean ET_File_Is_Supported (gchar *filename);
+GList *ET_Add_File_To_File_List (gchar *filename);
+gboolean ET_Remove_File_From_File_List (ET_File *ETFile);
+
+gboolean ET_Create_Artist_Album_File_List (void);
+gboolean ET_Add_File_To_Artist_Album_File_List (ET_File *ETFile);
+gboolean ET_Remove_File_From_File_List (ET_File *ETFile);
+
+gboolean ET_Check_If_File_Is_Saved (ET_File *ETFile);
+gboolean ET_Check_If_All_Files_Are_Saved (void);
+
+//gboolean ET_Free_File_List (void);
+gboolean ET_Free_History_File_List (void);
+gboolean ET_Free_Displayed_File_List (void);
+
+ET_File *ET_File_Item_New (void);
+File_Name *ET_File_Name_Item_New (void);
+File_Tag *ET_File_Tag_Item_New (void);
+ET_File_Info *ET_File_Info_Item_New (void);
+gboolean ET_Free_File_Tag_Item (File_Tag *FileTag);
+gboolean ET_Free_File_List_Item (ET_File *ETFile);
+
+gboolean ET_Copy_File_Tag_Item (ET_File *ETFile, File_Tag *FileTag);
+gboolean ET_Set_Field_File_Name_Item (gchar **FileNameField, gchar *value);
+gboolean ET_Set_Filename_File_Name_Item (File_Name *FileName, gchar *filename_utf8, gchar *filename);
+gboolean ET_Set_Field_File_Tag_Item (gchar **FileTagField, gchar *value);
+gboolean ET_Set_Field_File_Tag_Picture (Picture **FileTagField, Picture *pic);
+
+GList *ET_Displayed_File_List_First (void);
+GList *ET_Displayed_File_List_Previous (void);
+GList *ET_Displayed_File_List_Next (void);
+GList *ET_Displayed_File_List_Last (void);
+GList *ET_Displayed_File_List_By_Etfile (ET_File *ETFile);
+GList *ET_Displayed_File_List_By_Position (gulong pos_in_list);
+guint ET_Displayed_File_List_Get_Length (void);
+//GList *ET_Displayed_File_List_Current (void);
+
+gboolean ET_Set_Displayed_File_List (GList *ETFileList);
+
+void ET_Display_File_Data_To_UI (ET_File *ETFile);
+void ET_Save_File_Data_From_UI (ET_File *ETFile);
+gboolean ET_Save_File_Tag_To_HD (ET_File *ETFile);
+
+gboolean ET_Undo_File_Data (ET_File *ETFile);
+gboolean ET_Redo_File_Data (ET_File *ETFile);
+gboolean ET_File_Data_Has_Undo_Data (ET_File *ETFile);
+gboolean ET_File_Data_Has_Redo_Data (ET_File *ETFile);
+
+ET_File *ET_Undo_History_File_Data (void);
+ET_File *ET_Redo_History_File_Data (void);
+gboolean ET_History_File_List_Has_Undo_Data (void);
+gboolean ET_History_File_List_Has_Redo_Data (void);
+
+gboolean ET_Manage_Changes_Of_File_Data (ET_File *ETFile, File_Name *FileName, File_Tag *FileTag);
+void ET_Mark_File_Name_As_Saved (ET_File *ETFile);
+void ET_Update_Directory_Name_Into_File_List (gchar* last_path, gchar *new_path);
+gboolean ET_File_Name_Convert_Character (gchar *filename_utf8);
+gchar *ET_File_Name_Format_Extension (ET_File *ETFile);
+gchar *ET_File_Name_Generate (ET_File *ETFile, gchar *new_file_name);
+guint ET_Get_Number_Of_Files_In_Directory (gchar *path_utf8);
+gchar *ET_Utf8_Validate_Full_String (gchar *string_to_validate);
+
+gboolean ET_Detect_Changes_Of_File_Tag (File_Tag *FileTag1, File_Tag *FileTag2);
+
+GList *ET_Sort_File_List (GList *ETFileList, ET_Sorting_Type Sorting_Type);
+void ET_Sort_Displayed_File_List (ET_Sorting_Type Sorting_Type);
+void ET_Sort_Displayed_File_List_And_Update_UI (ET_Sorting_Type Sorting_Type);
+gint ET_Comp_Func_Sort_File_By_Ascending_Filename (ET_File *ETFile1, ET_File *ETFile2);
+gint ET_Comp_Func_Sort_File_By_Descending_Filename (ET_File *ETFile1, ET_File *ETFile2);
+gint ET_Comp_Func_Sort_File_By_Ascending_Creation_Date (ET_File *ETFile1, ET_File *ETFile2);
+gint ET_Comp_Func_Sort_File_By_Descending_Creation_Date (ET_File *ETFile1, ET_File *ETFile2);
+gint ET_Comp_Func_Sort_File_By_Ascending_Track_Number (ET_File *ETFile1, ET_File *ETFile2);
+gint ET_Comp_Func_Sort_File_By_Descending_Track_Number (ET_File *ETFile1, ET_File *ETFile2);
+gint ET_Comp_Func_Sort_File_By_Ascending_Title (ET_File *ETFile1, ET_File *ETFile2);
+gint ET_Comp_Func_Sort_File_By_Descending_Title (ET_File *ETFile1, ET_File *ETFile2);
+gint ET_Comp_Func_Sort_File_By_Ascending_Artist (ET_File *ETFile1, ET_File *ETFile2);
+gint ET_Comp_Func_Sort_File_By_Descending_Artist (ET_File *ETFile1, ET_File *ETFile2);
+gint ET_Comp_Func_Sort_File_By_Ascending_Album (ET_File *ETFile1, ET_File *ETFile2);
+gint ET_Comp_Func_Sort_File_By_Descending_Album (ET_File *ETFile1, ET_File *ETFile2);
+gint ET_Comp_Func_Sort_File_By_Ascending_Year (ET_File *ETFile1, ET_File *ETFile2);
+gint ET_Comp_Func_Sort_File_By_Descending_Year (ET_File *ETFile1, ET_File *ETFile2);
+gint ET_Comp_Func_Sort_File_By_Ascending_Genre (ET_File *ETFile1, ET_File *ETFile2);
+gint ET_Comp_Func_Sort_File_By_Descending_Genre (ET_File *ETFile1, ET_File *ETFile2);
+gint ET_Comp_Func_Sort_File_By_Ascending_Comment (ET_File *ETFile1, ET_File *ETFile2);
+gint ET_Comp_Func_Sort_File_By_Descending_Comment (ET_File *ETFile1, ET_File *ETFile2);
+gint ET_Comp_Func_Sort_File_By_Ascending_Composer (ET_File *ETFile1, ET_File *ETFile2);
+gint ET_Comp_Func_Sort_File_By_Descending_Composer (ET_File *ETFile1, ET_File *ETFile2);
+gint ET_Comp_Func_Sort_File_By_Ascending_Orig_Artist (ET_File *ETFile1, ET_File *ETFile2);
+gint ET_Comp_Func_Sort_File_By_Descending_Orig_Artist (ET_File *ETFile1, ET_File *ETFile2);
+gint ET_Comp_Func_Sort_File_By_Ascending_Copyright (ET_File *ETFile1, ET_File *ETFile2);
+gint ET_Comp_Func_Sort_File_By_Descending_Copyright (ET_File *ETFile1, ET_File *ETFile2);
+gint ET_Comp_Func_Sort_File_By_Ascending_Url (ET_File *ETFile1, ET_File *ETFile2);
+gint ET_Comp_Func_Sort_File_By_Descending_Url (ET_File *ETFile1, ET_File *ETFile2);
+gint ET_Comp_Func_Sort_File_By_Ascending_Encoded_By (ET_File *ETFile1, ET_File *ETFile2);
+gint ET_Comp_Func_Sort_File_By_Descending_Encoded_By (ET_File *ETFile1, ET_File *ETFile2);
+
+gint ET_Comp_Func_Sort_File_By_Ascending_File_Type (ET_File *ETFile1, ET_File *ETFile2);
+gint ET_Comp_Func_Sort_File_By_Descending_File_Type (ET_File *ETFile1, ET_File *ETFile2);
+gint ET_Comp_Func_Sort_File_By_Ascending_File_Size (ET_File *ETFile1, ET_File *ETFile2);
+gint ET_Comp_Func_Sort_File_By_Descending_File_Size (ET_File *ETFile1, ET_File *ETFile2);
+gint ET_Comp_Func_Sort_File_By_Ascending_File_Duration (ET_File *ETFile1, ET_File *ETFile2);
+gint ET_Comp_Func_Sort_File_By_Descending_File_Duration (ET_File *ETFile1, ET_File *ETFile2);
+gint ET_Comp_Func_Sort_File_By_Ascending_File_Bitrate (ET_File *ETFile1, ET_File *ETFile2);
+gint ET_Comp_Func_Sort_File_By_Descending_File_Bitrate (ET_File *ETFile1, ET_File *ETFile2);
+gint ET_Comp_Func_Sort_File_By_Ascending_File_Samplerate (ET_File *ETFile1, ET_File *ETFile2);
+gint ET_Comp_Func_Sort_File_By_Descending_File_Samplerate (ET_File *ETFile1, ET_File *ETFile2);
+
+
+/* Functions for debugging */
+void ET_Debug_Print_File (ET_File *ETFile, gchar *file, gint line, gchar *function);
+void ET_Debug_Print_File_List (GList *ETFileList, gchar *file, gint line, gchar *function);
+void ET_Debug_Print_Artist_Album_List (gchar *file, gint line, gchar *function);
+void ET_Debug_Print_Time (gchar *msg);
+
+
+#endif /* __ET_CORE_H__ */
diff --git a/src/flac_header.c b/src/flac_header.c
new file mode 100755
index 0000000..408b73b
--- /dev/null
+++ b/src/flac_header.c
@@ -0,0 +1,335 @@
+/* flac_header.c - 2002/07/03 */
+/*
+ * EasyTAG - Tag editor for MP3 and Ogg Vorbis files
+ * Copyright (C) 2000-2003 Jerome Couderc <easytag@gmail.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * Code taken from :
+ * FLAC - Free Lossless Audio Codec - v1.0.3
+ * Copyright (C) 2001 Josh Coalson
+ *
+ */
+
+#include <config.h> // For definition of ENABLE_FLAC
+
+#ifdef ENABLE_FLAC
+
+#include <gtk/gtk.h>
+#include <glib/gi18n-lib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h> // 20030519 <tmancill@debian.org> for compiliation on ia64 arch
+#include <FLAC/all.h>
+
+#include "easytag.h"
+#include "et_core.h"
+#include "log.h"
+#include "misc.h"
+#include "charset.h"
+
+/* Patch from Josh Coalson
+ * FLAC 1.1.3 has FLAC_API_VERSION_CURRENT == 8 *
+ * by LEGACY_FLAC we mean pre-FLAC 1.1.3; in FLAC 1.1.3 the FLAC__FileDecoder was merged into the FLAC__StreamDecoder */
+#if !defined(FLAC_API_VERSION_CURRENT) || FLAC_API_VERSION_CURRENT < 8
+#define LEGACY_FLAC // For FLAC version < 1.1.3
+#else
+#undef LEGACY_FLAC
+#endif
+
+
+/****************
+ * Declarations *
+ ****************/
+/* Taken in "xmms/plugin.h" */
+typedef enum
+{
+ FMT_U8, FMT_S8, FMT_U16_LE, FMT_U16_BE, FMT_U16_NE, FMT_S16_LE, FMT_S16_BE, FMT_S16_NE
+}
+AFormat;
+
+typedef struct {
+ FLAC__bool abort_flag;
+ FLAC__bool is_playing;
+ FLAC__bool eof;
+ FLAC__bool play_thread_open; /* if true, is_playing must also be true */
+ unsigned total_samples;
+ unsigned bits_per_sample;
+ unsigned channels;
+ unsigned sample_rate;
+ unsigned length_in_msec;
+ AFormat sample_format; // Note : defined in XMMS devel
+ int seek_to_in_sec;
+} file_info_struct;
+
+
+static FLAC__byte reservoir_[FLAC__MAX_BLOCK_SIZE * 2 * 2 * 2]; /* *2 for max bytes-per-sample, *2 for max channels, another *2 for overflow */
+static unsigned reservoir_samples_ = 0;
+
+
+/**************
+ * Prototypes *
+ **************/
+#ifdef LEGACY_FLAC
+static FLAC__StreamDecoderWriteStatus write_callback_(const FLAC__FileDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data);
+static void metadata_callback_(const FLAC__FileDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data);
+static void error_callback_ (const FLAC__FileDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data);
+#else
+static FLAC__StreamDecoderWriteStatus write_callback_(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data);
+static void metadata_callback_(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data);
+static void error_callback_ (const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data);
+#endif
+
+
+
+/*************
+ * Functions *
+ *************/
+
+/****************************
+ * Header info of FLAC file *
+ ****************************/
+
+gboolean Flac_Header_Read_File_Info (gchar *filename, ET_File_Info *ETFileInfo)
+{
+ FILE *file;
+ gdouble duration = 0;
+ gulong filesize;
+
+#ifdef LEGACY_FLAC
+ FLAC__FileDecoder *tmp_decoder;
+#else
+ FLAC__StreamDecoder *tmp_decoder;
+#endif
+ file_info_struct tmp_file_info;
+
+ if (!filename || !ETFileInfo)
+ return FALSE;
+
+ if ( (file=fopen(filename,"r"))==NULL )
+ {
+ gchar *filename_utf8 = filename_to_display(filename);
+ Log_Print(_("ERROR while opening file: '%s' (%s)."),filename_utf8,g_strerror(errno));
+ g_free(filename_utf8);
+ return FALSE;
+ }
+ fclose(file);
+
+ /* Decoding FLAC file */
+#ifdef LEGACY_FLAC
+ tmp_decoder = FLAC__file_decoder_new();
+#else
+ tmp_decoder = FLAC__stream_decoder_new();
+#endif
+ if (tmp_decoder == 0)
+ {
+ return FALSE;
+ }
+
+ tmp_file_info.abort_flag = false;
+#ifdef LEGACY_FLAC
+ FLAC__file_decoder_set_md5_checking (tmp_decoder, false);
+ FLAC__file_decoder_set_filename (tmp_decoder, filename);
+ FLAC__file_decoder_set_write_callback (tmp_decoder, write_callback_);
+ FLAC__file_decoder_set_metadata_callback(tmp_decoder, metadata_callback_);
+ FLAC__file_decoder_set_error_callback (tmp_decoder, error_callback_);
+ FLAC__file_decoder_set_client_data (tmp_decoder, &tmp_file_info);
+ if (FLAC__file_decoder_init(tmp_decoder) != FLAC__FILE_DECODER_OK)
+ {
+ return FALSE;
+ }
+
+#else
+ FLAC__stream_decoder_set_md5_checking (tmp_decoder, false);
+ if(FLAC__stream_decoder_init_file(tmp_decoder, filename, write_callback_, metadata_callback_, error_callback_, &tmp_file_info) != FLAC__STREAM_DECODER_INIT_STATUS_OK)
+ return FALSE;
+#endif
+
+#ifdef LEGACY_FLAC
+ // In FLAC 1.0.3, is used : FLAC__file_decoder_process_metadata
+ // In FLAC 1.0.4, is used : FLAC__file_decoder_process_until_end_of_metadata
+#if ( (LIBFLAC_MAJOR <= 1) && (LIBFLAC_MINOR <= 0) && (LIBFLAC_PATCH <= 3) )
+ if (!FLAC__file_decoder_process_metadata(tmp_decoder)) // FLAC 1.0.3
+#else
+ if (!FLAC__file_decoder_process_until_end_of_metadata(tmp_decoder)) // FLAC 1.0.4 (Bastian Kleineidam)
+#endif
+#else
+ if(!FLAC__stream_decoder_process_until_end_of_metadata(tmp_decoder))
+#endif
+ {
+ return FALSE;
+ }
+
+#ifdef LEGACY_FLAC
+ FLAC__file_decoder_finish(tmp_decoder);
+ FLAC__file_decoder_delete(tmp_decoder);
+#else
+ FLAC__stream_decoder_finish(tmp_decoder);
+ FLAC__stream_decoder_delete(tmp_decoder);
+#endif
+ /* End of decoding FLAC file */
+
+
+ filesize = Get_File_Size(filename);
+ duration = (gint)tmp_file_info.length_in_msec/1000;
+
+ ETFileInfo->version = 0; // Not defined in FLAC file
+ if (duration > 0)
+ ETFileInfo->bitrate = filesize*8/duration/1000; // FIXME : Approximation!! Needs to remove tag size!
+ ETFileInfo->samplerate = tmp_file_info.sample_rate;
+ ETFileInfo->mode = tmp_file_info.channels;
+ ETFileInfo->size = filesize;
+ ETFileInfo->duration = duration;
+
+ return TRUE;
+}
+
+
+
+#ifdef LEGACY_FLAC
+FLAC__StreamDecoderWriteStatus write_callback_(const FLAC__FileDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data)
+#else
+FLAC__StreamDecoderWriteStatus write_callback_(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data)
+#endif
+{
+ file_info_struct *file_info = (file_info_struct *)client_data;
+ const unsigned bps = file_info->bits_per_sample, channels = file_info->channels, wide_samples = frame->header.blocksize;
+ unsigned wide_sample, sample, channel;
+ FLAC__int8 *scbuffer = (FLAC__int8*)reservoir_;
+ FLAC__int16 *ssbuffer = (FLAC__int16*)reservoir_;
+
+ (void)decoder;
+
+ if (file_info->abort_flag)
+ return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
+
+ if (bps == 8) {
+ for(sample = reservoir_samples_*channels, wide_sample = 0; wide_sample < wide_samples; wide_sample++)
+ for(channel = 0; channel < channels; channel++, sample++)
+ scbuffer[sample] = (FLAC__int8)buffer[channel][wide_sample];
+ }
+ else if (bps == 16) {
+ for(sample = reservoir_samples_*channels, wide_sample = 0; wide_sample < wide_samples; wide_sample++)
+ for(channel = 0; channel < channels; channel++, sample++)
+ ssbuffer[sample] = (FLAC__int16)buffer[channel][wide_sample];
+ }
+ else {
+ file_info->abort_flag = true;
+ return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
+ }
+
+ reservoir_samples_ += wide_samples;
+
+ return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
+}
+
+#ifdef LEGACY_FLAC
+void metadata_callback_(const FLAC__FileDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data)
+#else
+void metadata_callback_(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data)
+#endif
+{
+ file_info_struct *file_info = (file_info_struct *)client_data;
+ (void)decoder;
+ if (metadata->type == FLAC__METADATA_TYPE_STREAMINFO) {
+ FLAC__ASSERT(metadata->data.stream_info.total_samples < 0x100000000); /* this plugin can only handle < 4 gigasamples */
+ file_info->total_samples = (unsigned)(metadata->data.stream_info.total_samples&0xffffffff);
+ file_info->bits_per_sample = metadata->data.stream_info.bits_per_sample;
+ file_info->channels = metadata->data.stream_info.channels;
+ file_info->sample_rate = metadata->data.stream_info.sample_rate;
+
+ if (file_info->bits_per_sample == 8) {
+ file_info->sample_format = FMT_S8;
+ }
+ else if (file_info->bits_per_sample == 16) {
+ file_info->sample_format = FMT_S16_NE;
+ }
+ else {
+ file_info->abort_flag = true;
+ return;
+ }
+ file_info->length_in_msec = file_info->total_samples * 10 / (file_info->sample_rate / 100);
+ }
+}
+
+#ifdef LEGACY_FLAC
+void error_callback_(const FLAC__FileDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data)
+#else
+void error_callback_(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data)
+#endif
+{
+ file_info_struct *file_info = (file_info_struct *)client_data;
+ (void)decoder;
+ if (status != FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC)
+ file_info->abort_flag = true;
+}
+
+
+
+
+gboolean Flac_Header_Display_File_Info_To_UI (gchar *filename_utf8, ET_File_Info *ETFileInfo)
+{
+ gchar *text;
+ gchar *time = NULL;
+ gchar *time1 = NULL;
+ gchar *size = NULL;
+ gchar *size1 = NULL;
+
+ /* Nothing to display */
+ //gtk_label_set_text(GTK_LABEL(VersionLabel),"");
+ //gtk_label_set_text(GTK_LABEL(VersionValueLabel),"");
+ gtk_label_set_text(GTK_LABEL(VersionLabel),_("Encoder:"));
+ gtk_label_set_text(GTK_LABEL(VersionValueLabel),"flac");
+
+ /* Bitrate */
+ text = g_strdup_printf(_("%d kb/s"),ETFileInfo->bitrate);
+ gtk_label_set_text(GTK_LABEL(BitrateValueLabel),text);
+ g_free(text);
+
+ /* Samplerate */
+ text = g_strdup_printf(_("%d Hz"),ETFileInfo->samplerate);
+ gtk_label_set_text(GTK_LABEL(SampleRateValueLabel),text);
+ g_free(text);
+
+ /* Mode */
+ gtk_label_set_text(GTK_LABEL(ModeLabel),_("Channels:"));
+ text = g_strdup_printf("%d",ETFileInfo->mode);
+ gtk_label_set_text(GTK_LABEL(ModeValueLabel),text);
+ g_free(text);
+
+ /* Size */
+ size = Convert_Size(ETFileInfo->size);
+ size1 = Convert_Size(ETCore->ETFileDisplayedList_TotalSize);
+ text = g_strdup_printf("%s (%s)",size,size1);
+ gtk_label_set_text(GTK_LABEL(SizeValueLabel),text);
+ g_free(size);
+ g_free(size1);
+ g_free(text);
+
+ /* Duration */
+ time = Convert_Duration(ETFileInfo->duration);
+ time1 = Convert_Duration(ETCore->ETFileDisplayedList_TotalDuration);
+ text = g_strdup_printf("%s (%s)",time,time1);
+ gtk_label_set_text(GTK_LABEL(DurationValueLabel),text);
+ g_free(time);
+ g_free(time1);
+ g_free(text);
+
+ return TRUE;
+}
+
+#endif /* ENABLE_FLAC */
diff --git a/src/flac_header.h b/src/flac_header.h
new file mode 100755
index 0000000..7a7665f
--- /dev/null
+++ b/src/flac_header.h
@@ -0,0 +1,41 @@
+/* flac_header.h - 2002/07/03 */
+/*
+ * EasyTAG - Tag editor for MP3 and Ogg Vorbis files
+ * Copyright (C) 2000-2003 Jerome Couderc <easytag@gmail.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef __FLAC_HEADER_H__
+#define __FLAC_HEADER_H__
+
+
+#include "et_core.h"
+
+/****************
+ * Declarations *
+ ****************/
+
+
+/**************
+ * Prototypes *
+ **************/
+
+gboolean Flac_Header_Read_File_Info (gchar *filename, ET_File_Info *ETFileInfo);
+gboolean Flac_Header_Display_File_Info_To_UI (gchar *filename, ET_File_Info *ETFileInfo);
+
+
+#endif /* __FLAC_HEADER_H__ */
diff --git a/src/flac_tag.c b/src/flac_tag.c
new file mode 100755
index 0000000..a76e1cc
--- /dev/null
+++ b/src/flac_tag.c
@@ -0,0 +1,1029 @@
+/* flac_tag.c - 2003/12/27 */
+/*
+ * EasyTAG - Tag editor for MP3 and Ogg Vorbis files
+ * Copyright (C) 2001-2003 Jerome Couderc <easytag@gmail.com>
+ * Copyright (C) 2003 Pavel Minayev <thalion@front.ru>
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+
+#ifdef ENABLE_FLAC
+
+#include <gtk/gtk.h>
+#include <glib/gi18n-lib.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <FLAC/metadata.h>
+#include <unistd.h>
+
+#include "easytag.h"
+#include "flac_tag.h"
+#include "vcedit.h"
+#include "et_core.h"
+#include "id3_tag.h"
+#include "log.h"
+#include "misc.h"
+#include "setting.h"
+#include "picture.h"
+#include "charset.h"
+
+
+/***************
+ * Declaration *
+ ***************/
+
+#define MULTIFIELD_SEPARATOR " - "
+
+/* Patch from Josh Coalson
+ * FLAC 1.1.3 has FLAC_API_VERSION_CURRENT == 8 *
+ * by LEGACY_FLAC we mean pre-FLAC 1.1.3; in FLAC 1.1.3 the FLAC__FileDecoder was merged into the FLAC__StreamDecoder */
+#if !defined(FLAC_API_VERSION_CURRENT) || FLAC_API_VERSION_CURRENT < 8
+#define LEGACY_FLAC // For FLAC version < 1.1.3
+#else
+#undef LEGACY_FLAC
+#endif
+
+
+/* FLAC uses Ogg Vorbis comments
+ * Ogg Vorbis fields names :
+ * - TITLE : Track name
+ * - VERSION : The version field may be used to differentiate multiple version of the same track title in a single collection. (e.g. remix info)
+ * - ALBUM : The collection name to which this track belongs
+ * - TRACKNUMBER : The track number of this piece if part of a specific larger collection or album
+ * - TRACKTOTAL :
+ * - ARTIST : Track performer
+ * - ORGANIZATION : Name of the organization producing the track (i.e. the 'record label')
+ * - DESCRIPTION : A short text description of the contents
+ * - COMME?T : same than DESCRIPTION
+ * - GENRE : A short text indication of music genre
+ * - DATE : Date the track was recorded
+ * - LOCATION : Location where track was recorded
+ * - COPYRIGHT : Copyright information
+ * - ISRC : ISRC number for the track; see the ISRC intro page for more information on ISRC numbers.
+ *
+ * Field names should not be 'internationalized'; this is a concession to simplicity
+ * not an attempt to exclude the majority of the world that doesn't speak English.
+ * Field *contents*, however, are represented in UTF-8 to allow easy representation
+ * of any language.
+ */
+
+
+
+/**************
+ * Prototypes *
+ **************/
+gboolean Flac_Tag_Write_File (FILE *file_in, gchar *filename_in, vcedit_state *state);
+
+
+/*************
+ * Functions *
+ *************/
+
+/*
+ * Read tag data from a FLAC file.
+ * Note:
+ * - if field is found but contains no info (strlen(str)==0), we don't read it
+ */
+gboolean Flac_Tag_Read_File_Tag (gchar *filename, File_Tag *FileTag)
+{
+ FLAC__Metadata_SimpleIterator *iter;
+ FLAC__StreamMetadata *vc_block;
+ FLAC__StreamMetadata_VorbisComment *vc;
+ FLAC__StreamMetadata_VorbisComment_Entry *field;
+ gchar *field_value;
+ gchar *field_value_tmp;
+ gchar *string = NULL;
+ gint field_num;
+ gint field_len;
+ guint i;
+
+
+ if (!filename || !FileTag)
+ return FALSE;
+
+ flac_error_msg = NULL;
+
+ iter = FLAC__metadata_simple_iterator_new();
+ if ( iter == NULL || !FLAC__metadata_simple_iterator_init(iter, filename, true, false) )
+ {
+ gchar *filename_utf8 = filename_to_display(filename);
+ if ( iter == NULL )
+ {
+#ifdef WIN32
+ const char ** const iter = FLAC__Metadata_SimpleIteratorStatusString; /* this is for win32 auto-import of this external symbol works */
+ flac_error_msg = iter[FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR];
+#else
+ flac_error_msg = FLAC__Metadata_SimpleIteratorStatusString[FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR];
+#endif
+ }else
+ {
+ flac_error_msg = FLAC__Metadata_SimpleIteratorStatusString[FLAC__metadata_simple_iterator_status(iter)];
+ FLAC__metadata_simple_iterator_delete(iter);
+ }
+
+ Log_Print(_("ERROR while opening file: '%s' as FLAC (%s)."),filename_utf8,flac_error_msg);
+ g_free(filename_utf8);
+ return FALSE;
+ }
+
+ /* libFLAC is able to detect (and skip) ID3v2 tags by itself */
+
+ /* Find the VORBIS_COMMENT block */
+ while ( FLAC__metadata_simple_iterator_get_block_type(iter) != FLAC__METADATA_TYPE_VORBIS_COMMENT )
+ {
+ if ( !FLAC__metadata_simple_iterator_next(iter) )
+ {
+ /* End of metadata: comment block not found, nothing to read */
+ FLAC__metadata_simple_iterator_delete(iter);
+ return TRUE;
+ }
+ }
+
+ /* Get comments from block */
+ vc_block = FLAC__metadata_simple_iterator_get_block(iter);
+ vc = &vc_block->data.vorbis_comment;
+
+ /* Get vendor string */
+ /*{
+ FLAC__StreamMetadata_VorbisComment_Entry vce;
+ vce = vc->vendor_string;
+ g_print("File %s : \n",filename);
+ g_print(" - FLAC File vendor string : '%s'\n",g_strndup(vce.entry,vce.length));
+ g_print(" - FLAC Lib vendor string : '%s'\n",FLAC__VENDOR_STRING);
+ }*/
+
+
+ /*********
+ * Title *
+ *********/
+ field_num = 0;
+ while ( (field_num = FLAC__metadata_object_vorbiscomment_find_entry_from(vc_block,field_num,"TITLE")) >= 0 )
+ {
+ /* Extract field value */
+ field = &vc->comments[field_num++];
+ field_value = memchr(field->entry, '=', field->length);
+
+ if (field_value)
+ {
+ field_value++;
+ if ( field_value && g_utf8_strlen(field_value, -1) > 0 )
+ {
+ field_len = field->length - (field_value - (gchar*) field->entry);
+ field_value_tmp = g_strndup(field_value, field_len);
+ field_value = Try_To_Validate_Utf8_String(field_value_tmp);
+ g_free(field_value_tmp);
+ if (FileTag->title==NULL)
+ FileTag->title = g_strdup(field_value);
+ else
+ FileTag->title = g_strconcat(FileTag->title,MULTIFIELD_SEPARATOR,field_value,NULL);
+ g_free(field_value);
+ }
+ }
+ }
+
+ /**********
+ * Artist *
+ **********/
+ field_num = 0;
+ while ( (field_num = FLAC__metadata_object_vorbiscomment_find_entry_from(vc_block,field_num,"ARTIST")) >= 0 )
+ {
+ /* Extract field value */
+ field = &vc->comments[field_num++];
+ field_value = memchr(field->entry, '=', field->length);
+
+ if (field_value)
+ {
+ field_value++;
+ if ( field_value && g_utf8_strlen(field_value, -1) > 0 )
+ {
+ field_len = field->length - (field_value - (gchar*) field->entry);
+ field_value_tmp = g_strndup(field_value, field_len);
+ field_value = Try_To_Validate_Utf8_String(field_value_tmp);
+ g_free(field_value_tmp);
+ if (FileTag->artist==NULL)
+ FileTag->artist = g_strdup(field_value);
+ else
+ FileTag->artist = g_strconcat(FileTag->artist,MULTIFIELD_SEPARATOR,field_value,NULL);
+ g_free(field_value);
+ }
+ }
+ }
+
+ /*********
+ * Album *
+ *********/
+ field_num = 0;
+ while ( (field_num = FLAC__metadata_object_vorbiscomment_find_entry_from(vc_block,field_num,"ALBUM")) >= 0 )
+ {
+ /* Extract field value */
+ field = &vc->comments[field_num++];
+ field_value = memchr(field->entry, '=', field->length);
+
+ if (field_value)
+ {
+ field_value++;
+ if ( field_value && g_utf8_strlen(field_value, -1) > 0 )
+ {
+ field_len = field->length - (field_value - (gchar*) field->entry);
+ field_value_tmp = g_strndup(field_value, field_len);
+ field_value = Try_To_Validate_Utf8_String(field_value_tmp);
+ g_free(field_value_tmp);
+ if (FileTag->album==NULL)
+ FileTag->album = g_strdup(field_value);
+ else
+ FileTag->album = g_strconcat(FileTag->album,MULTIFIELD_SEPARATOR,field_value,NULL);
+ g_free(field_value);
+ }
+ }
+ }
+
+ /***************
+ * Disc Number *
+ ***************/
+ if ( (field_num = FLAC__metadata_object_vorbiscomment_find_entry_from(vc_block,0,"DISCNUMBER")) >= 0 )
+ {
+ /* Extract field value */
+ field = &vc->comments[field_num];
+ field_value = memchr(field->entry, '=', field->length);
+
+ if (field_value)
+ {
+ field_value++;
+ if ( field_value && g_utf8_strlen(field_value, -1) > 0 )
+ {
+ field_len = field->length - (field_value - (gchar*) field->entry);
+ field_value_tmp = g_strndup(field_value, field_len);
+ field_value = Try_To_Validate_Utf8_String(field_value_tmp);
+ g_free(field_value_tmp);
+ FileTag->disc_number = field_value;
+ }
+ }
+ }
+
+ /********
+ * Year *
+ ********/
+ if ( (field_num = FLAC__metadata_object_vorbiscomment_find_entry_from(vc_block,0,"DATE")) >= 0 )
+ {
+ /* Extract field value */
+ field = &vc->comments[field_num];
+ field_value = memchr(field->entry, '=', field->length);
+
+ if (field_value)
+ {
+ field_value++;
+ if ( field_value && g_utf8_strlen(field_value, -1) > 0 )
+ {
+ field_len = field->length - (field_value - (gchar*) field->entry);
+ field_value_tmp = g_strndup(field_value, field_len);
+ field_value = Try_To_Validate_Utf8_String(field_value_tmp);
+ g_free(field_value_tmp);
+ FileTag->year = field_value;
+ }
+ }
+ }
+
+ /*************************
+ * Track and Total Track *
+ *************************/
+ if ( (field_num = FLAC__metadata_object_vorbiscomment_find_entry_from(vc_block,0,"TRACKTOTAL")) >= 0 )
+ {
+ /* Extract field value */
+ field = &vc->comments[field_num];
+ field_value = memchr(field->entry, '=', field->length);
+
+ if (field_value)
+ {
+ field_value++;
+ if ( field_value && g_utf8_strlen(field_value, -1) > 0 )
+ {
+ field_len = field->length - (field_value - (gchar*) field->entry);
+ field_value_tmp = g_strndup(field_value, field_len);
+ field_value = Try_To_Validate_Utf8_String(field_value_tmp);
+ g_free(field_value_tmp);
+ if (NUMBER_TRACK_FORMATED)
+ {
+ FileTag->track_total = g_strdup_printf("%.*d",NUMBER_TRACK_FORMATED_SPIN_BUTTON,atoi(field_value));
+ }else
+ {
+ FileTag->track_total = g_strdup(field_value);
+ }
+ g_free(field_value);
+ }
+ }
+ // Below is also filled track_total if not done here
+ }
+
+ if ( (field_num = FLAC__metadata_object_vorbiscomment_find_entry_from(vc_block,0,"TRACKNUMBER")) >= 0 )
+ {
+ /* Extract field value */
+ field = &vc->comments[field_num];
+ field_value = memchr(field->entry, '=', field->length);
+
+ if (field_value)
+ {
+ field_value++;
+ if ( field_value && g_utf8_strlen(field_value, -1) > 0 )
+ {
+ field_len = field->length - (field_value - (gchar*) field->entry);
+ field_value_tmp = g_strndup(field_value, field_len);
+ field_value = Try_To_Validate_Utf8_String(field_value_tmp);
+ g_free(field_value_tmp);
+ string = g_utf8_strchr(field_value, -1, '/');
+ if (NUMBER_TRACK_FORMATED)
+ {
+ // If track_total not filled before, try now...
+ if (string && !FileTag->track_total)
+ {
+ FileTag->track_total = g_strdup_printf("%.*d",NUMBER_TRACK_FORMATED_SPIN_BUTTON,atoi(string+1));
+ *string = '\0';
+ }
+ FileTag->track = g_strdup_printf("%.*d",NUMBER_TRACK_FORMATED_SPIN_BUTTON,atoi(field_value));
+ }else
+ {
+ if (string && !FileTag->track_total)
+ {
+ FileTag->track_total = g_strdup(string+1);
+ *string = '\0';
+ }
+ FileTag->track = g_strdup(field_value);
+ }
+ g_free(field_value);
+ }
+ }
+ }
+
+ /*********
+ * Genre *
+ *********/
+ field_num = 0;
+ while ( (field_num = FLAC__metadata_object_vorbiscomment_find_entry_from(vc_block,field_num,"GENRE")) >= 0 )
+ {
+ /* Extract field value */
+ field = &vc->comments[field_num++];
+ field_value = memchr(field->entry, '=', field->length);
+
+ if (field_value)
+ {
+ field_value++;
+ if ( field_value && g_utf8_strlen(field_value, -1) > 0 )
+ {
+ field_len = field->length - (field_value - (gchar*) field->entry);
+ field_value_tmp = g_strndup(field_value, field_len);
+ field_value = Try_To_Validate_Utf8_String(field_value_tmp);
+ g_free(field_value_tmp);
+ if (FileTag->genre==NULL)
+ FileTag->genre = g_strdup(field_value);
+ else
+ FileTag->genre = g_strconcat(FileTag->genre,MULTIFIELD_SEPARATOR,field_value,NULL);
+ g_free(field_value);
+ }
+ }
+ }
+
+ /***********
+ * Comment *
+ ***********/
+ field_num = 0;
+ while ( 1 )
+ {
+ gint field_num1, field_num2;
+
+ // The comment field can take two forms...
+ field_num1 = FLAC__metadata_object_vorbiscomment_find_entry_from(vc_block,field_num,"DESCRIPTION");
+ field_num2 = FLAC__metadata_object_vorbiscomment_find_entry_from(vc_block,field_num,"COMMENT");
+
+ if (field_num1 >= 0 && field_num2 >= 0)
+ // Note : We set field_num to the last "comment" field to avoid to concatenate
+ // the DESCRIPTION and COMMENT field if there are both present (EasyTAG writes the both...)
+ if (field_num1 < field_num2)
+ field_num = field_num2;
+ else
+ field_num = field_num1;
+ else if (field_num1 >= 0)
+ field_num = field_num1;
+ else if (field_num2 >= 0)
+ field_num = field_num2;
+ else
+ break;
+
+ /* Extract field value */
+ field = &vc->comments[field_num++];
+ field_value = memchr(field->entry, '=', field->length);
+
+ if (field_value)
+ {
+ field_value++;
+ if ( field_value && g_utf8_strlen(field_value, -1) > 0 )
+ {
+ field_len = field->length - (field_value - (gchar*) field->entry);
+ field_value_tmp = g_strndup(field_value, field_len);
+ field_value = Try_To_Validate_Utf8_String(field_value_tmp);
+ g_free(field_value_tmp);
+ if (FileTag->comment==NULL)
+ FileTag->comment = g_strdup(field_value);
+ else
+ FileTag->comment = g_strconcat(FileTag->comment,MULTIFIELD_SEPARATOR,field_value,NULL);
+ g_free(field_value);
+ }
+ }
+ }
+
+ /************
+ * Composer *
+ ************/
+ field_num = 0;
+ while ( (field_num = FLAC__metadata_object_vorbiscomment_find_entry_from(vc_block,field_num,"COMPOSER")) >= 0 )
+ {
+ /* Extract field value */
+ field = &vc->comments[field_num++];
+ field_value = memchr(field->entry, '=', field->length);
+
+ if (field_value)
+ {
+ field_value++;
+ if ( field_value && g_utf8_strlen(field_value, -1) > 0 )
+ {
+ field_len = field->length - (field_value - (gchar*) field->entry);
+ field_value_tmp = g_strndup(field_value, field_len);
+ field_value = Try_To_Validate_Utf8_String(field_value_tmp);
+ g_free(field_value_tmp);
+ if (FileTag->composer==NULL)
+ FileTag->composer = g_strdup(field_value);
+ else
+ FileTag->composer = g_strconcat(FileTag->composer,MULTIFIELD_SEPARATOR,field_value,NULL);
+ g_free(field_value);
+ }
+ }
+ }
+
+ /*******************
+ * Original artist *
+ *******************/
+ field_num = 0;
+ while ( (field_num = FLAC__metadata_object_vorbiscomment_find_entry_from(vc_block,field_num,"PERFORMER")) >= 0 )
+ {
+ /* Extract field value */
+ field = &vc->comments[field_num++];
+ field_value = memchr(field->entry, '=', field->length);
+
+ if (field_value)
+ {
+ field_value++;
+ if ( field_value && g_utf8_strlen(field_value, -1) > 0 )
+ {
+ field_len = field->length - (field_value - (gchar*) field->entry);
+ field_value_tmp = g_strndup(field_value, field_len);
+ field_value = Try_To_Validate_Utf8_String(field_value_tmp);
+ g_free(field_value_tmp);
+ if (FileTag->orig_artist==NULL)
+ FileTag->orig_artist = g_strdup(field_value);
+ else
+ FileTag->orig_artist = g_strconcat(FileTag->orig_artist,MULTIFIELD_SEPARATOR,field_value,NULL);
+ g_free(field_value);
+ }
+ }
+ }
+
+ /*************
+ * Copyright *
+ *************/
+ field_num = 0;
+ while ( (field_num = FLAC__metadata_object_vorbiscomment_find_entry_from(vc_block,field_num,"COPYRIGHT")) >= 0 )
+ {
+ /* Extract field value */
+ field = &vc->comments[field_num++];
+ field_value = memchr(field->entry, '=', field->length);
+
+ if (field_value)
+ {
+ field_value++;
+ if ( field_value && g_utf8_strlen(field_value, -1) > 0 )
+ {
+ field_len = field->length - (field_value - (gchar*) field->entry);
+ field_value_tmp = g_strndup(field_value, field_len);
+ field_value = Try_To_Validate_Utf8_String(field_value_tmp);
+ g_free(field_value_tmp);
+ if (FileTag->copyright==NULL)
+ FileTag->copyright = g_strdup(field_value);
+ else
+ FileTag->copyright = g_strconcat(FileTag->copyright,MULTIFIELD_SEPARATOR,field_value,NULL);
+ g_free(field_value);
+ }
+ }
+ }
+
+ /*******
+ * URL *
+ *******/
+ field_num = 0;
+ while ( (field_num = FLAC__metadata_object_vorbiscomment_find_entry_from(vc_block,field_num,"LICENSE")) >= 0 )
+ {
+ /* Extract field value */
+ field = &vc->comments[field_num++];
+ field_value = memchr(field->entry, '=', field->length);
+
+ if (field_value)
+ {
+ field_value++;
+ if ( field_value && g_utf8_strlen(field_value, -1) > 0 )
+ {
+ field_len = field->length - (field_value - (gchar*) field->entry);
+ field_value_tmp = g_strndup(field_value, field_len);
+ field_value = Try_To_Validate_Utf8_String(field_value_tmp);
+ g_free(field_value_tmp);
+ if (FileTag->url==NULL)
+ FileTag->url = g_strdup(field_value);
+ else
+ FileTag->url = g_strconcat(FileTag->url,MULTIFIELD_SEPARATOR,field_value,NULL);
+ g_free(field_value);
+ }
+ }
+ }
+
+ /**************
+ * Encoded by *
+ **************/
+ field_num = 0;
+ while ( (field_num = FLAC__metadata_object_vorbiscomment_find_entry_from(vc_block,field_num,"ENCODED-BY")) >= 0 )
+ {
+ /* Extract field value */
+ field = &vc->comments[field_num++];
+ field_value = memchr(field->entry, '=', field->length);
+
+ if (field_value)
+ {
+ field_value++;
+ if ( field_value && g_utf8_strlen(field_value, -1) > 0 )
+ {
+ field_len = field->length - (field_value - (gchar*) field->entry);
+ field_value_tmp = g_strndup(field_value, field_len);
+ field_value = Try_To_Validate_Utf8_String(field_value_tmp);
+ g_free(field_value_tmp);
+ if (FileTag->encoded_by==NULL)
+ FileTag->encoded_by = g_strdup(field_value);
+ else
+ FileTag->encoded_by = g_strconcat(FileTag->encoded_by,MULTIFIELD_SEPARATOR,field_value,NULL);
+ g_free(field_value);
+ }
+ }
+ }
+
+
+ /***********
+ * Picture *
+ ***********/
+ // For FLAC > 1.1.3
+ #ifndef LEGACY_FLAC
+
+ #endif
+
+
+ /***************************
+ * Save unsupported fields *
+ ***************************/
+ for (i=0;i<(guint)vc->num_comments;i++)
+ {
+ field = &vc->comments[i];
+ if ( strncasecmp((gchar *)field->entry,"TITLE=", MIN(6, field->length)) != 0
+ && strncasecmp((gchar *)field->entry,"ARTIST=", MIN(7, field->length)) != 0
+ && strncasecmp((gchar *)field->entry,"ALBUM=", MIN(6, field->length)) != 0
+ && strncasecmp((gchar *)field->entry,"DISCNUMBER=", MIN(11, field->length)) != 0
+ && strncasecmp((gchar *)field->entry,"DATE=", MIN(5, field->length)) != 0
+ && strncasecmp((gchar *)field->entry,"TRACKNUMBER=", MIN(12, field->length)) != 0
+ && strncasecmp((gchar *)field->entry,"TRACKTOTAL=", MIN(11, field->length)) != 0
+ && strncasecmp((gchar *)field->entry,"GENRE=", MIN(6, field->length)) != 0
+ && strncasecmp((gchar *)field->entry,"DESCRIPTION=", MIN(12, field->length)) != 0
+ && strncasecmp((gchar *)field->entry,"COMMENT=", MIN(8, field->length)) != 0
+ && strncasecmp((gchar *)field->entry,"COMPOSER=", MIN(9, field->length)) != 0
+ && strncasecmp((gchar *)field->entry,"PERFORMER=", MIN(10, field->length)) != 0
+ && strncasecmp((gchar *)field->entry,"COPYRIGHT=", MIN(10, field->length)) != 0
+ && strncasecmp((gchar *)field->entry,"LICENSE=", MIN(8, field->length)) != 0
+ && strncasecmp((gchar *)field->entry,"ENCODED-BY=", MIN(11, field->length)) != 0 )
+ {
+ //g_print("custom %*s\n", field->length, field->entry);
+ FileTag->other = g_list_append(FileTag->other,g_strndup((const gchar *)field->entry, field->length));
+ }
+ }
+
+ FLAC__metadata_object_delete(vc_block);
+ FLAC__metadata_simple_iterator_delete(iter);
+
+#ifdef ENABLE_MP3
+ /* If no FLAC vorbis tag found : we try to get the ID3 tag if it exists
+ * (will be deleted when writing the tag */
+ if ( FileTag->title == NULL
+ && FileTag->artist == NULL
+ && FileTag->album == NULL
+ && FileTag->disc_number == NULL
+ && FileTag->year == NULL
+ && FileTag->track == NULL
+ && FileTag->track_total == NULL
+ && FileTag->genre == NULL
+ && FileTag->comment == NULL
+ && FileTag->composer == NULL
+ && FileTag->orig_artist == NULL
+ && FileTag->copyright == NULL
+ && FileTag->url == NULL
+ && FileTag->encoded_by == NULL)
+ {
+ gint rc = Id3tag_Read_File_Tag(filename,FileTag);
+
+ // If an ID3 tag has been found (and no FLAC tag), we mark the file as
+ // unsaved to rewrite a flac tag
+ if ( FileTag->title != NULL
+ || FileTag->artist != NULL
+ || FileTag->album != NULL
+ || FileTag->disc_number != NULL
+ || FileTag->year != NULL
+ || FileTag->track != NULL
+ || FileTag->track_total != NULL
+ || FileTag->genre != NULL
+ || FileTag->comment != NULL
+ || FileTag->composer != NULL
+ || FileTag->orig_artist != NULL
+ || FileTag->copyright != NULL
+ || FileTag->url != NULL
+ || FileTag->encoded_by != NULL)
+ {
+ FileTag->saved = FALSE;
+ }
+
+ return rc;
+ }
+
+ /* Part to get cover artist :
+ * If we have read the ID3 tag previously we don't arrive here (and we have
+ * the picture if it exists)
+ * Else the ID3 tag wasn't read (as there was data in FLAC tag) so we try
+ * to read it only to get the picture (not supported by the FLAC tag) */
+ if (WRITE_ID3_TAGS_IN_FLAC_FILE && FileTag->picture == NULL)
+ {
+ File_Tag *FileTag_tmp = ET_File_Tag_Item_New();
+ gint rc = Id3tag_Read_File_Tag(filename,FileTag_tmp);
+ if (rc && FileTag_tmp->picture)
+ {
+ // Copy picture to FileTag
+ FileTag->picture = Picture_Copy(FileTag_tmp->picture);
+ }
+
+ ET_Free_File_Tag_Item(FileTag_tmp);
+
+ return rc;
+ }
+#endif
+
+ return TRUE;
+}
+
+
+gboolean Flac_Tag_Write_File_Tag (ET_File *ETFile)
+{
+ File_Tag *FileTag;
+ gchar *filename_utf8, *filename;
+ gchar *basename_utf8;
+ FLAC__Metadata_SimpleIterator *iter;
+ FLAC__StreamMetadata *vc_block;
+ FLAC__StreamMetadata_VorbisComment_Entry field;
+ FLAC__bool write_ok;
+ gchar *string;
+ GList *list;
+ // To get original vendor string
+ FLAC__StreamMetadata *vc_block_svg = NULL;
+ FLAC__StreamMetadata_VorbisComment *vc_svg;
+ FLAC__StreamMetadata_VorbisComment_Entry field_svg;
+ gboolean vc_found_svg = TRUE;
+
+
+ if (!ETFile || !ETFile->FileTag)
+ return FALSE;
+
+ FileTag = (File_Tag *)ETFile->FileTag->data;
+ filename = ((File_Name *)ETFile->FileNameCur->data)->value;
+ filename_utf8 = ((File_Name *)ETFile->FileNameCur->data)->value_utf8;
+ flac_error_msg = NULL;
+
+ /* libFLAC is able to detect (and skip) ID3v2 tags by itself */
+
+ iter = FLAC__metadata_simple_iterator_new();
+ if ( iter == NULL || !FLAC__metadata_simple_iterator_init(iter,filename,false,false) )
+ {
+ if ( iter == NULL )
+ {
+#ifdef WIN32
+ const char **iter = FLAC__Metadata_SimpleIteratorStatusString; /* this is for win32 auto-import of this external symbol works */
+ flac_error_msg = iter[FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR];
+#else
+ flac_error_msg = FLAC__Metadata_SimpleIteratorStatusString[FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR];
+#endif
+ }else
+ {
+ flac_error_msg = FLAC__Metadata_SimpleIteratorStatusString[FLAC__metadata_simple_iterator_status(iter)];
+ FLAC__metadata_simple_iterator_delete(iter);
+ }
+
+ Log_Print(_("ERROR while opening file: '%s' as FLAC (%s)."),filename_utf8,flac_error_msg);
+ return FALSE;
+ }
+
+ /* Find the VORBIS_COMMENT block to get original vendor string */
+ while ( FLAC__metadata_simple_iterator_get_block_type(iter) != FLAC__METADATA_TYPE_VORBIS_COMMENT )
+ {
+ if ( !FLAC__metadata_simple_iterator_next(iter) )
+ {
+ /* End of metadata: comment block not found, nothing to read */
+ vc_found_svg = FALSE;
+ break;
+ }
+ }
+ if (vc_found_svg)
+ {
+ /* Get comments from block */
+ vc_block_svg = FLAC__metadata_simple_iterator_get_block(iter);
+ vc_svg = &vc_block_svg->data.vorbis_comment;
+ /* Get original vendor string */
+ field_svg = vc_svg->vendor_string;
+ }
+
+
+ vc_block = FLAC__metadata_object_new(FLAC__METADATA_TYPE_VORBIS_COMMENT);
+
+ /* Set the original vendor string else it is version of library */
+ if (vc_found_svg)
+ FLAC__metadata_object_vorbiscomment_set_vendor_string(vc_block, field_svg, true);
+
+
+ /*********
+ * Title *
+ *********/
+ if ( FileTag->title )
+ {
+ string = g_strconcat("TITLE=",FileTag->title,NULL);
+ field.entry = (FLAC__byte *)string;
+ field.length = strlen(string); // Warning : g_utf8_strlen doesn't count the multibyte characters. Here we need the allocated size.
+ FLAC__metadata_object_vorbiscomment_insert_comment(vc_block,vc_block->data.vorbis_comment.num_comments,field,true);
+ g_free(string);
+ }
+
+ /**********
+ * Artist *
+ **********/
+ if ( FileTag->artist )
+ {
+ string = g_strconcat("ARTIST=",FileTag->artist,NULL);
+ field.entry = (FLAC__byte *)string;
+ field.length = strlen(string);
+ FLAC__metadata_object_vorbiscomment_insert_comment(vc_block,vc_block->data.vorbis_comment.num_comments,field,true);
+ g_free(string);
+ }
+
+ /*********
+ * Album *
+ *********/
+ if ( FileTag->album )
+ {
+ string = g_strconcat("ALBUM=",FileTag->album,NULL);
+ field.entry = (FLAC__byte *)string;
+ field.length = strlen(string);
+ FLAC__metadata_object_vorbiscomment_insert_comment(vc_block,vc_block->data.vorbis_comment.num_comments,field,true);
+ g_free(string);
+ }
+
+ /***************
+ * Disc Number *
+ ***************/
+ if ( FileTag->disc_number )
+ {
+ string = g_strconcat("DISCNUMBER=",FileTag->disc_number,NULL);
+ field.entry = (FLAC__byte *)string;
+ field.length = strlen(string);
+ FLAC__metadata_object_vorbiscomment_insert_comment(vc_block,vc_block->data.vorbis_comment.num_comments,field,true);
+ g_free(string);
+ }
+
+ /********
+ * Year *
+ ********/
+ if ( FileTag->year )
+ {
+ string = g_strconcat("DATE=",FileTag->year,NULL);
+ field.entry = (FLAC__byte *)string;
+ field.length = strlen(string);
+ FLAC__metadata_object_vorbiscomment_insert_comment(vc_block,vc_block->data.vorbis_comment.num_comments,field,true);
+ g_free(string);
+ }
+
+ /*************************
+ * Track and Total Track *
+ *************************/
+ if ( FileTag->track )
+ {
+ string = g_strconcat("TRACKNUMBER=",FileTag->track,NULL);
+ field.entry = (FLAC__byte *)string;
+ field.length = strlen(string);
+ FLAC__metadata_object_vorbiscomment_insert_comment(vc_block,vc_block->data.vorbis_comment.num_comments,field,true);
+ g_free(string);
+ }
+ if ( FileTag->track_total /*&& strlen(FileTag->track_total)>0*/ )
+ {
+ string = g_strconcat("TRACKTOTAL=",FileTag->track_total,NULL);
+ field.entry = (FLAC__byte *)string;
+ field.length = strlen(string);
+ FLAC__metadata_object_vorbiscomment_insert_comment(vc_block,vc_block->data.vorbis_comment.num_comments,field,true);
+ g_free(string);
+ }
+
+ /*********
+ * Genre *
+ *********/
+ if ( FileTag->genre )
+ {
+ string = g_strconcat("GENRE=",FileTag->genre,NULL);
+ field.entry = (FLAC__byte *)string;
+ field.length = strlen(string);
+ FLAC__metadata_object_vorbiscomment_insert_comment(vc_block,vc_block->data.vorbis_comment.num_comments,field,true);
+ g_free(string);
+ }
+
+ /***********
+ * Comment *
+ ***********/
+ // We write the comment using the "both" format
+ if ( FileTag->comment )
+ {
+ string = g_strconcat("DESCRIPTION=",FileTag->comment,NULL);
+ field.entry = (FLAC__byte *)string;
+ field.length = strlen(string);
+ FLAC__metadata_object_vorbiscomment_insert_comment(vc_block,vc_block->data.vorbis_comment.num_comments,field,true);
+ g_free(string);
+
+ string = g_strconcat("COMMENT=",FileTag->comment,NULL);
+ field.entry = (FLAC__byte *)string;
+ field.length = strlen(string);
+ FLAC__metadata_object_vorbiscomment_insert_comment(vc_block,vc_block->data.vorbis_comment.num_comments,field,true);
+ g_free(string);
+ }
+
+ /************
+ * Composer *
+ ************/
+ if ( FileTag->composer )
+ {
+ string = g_strconcat("COMPOSER=",FileTag->composer,NULL);
+ field.entry = (FLAC__byte *)string;
+ field.length = strlen(string);
+ FLAC__metadata_object_vorbiscomment_insert_comment(vc_block,vc_block->data.vorbis_comment.num_comments,field,true);
+ g_free(string);
+ }
+
+ /*******************
+ * Original artist *
+ *******************/
+ if ( FileTag->orig_artist )
+ {
+ string = g_strconcat("PERFORMER=",FileTag->orig_artist,NULL);
+ field.entry = (FLAC__byte *)string;
+ field.length = strlen(string);
+ FLAC__metadata_object_vorbiscomment_insert_comment(vc_block,vc_block->data.vorbis_comment.num_comments,field,true);
+ g_free(string);
+ }
+
+ /*************
+ * Copyright *
+ *************/
+ if ( FileTag->copyright )
+ {
+ string = g_strconcat("COPYRIGHT=",FileTag->copyright,NULL);
+ field.entry = (FLAC__byte *)string;
+ field.length = strlen(string);
+ FLAC__metadata_object_vorbiscomment_insert_comment(vc_block,vc_block->data.vorbis_comment.num_comments,field,true);
+ g_free(string);
+ }
+
+ /*******
+ * URL *
+ *******/
+ if ( FileTag->url )
+ {
+ string = g_strconcat("LICENSE=",FileTag->url,NULL);
+ field.entry = (FLAC__byte *)string;
+ field.length = strlen(string);
+ FLAC__metadata_object_vorbiscomment_insert_comment(vc_block,vc_block->data.vorbis_comment.num_comments,field,true);
+ g_free(string);
+ }
+
+ /**************
+ * Encoded by *
+ **************/
+ if ( FileTag->encoded_by )
+ {
+ string = g_strconcat("ENCODED-BY=",FileTag->encoded_by,NULL);
+ field.entry = (FLAC__byte *)string;
+ field.length = strlen(string);
+ FLAC__metadata_object_vorbiscomment_insert_comment(vc_block,vc_block->data.vorbis_comment.num_comments,field,true);
+ g_free(string);
+ }
+
+
+ /**************************
+ * Set unsupported fields *
+ **************************/
+ list = FileTag->other;
+ while (list)
+ {
+ if (list->data)
+ {
+ string = (gchar*)list->data;
+ field.entry = (FLAC__byte *)string;
+ field.length = strlen(string);
+ FLAC__metadata_object_vorbiscomment_insert_comment(vc_block,vc_block->data.vorbis_comment.num_comments,field,true);
+ }
+ list = list->next;
+ }
+
+ /* Find the VORBIS_COMMENT block */
+ while ( FLAC__metadata_simple_iterator_get_block_type(iter) != FLAC__METADATA_TYPE_VORBIS_COMMENT )
+ {
+ if ( !FLAC__metadata_simple_iterator_next(iter) )
+ break;
+ }
+
+
+ /*
+ * Write FLAC tag (as Vorbis comment)
+ */
+ if ( FLAC__metadata_simple_iterator_get_block_type(iter) != FLAC__METADATA_TYPE_VORBIS_COMMENT )
+ {
+ /* End of metadata: no comment block, so insert one */
+ write_ok = FLAC__metadata_simple_iterator_insert_block_after(iter,vc_block,true);
+ }else
+ {
+ write_ok = FLAC__metadata_simple_iterator_set_block(iter,vc_block,true);
+ }
+
+ if ( !write_ok )
+ {
+ flac_error_msg = FLAC__Metadata_SimpleIteratorStatusString[FLAC__metadata_simple_iterator_status(iter)];
+ Log_Print(_("ERROR: Failed to write comments to file '%s' (%s)."),filename_utf8,flac_error_msg);
+ FLAC__metadata_simple_iterator_delete(iter);
+ FLAC__metadata_object_delete(vc_block);
+ return FALSE;
+ }else
+ {
+ basename_utf8 = g_path_get_basename(filename_utf8);
+ Log_Print(_("Written tag of '%s'"),basename_utf8);
+ g_free(basename_utf8);
+ }
+
+ FLAC__metadata_simple_iterator_delete(iter);
+ FLAC__metadata_object_delete(vc_block);
+ if (vc_found_svg)
+ FLAC__metadata_object_delete(vc_block_svg);
+
+#ifdef ENABLE_MP3
+ /*
+ * Write also the ID3 tags (ID3v1 and/or ID3v2) if wanted (as needed by some players)
+ */
+ if (WRITE_ID3_TAGS_IN_FLAC_FILE)
+ {
+ Id3tag_Write_File_Tag(ETFile);
+ }else
+ {
+ // Delete the ID3 tags (create a dummy ETFile for the Id3tag_... function)
+ ET_File *ETFile_tmp = ET_File_Item_New();
+ File_Name *FileName_tmp = ET_File_Name_Item_New();
+ File_Tag *FileTag_tmp = ET_File_Tag_Item_New();
+ // Same file...
+ FileName_tmp->value = g_strdup(filename);
+ FileName_tmp->value_utf8 = g_strdup(filename_utf8); // Not necessary to fill 'value_ck'
+ ETFile_tmp->FileNameList = g_list_append(NULL,FileName_tmp);
+ ETFile_tmp->FileNameCur = ETFile_tmp->FileNameList;
+ // With empty tag...
+ ETFile_tmp->FileTagList = g_list_append(NULL,FileTag_tmp);
+ ETFile_tmp->FileTag = ETFile_tmp->FileTagList;
+ Id3tag_Write_File_Tag(ETFile_tmp);
+ ET_Free_File_List_Item(ETFile_tmp);
+ }
+#endif
+
+ return TRUE;
+}
+
+
+#endif /* ENABLE_FLAC */
diff --git a/src/flac_tag.h b/src/flac_tag.h
new file mode 100755
index 0000000..99ad489
--- /dev/null
+++ b/src/flac_tag.h
@@ -0,0 +1,43 @@
+/* flac_tag.h - 2003/12/27 */
+/*
+ * EasyTAG - Tag editor for MP3 and Ogg Vorbis files
+ * Copyright (C) 2001-2003 Jerome Couderc <easytag@gmail.com>
+ * Copyright (C) 2003 Pavel Minayev <thalion@front.ru>
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef __FLAC_TAG_H__
+#define __FLAC_TAG_H__
+
+
+#include <glib.h>
+#include "et_core.h"
+
+/***************
+ * Declaration *
+ ***************/
+const gchar *flac_error_msg;
+
+
+/**************
+ * Prototypes *
+ **************/
+gboolean Flac_Tag_Read_File_Tag (gchar *filename, File_Tag *FileTag);
+gboolean Flac_Tag_Write_File_Tag (ET_File *ETFile);
+
+
+#endif /* __FLAC_TAG_H__ */
diff --git a/src/genres.h b/src/genres.h
new file mode 100755
index 0000000..24f297b
--- /dev/null
+++ b/src/genres.h
@@ -0,0 +1,195 @@
+/* genres.h - EasyTAG - Jerome Couderc 2000/05/29 */
+/*
+ * EasyTAG - Tag editor for MP3 and Ogg Vorbis files
+ * Copyright (C) 2000-2003 Jerome Couderc <easytag@gmail.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef __GENRES_H__
+#define __GENRES_H__
+
+
+/* GENRE_MAX is the last genre number that can be used */
+#define GENRE_MAX ( sizeof(id3_genres)/sizeof(id3_genres[0]) - 1 )
+#define ID3_INVALID_GENRE 255
+
+/**
+ \def genre_no(IndeX)
+ \param IndeX number of genre using in id3v1
+ \return pointer to genre as string
+*/
+#define genre_no(IndeX) ( IndeX < (sizeof(id3_genres)/sizeof(*id3_genres) ) ? id3_genres[IndeX] : "Unknown" )
+
+
+/*
+ * Do not sort genres!!
+ * Last Update: 2000/04/30
+ */
+static char *id3_genres[] =
+{
+ "Blues", /* 0 */
+ "Classic Rock",
+ "Country",
+ "Dance",
+ "Disco",
+ "Funk", /* 5 */
+ "Grunge",
+ "Hip-Hop",
+ "Jazz",
+ "Metal",
+ "New Age", /* 10 */
+ "Oldies",
+ "Other",
+ "Pop",
+ "R&B",
+ "Rap", /* 15 */
+ "Reggae",
+ "Rock",
+ "Techno",
+ "Industrial",
+ "Alternative", /* 20 */
+ "Ska",
+ "Death Metal",
+ "Pranks",
+ "Soundtrack",
+ "Euro-Techno", /* 25 */
+ "Ambient",
+ "Trip-Hop",
+ "Vocal",
+ "Jazz+Funk",
+ "Fusion", /* 30 */
+ "Trance",
+ "Classical",
+ "Instrumental",
+ "Acid",
+ "House", /* 35 */
+ "Game",
+ "Sound Clip",
+ "Gospel",
+ "Noise",
+ "Altern Rock", /* 40 */
+ "Bass",
+ "Soul",
+ "Punk",
+ "Space",
+ "Meditative", /* 45 */
+ "Instrumental Pop",
+ "Instrumental Rock",
+ "Ethnic",
+ "Gothic",
+ "Darkwave", /* 50 */
+ "Techno-Industrial",
+ "Electronic",
+ "Pop-Folk",
+ "Eurodance",
+ "Dream", /* 55 */
+ "Southern Rock",
+ "Comedy",
+ "Cult",
+ "Gangsta",
+ "Top 40", /* 60 */
+ "Christian Rap",
+ "Pop/Funk",
+ "Jungle",
+ "Native American",
+ "Cabaret", /* 65 */
+ "New Wave",
+ "Psychadelic",
+ "Rave",
+ "Showtunes",
+ "Trailer", /* 70 */
+ "Lo-Fi",
+ "Tribal",
+ "Acid Punk",
+ "Acid Jazz",
+ "Polka", /* 75 */
+ "Retro",
+ "Musical",
+ "Rock & Roll",
+ "Hard Rock",
+ "Folk", /* 80 */
+ "Folk/Rock",
+ "National Folk",
+ "Swing",
+ "Fast Fusion",
+ "Bebob", /* 85 */
+ "Latin",
+ "Revival",
+ "Celtic",
+ "Bluegrass",
+ "Avantgarde", /* 90 */
+ "Gothic Rock",
+ "Progressive Rock",
+ "Psychedelic Rock",
+ "Symphonic Rock",
+ "Slow Rock", /* 95 */
+ "Big Band",
+ "Chorus",
+ "Easy Listening",
+ "Acoustic",
+ "Humour", /* 100 */
+ "Speech",
+ "Chanson",
+ "Opera",
+ "Chamber Music",
+ "Sonata", /* 105 */
+ "Symphony",
+ "Booty Bass",
+ "Primus",
+ "Porn Groove",
+ "Satire", /* 110 */
+ "Slow Jam",
+ "Club",
+ "Tango",
+ "Samba",
+ "Folklore", /* 115 */
+ "Ballad",
+ "Power Ballad",
+ "Rhythmic Soul",
+ "Freestyle",
+ "Duet", /* 120 */
+ "Punk Rock",
+ "Drum Solo",
+ "A Capella",
+ "Euro-House",
+ "Dance Hall", /* 125 */
+ "Goa",
+ "Drum & Bass",
+ "Club-House",
+ "Hardcore",
+ "Terror", /* 130 */
+ "Indie",
+ "BritPop",
+ "Negerpunk",
+ "Polsk Punk",
+ "Beat", /* 135 */
+ "Christian Gangsta Rap",
+ "Heavy Metal",
+ "Black Metal",
+ "Crossover",
+ "Contemporary Christian",/* 140 */
+ "Christian Rock",
+ "Merengue",
+ "Salsa",
+ "Thrash Metal",
+ "Anime", /* 145 */
+ "JPop",
+ "Synthpop"
+};
+
+
+#endif /* __GENRES_H__ */
diff --git a/src/id3_tag.c b/src/id3_tag.c
new file mode 100755
index 0000000..9092fee
--- /dev/null
+++ b/src/id3_tag.c
@@ -0,0 +1,1362 @@
+/* id3tag.c - 2001/02/16 */
+/*
+ * EasyTAG - Tag editor for MP3 and Ogg Vorbis files
+ * Copyright (C) 2001-2003 Jerome Couderc <easytag@gmail.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+
+#include <gtk/gtk.h>
+#include <glib/gi18n-lib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+
+#include "id3_tag.h"
+#include "ape_tag.h"
+#include "picture.h"
+#include "easytag.h"
+#include "browser.h"
+#include "genres.h"
+#include "setting.h"
+#include "log.h"
+#include "misc.h"
+#include "et_core.h"
+#include "msgbox.h"
+#include "charset.h"
+
+#ifdef ENABLE_MP3
+
+#ifdef ENABLE_ID3LIB
+#include <id3.h>
+#include "id3lib/id3_bugfix.h"
+#endif
+
+/****************
+ * Declarations *
+ ****************/
+#define ID3V2_MAX_STRING_LEN 4096
+#define MULTIFIELD_SEPARATOR " - "
+
+
+#ifdef ENABLE_ID3LIB
+
+/**************
+ * Prototypes *
+ **************/
+gchar *Id3tag_Get_Error_Message (ID3_Err error);
+void Id3tag_Prepare_ID3v1 (ID3Tag *id3_tag);
+gchar *Id3tag_Rules_For_ISO_Fields (const gchar *string, const gchar *from_codeset, const gchar *to_codeset);
+gchar *Id3tag_Get_Field (const ID3Frame *id3_frame, ID3_FieldID id3_fieldid);
+ID3_TextEnc Id3tag_Set_Field (const ID3Frame *id3_frame, ID3_FieldID id3_fieldid, gchar *string);
+
+ID3_C_EXPORT size_t ID3Tag_Link_1 (ID3Tag *id3tag, const char *filename);
+ID3_C_EXPORT size_t ID3Field_GetASCII_1 (const ID3Field *field, char *buffer, size_t maxChars, size_t itemNum);
+ID3_C_EXPORT size_t ID3Field_GetUNICODE_1 (const ID3Field *field, unicode_t *buffer, size_t maxChars, size_t itemNum);
+
+gboolean Id3tag_Check_If_File_Is_Corrupted (gchar *filename);
+
+gboolean Id3tag_Check_If_Id3lib_Is_Bugged (void);
+
+
+
+/*************
+ * Functions *
+ *************/
+
+/*
+ * Write the ID3 tags to the file. Returns TRUE on success, else 0.
+ */
+gboolean Id3tag_Write_File_v23Tag (ET_File *ETFile)
+{
+ File_Tag *FileTag;
+ gchar *filename;
+ gchar *filename_utf8;
+ gchar *basename_utf8;
+ //gchar *temp;
+ FILE *file;
+ ID3Tag *id3_tag = NULL;
+ ID3_Err error_strip_id3v1 = ID3E_NoError;
+ ID3_Err error_strip_id3v2 = ID3E_NoError;
+ ID3_Err error_update_id3v1 = ID3E_NoError;
+ ID3_Err error_update_id3v2 = ID3E_NoError;
+ gint error = 0;
+ gint number_of_frames;
+ gboolean has_title = FALSE;
+ gboolean has_artist = FALSE;
+ gboolean has_album = FALSE;
+ gboolean has_disc_number = FALSE;
+ gboolean has_year = FALSE;
+ gboolean has_track = FALSE;
+ gboolean has_genre = FALSE;
+ gboolean has_comment = FALSE;
+ gboolean has_composer = FALSE;
+ gboolean has_orig_artist = FALSE;
+ gboolean has_copyright = FALSE;
+ gboolean has_url = FALSE;
+ gboolean has_encoded_by = FALSE;
+ gboolean has_picture = FALSE;
+ //gboolean has_song_len = FALSE;
+ static gboolean flag_first_check = TRUE;
+ static gboolean flag_id3lib_bugged = TRUE;
+
+ ID3Frame *id3_frame;
+ ID3Field *id3_field;
+ //gchar *string;
+ gchar *string1;
+ Picture *pic;
+
+
+ // When writing the first MP3 file, we check if the version of id3lib of the
+ // system doesn't contain a bug when writting Unicode tags
+ if (flag_first_check
+ && (FILE_WRITING_ID3V2_USE_UNICODE_CHARACTER_SET) )
+ {
+ flag_first_check = FALSE;
+ flag_id3lib_bugged = Id3tag_Check_If_Id3lib_Is_Bugged();
+ }
+
+ if (!ETFile || !ETFile->FileTag)
+ return FALSE;
+
+ FileTag = (File_Tag *)ETFile->FileTag->data;
+ filename = ((File_Name *)ETFile->FileNameCur->data)->value;
+ filename_utf8 = ((File_Name *)ETFile->FileNameCur->data)->value_utf8;
+
+ /* Test to know if we can write into the file */
+ if ( (file=fopen(filename,"r+"))==NULL )
+ {
+ Log_Print(_("ERROR while opening file: '%s' (%s)."),filename_utf8,g_strerror(errno));
+ return FALSE;
+ }
+ fclose(file);
+
+ /* This is a protection against a bug in id3lib that generate an infinite
+ * loop with corrupted MP3 files (files containing only zeroes) */
+ if (Id3tag_Check_If_File_Is_Corrupted(filename))
+ return FALSE;
+
+ /* We get again the tag from the file to keep also unused data (by EasyTAG), then
+ * we replace the changed data */
+ if ( (id3_tag = ID3Tag_New()) == NULL )
+ return FALSE;
+
+ basename_utf8 = g_path_get_basename(filename_utf8);
+
+ ID3Tag_Link(id3_tag,filename);
+
+ /* Set padding when tag was changed, for faster writing */
+ ID3Tag_SetPadding(id3_tag,TRUE);
+
+
+ /*********
+ * Title *
+ *********/
+ // To avoid problem with a corrupted field, we remove it before to create a new one.
+ while ( (id3_frame = ID3Tag_FindFrameWithID(id3_tag,ID3FID_TITLE)) )
+ ID3Tag_RemoveFrame(id3_tag,id3_frame);
+
+ if (FileTag->title && g_utf8_strlen(FileTag->title, -1) > 0)
+ {
+ id3_frame = ID3Frame_NewID(ID3FID_TITLE);
+ ID3Tag_AttachFrame(id3_tag,id3_frame);
+ Id3tag_Set_Field(id3_frame, ID3FN_TEXT, FileTag->title);
+ has_title = TRUE;
+ }
+
+
+ /**********
+ * Artist *
+ **********/
+ while ( (id3_frame = ID3Tag_FindFrameWithID(id3_tag,ID3FID_LEADARTIST)) )
+ ID3Tag_RemoveFrame(id3_tag,id3_frame);
+ if (FileTag->artist && g_utf8_strlen(FileTag->artist, -1) > 0)
+ {
+ id3_frame = ID3Frame_NewID(ID3FID_LEADARTIST);
+ ID3Tag_AttachFrame(id3_tag,id3_frame);
+ Id3tag_Set_Field(id3_frame, ID3FN_TEXT, FileTag->artist);
+ has_artist = TRUE;
+ }
+
+
+ /*********
+ * Album *
+ *********/
+ while ( (id3_frame = ID3Tag_FindFrameWithID(id3_tag,ID3FID_ALBUM)) )
+ ID3Tag_RemoveFrame(id3_tag,id3_frame);
+ if (FileTag->album && g_utf8_strlen(FileTag->album, -1) > 0)
+ {
+ id3_frame = ID3Frame_NewID(ID3FID_ALBUM);
+ ID3Tag_AttachFrame(id3_tag,id3_frame);
+ Id3tag_Set_Field(id3_frame, ID3FN_TEXT, FileTag->album);
+ has_album = TRUE;
+ }
+
+
+ /***************
+ * Part of set *
+ ***************/
+ while ( (id3_frame = ID3Tag_FindFrameWithID(id3_tag,ID3FID_PARTINSET)) )
+ ID3Tag_RemoveFrame(id3_tag,id3_frame);
+ if (FileTag->disc_number && g_utf8_strlen(FileTag->disc_number, -1) > 0)
+ {
+ id3_frame = ID3Frame_NewID(ID3FID_PARTINSET);
+ ID3Tag_AttachFrame(id3_tag,id3_frame);
+ Id3tag_Set_Field(id3_frame, ID3FN_TEXT, FileTag->disc_number);
+ has_disc_number = TRUE;
+ }
+
+
+ /********
+ * Year *
+ ********/
+ while ( (id3_frame = ID3Tag_FindFrameWithID(id3_tag,ID3FID_YEAR)) )
+ ID3Tag_RemoveFrame(id3_tag,id3_frame);
+ if (FileTag->year && g_utf8_strlen(FileTag->year, -1) > 0)
+ {
+ id3_frame = ID3Frame_NewID(ID3FID_YEAR);
+ ID3Tag_AttachFrame(id3_tag,id3_frame);
+ Id3tag_Set_Field(id3_frame, ID3FN_TEXT, FileTag->year);
+ has_year = TRUE;
+ }
+
+
+ /*************************
+ * Track and Total Track *
+ *************************/
+ while ( (id3_frame = ID3Tag_FindFrameWithID(id3_tag,ID3FID_TRACKNUM)) )
+ ID3Tag_RemoveFrame(id3_tag,id3_frame);
+ if (FileTag->track && g_utf8_strlen(FileTag->track, -1) > 0)
+ {
+ id3_frame = ID3Frame_NewID(ID3FID_TRACKNUM);
+ ID3Tag_AttachFrame(id3_tag,id3_frame);
+
+ if ( FileTag->track_total && g_utf8_strlen(FileTag->track_total, -1) > 0)
+ string1 = g_strconcat(FileTag->track,"/",FileTag->track_total,NULL);
+ else
+ string1 = g_strdup(FileTag->track);
+
+ Id3tag_Set_Field(id3_frame, ID3FN_TEXT, string1);
+ g_free(string1);
+ has_track = TRUE;
+ }
+
+
+ /*********
+ * Genre *
+ *********
+ * Genre is written like this :
+ * - "(<genre_id>)" -> "(3)"
+ * - "(<genre_id>)<refinement>" -> "(3)EuroDance"
+ */
+ while ( (id3_frame = ID3Tag_FindFrameWithID(id3_tag,ID3FID_CONTENTTYPE)) )
+ ID3Tag_RemoveFrame(id3_tag,id3_frame);
+ if (FileTag->genre && strlen(FileTag->genre)>0 )
+ {
+ gchar *genre_string_tmp;
+ guchar genre_value;
+
+ id3_frame = ID3Frame_NewID(ID3FID_CONTENTTYPE);
+ ID3Tag_AttachFrame(id3_tag,id3_frame);
+
+ genre_value = Id3tag_String_To_Genre(FileTag->genre);
+ // If genre not defined don't write genre value between brackets! (priority problem noted with some tools)
+ if (genre_value == ID3_INVALID_GENRE)
+ genre_string_tmp = g_strdup_printf("%s",FileTag->genre);
+ else
+ genre_string_tmp = g_strdup_printf("(%d)",genre_value);
+
+ Id3tag_Set_Field(id3_frame, ID3FN_TEXT, genre_string_tmp);
+ g_free(genre_string_tmp);
+ has_genre = TRUE;
+ }
+
+
+ /***********
+ * Comment *
+ ***********/
+ while ( (id3_frame = ID3Tag_FindFrameWithID(id3_tag,ID3FID_COMMENT)) )
+ ID3Tag_RemoveFrame(id3_tag,id3_frame);
+ if (FileTag->comment && g_utf8_strlen(FileTag->comment, -1) > 0)
+ {
+ id3_frame = ID3Frame_NewID(ID3FID_COMMENT);
+ ID3Tag_AttachFrame(id3_tag,id3_frame);
+ Id3tag_Set_Field(id3_frame, ID3FN_TEXT, FileTag->comment);
+ // These 2 following fields allow synchronisation between id3v2 and id3v1 tags with id3lib
+ // Disabled as when using unicode, the comment field stay in ISO.
+ //Id3tag_Set_Field(id3_frame, ID3FN_DESCRIPTION, "ID3v1 Comment");
+ //Id3tag_Set_Field(id3_frame, ID3FN_LANGUAGE, "XXX");
+ has_comment = TRUE;
+ }
+
+
+ /************
+ * Composer *
+ ************/
+ while ( (id3_frame = ID3Tag_FindFrameWithID(id3_tag,ID3FID_COMPOSER)) )
+ ID3Tag_RemoveFrame(id3_tag,id3_frame);
+ if (FileTag->composer && g_utf8_strlen(FileTag->composer, -1) > 0)
+ {
+ id3_frame = ID3Frame_NewID(ID3FID_COMPOSER);
+ ID3Tag_AttachFrame(id3_tag,id3_frame);
+ Id3tag_Set_Field(id3_frame, ID3FN_TEXT, FileTag->composer);
+ has_composer = TRUE;
+ }
+
+
+ /*******************
+ * Original artist *
+ *******************/
+ while ( (id3_frame = ID3Tag_FindFrameWithID(id3_tag,ID3FID_ORIGARTIST)) )
+ ID3Tag_RemoveFrame(id3_tag,id3_frame);
+ if (FileTag->orig_artist && g_utf8_strlen(FileTag->orig_artist, -1) > 0)
+ {
+ id3_frame = ID3Frame_NewID(ID3FID_ORIGARTIST);
+ ID3Tag_AttachFrame(id3_tag,id3_frame);
+ Id3tag_Set_Field(id3_frame, ID3FN_TEXT, FileTag->orig_artist);
+ has_orig_artist = TRUE;
+ }
+
+
+ /*************
+ * Copyright *
+ *************/
+ while ( (id3_frame = ID3Tag_FindFrameWithID(id3_tag,ID3FID_COPYRIGHT)) )
+ ID3Tag_RemoveFrame(id3_tag,id3_frame);
+ if (FileTag->copyright && g_utf8_strlen(FileTag->copyright, -1) > 0)
+ {
+ id3_frame = ID3Frame_NewID(ID3FID_COPYRIGHT);
+ ID3Tag_AttachFrame(id3_tag,id3_frame);
+ Id3tag_Set_Field(id3_frame, ID3FN_TEXT, FileTag->copyright);
+ has_copyright = TRUE;
+ }
+
+
+ /*******
+ * URL *
+ *******/
+ while ( (id3_frame = ID3Tag_FindFrameWithID(id3_tag,ID3FID_WWWUSER)) )
+ ID3Tag_RemoveFrame(id3_tag,id3_frame);
+ if (FileTag->url && g_utf8_strlen(FileTag->url, -1) > 0)
+ {
+ id3_frame = ID3Frame_NewID(ID3FID_WWWUSER);
+ ID3Tag_AttachFrame(id3_tag,id3_frame);
+ Id3tag_Set_Field(id3_frame, ID3FN_URL, FileTag->url);
+ has_composer = TRUE;
+ }
+
+
+ /**************
+ * Encoded by *
+ **************/
+ while ( (id3_frame = ID3Tag_FindFrameWithID(id3_tag,ID3FID_ENCODEDBY)) )
+ ID3Tag_RemoveFrame(id3_tag,id3_frame);
+ if (FileTag->encoded_by && g_utf8_strlen(FileTag->encoded_by, -1) > 0)
+ {
+ id3_frame = ID3Frame_NewID(ID3FID_ENCODEDBY);
+ ID3Tag_AttachFrame(id3_tag,id3_frame);
+ Id3tag_Set_Field(id3_frame, ID3FN_TEXT, FileTag->encoded_by);
+ has_encoded_by = TRUE;
+ }
+
+
+ /***********
+ * Picture *
+ ***********/
+ while ( (id3_frame = ID3Tag_FindFrameWithID(id3_tag,ID3FID_PICTURE)) )
+ ID3Tag_RemoveFrame(id3_tag,id3_frame);
+ pic = FileTag->picture;
+ if (!pic)
+ has_picture = 0;
+ while (pic)
+ {
+ id3_frame = ID3Frame_NewID(ID3FID_PICTURE);
+ ID3Tag_AttachFrame(id3_tag,id3_frame);
+
+ switch (Picture_Format(pic))
+ {
+ case PICTURE_FORMAT_JPEG:
+ if ((id3_field = ID3Frame_GetField(id3_frame,ID3FN_MIMETYPE)))
+ ID3Field_SetASCII(id3_field, "image/jpeg");
+ if ((id3_field = ID3Frame_GetField(id3_frame,ID3FN_IMAGEFORMAT)))
+ ID3Field_SetASCII(id3_field, "JPG");
+ break;
+
+ case PICTURE_FORMAT_PNG:
+ if ((id3_field = ID3Frame_GetField(id3_frame,ID3FN_MIMETYPE)))
+ ID3Field_SetASCII(id3_field, "image/png");
+ if ((id3_field = ID3Frame_GetField(id3_frame,ID3FN_IMAGEFORMAT)))
+ ID3Field_SetASCII(id3_field, "PNG");
+ break;
+ }
+
+ if ((id3_field = ID3Frame_GetField(id3_frame, ID3FN_PICTURETYPE)))
+ ID3Field_SetINT(id3_field, pic->type);
+
+ if (pic->description)
+ Id3tag_Set_Field(id3_frame, ID3FN_DESCRIPTION, pic->description);
+
+ if ((id3_field = ID3Frame_GetField(id3_frame,ID3FN_DATA)))
+ ID3Field_SetBINARY(id3_field, pic->data, pic->size);
+
+ pic = pic->next;
+ has_picture = TRUE;
+ }
+
+
+ /*********************************
+ * File length (in milliseconds) *
+ *********************************/
+ /* Don't write this field, not useful? *
+ while ( (id3_frame = ID3Tag_FindFrameWithID(id3_tag,ID3FID_SONGLEN)) )
+ ID3Tag_RemoveFrame(id3_tag,id3_frame);
+ if (ETFile->ETFileInfo && ((ET_File_Info *)ETFile->ETFileInfo)->duration > 0 )
+ {
+ gchar *string;
+
+ id3_frame = ID3Frame_NewID(ID3FID_SONGLEN);
+ ID3Tag_AttachFrame(id3_tag,id3_frame);
+
+ string = g_strdup_printf("%d",((ET_File_Info *)ETFile->ETFileInfo)->duration * 1000);
+ Id3tag_Set_Field(id3_frame, ID3FN_TEXT, string);
+ g_free(string);
+ has_song_len = TRUE;
+ }*/
+
+
+ /******************************
+ * Delete an APE tag if found *
+ ******************************/
+ {
+ // Delete the APE tag (create a dummy ETFile for the Ape_Tag_... function)
+ ET_File *ETFile_tmp = ET_File_Item_New();
+ File_Name *FileName_tmp = ET_File_Name_Item_New();
+ File_Tag *FileTag_tmp = ET_File_Tag_Item_New();
+ // Same file...
+ FileName_tmp->value = g_strdup(filename);
+ FileName_tmp->value_utf8 = g_strdup(filename_utf8); // Not necessary to fill 'value_ck'
+ ETFile_tmp->FileNameList = g_list_append(NULL,FileName_tmp);
+ ETFile_tmp->FileNameCur = ETFile_tmp->FileNameList;
+ // With empty tag...
+ ETFile_tmp->FileTagList = g_list_append(NULL,FileTag_tmp);
+ ETFile_tmp->FileTag = ETFile_tmp->FileTagList;
+ Ape_Tag_Write_File_Tag(ETFile_tmp);
+ ET_Free_File_List_Item(ETFile_tmp);
+ }
+
+
+ /*********************************
+ * Update id3v1.x and id3v2 tags *
+ *********************************/
+ /* Get the number of frames into the tag, cause if it is
+ * equal to 0, id3lib-3.7.12 doesn't update the tag */
+ number_of_frames = ID3Tag_NumFrames(id3_tag);
+
+ /* If all fields (managed in the UI) are empty and option STRIP_TAG_WHEN_EMPTY_FIELDS
+ * is set to 1, we strip the ID3v1.x and ID3v2 tags. Else, write ID3v2 and/or ID3v1
+ */
+ if ( STRIP_TAG_WHEN_EMPTY_FIELDS
+ && !has_title && !has_artist && !has_album && !has_year && !has_track
+ && !has_genre && !has_composer && !has_orig_artist && !has_copyright && !has_url
+ && !has_encoded_by && !has_picture && !has_comment && !has_disc_number)//&& !has_song_len )
+ {
+ error_strip_id3v1 = ID3Tag_Strip(id3_tag,ID3TT_ID3V1);
+ error_strip_id3v2 = ID3Tag_Strip(id3_tag,ID3TT_ID3V2);
+ /* Check error messages */
+ if (error_strip_id3v1 == ID3E_NoError && error_strip_id3v2 == ID3E_NoError)
+ {
+ Log_Print(_("Removed tag of '%s'"),basename_utf8);
+ }else
+ {
+ if (error_strip_id3v1 != ID3E_NoError)
+ Log_Print(_("Error while removing ID3v1 tag of '%s' (%s)"),basename_utf8,Id3tag_Get_Error_Message(error_strip_id3v1));
+ if (error_strip_id3v2 != ID3E_NoError)
+ Log_Print(_("Error while removing ID3v2 tag of '%s' (%s)"),basename_utf8,Id3tag_Get_Error_Message(error_strip_id3v2));
+ error++;
+ }
+
+ }else
+ {
+ /* It's better to remove the id3v1 tag before, to synchronize it with the
+ * id3v2 tag (else id3lib doesn't do it correctly)
+ */
+ error_strip_id3v1 = ID3Tag_Strip(id3_tag,ID3TT_ID3V1);
+
+ /*
+ * ID3v2 tag
+ */
+ if (FILE_WRITING_ID3V2_WRITE_TAG && number_of_frames!=0)
+ {
+ error_update_id3v2 = ID3Tag_UpdateByTagType(id3_tag,ID3TT_ID3V2);
+ if (error_update_id3v2 != ID3E_NoError)
+ {
+ Log_Print(_("Error while updating ID3v2 tag of '%s' (%s)"),basename_utf8,Id3tag_Get_Error_Message(error_update_id3v2));
+ error++;
+ }else
+ {
+ /* See known problem on the top : [ 1016290 ] Unicode16 writing bug.
+ * When we write the tag in Unicode, we try to check if it was correctly
+ * written. So to test it : we read again the tag, and then compare
+ * with the previous one. We check up to find an error (as only some
+ * characters are affected).
+ * If the patch to id3lib was applied to fix the problem (tested
+ * by Id3tag_Check_If_Id3lib_Is_Bugged) we didn't make the following
+ * test => OK */
+ if (flag_id3lib_bugged
+ && ( FILE_WRITING_ID3V2_USE_UNICODE_CHARACTER_SET ))
+ {
+ File_Tag *FileTag_tmp = ET_File_Tag_Item_New();
+ if (Id3tag_Read_File_Tag(filename,FileTag_tmp) == TRUE
+ && ET_Detect_Changes_Of_File_Tag(FileTag,FileTag_tmp) == TRUE)
+ {
+ GtkWidget *msgbox = NULL;
+ gchar *msg;
+
+ msg = g_strdup_printf(_("You have tried to save this tag to Unicode "
+ "but it was detected that your version of id3lib is bugged.\n"
+ "If you reload this file, some characters in the tag may be not "
+ "displayed correctly...\nPlease, apply to id3lib the patch "
+ "src/id3lib/patch_id3lib_3.8.3_UTF16_writing_bug.diff\n"
+ "available in EasyTAG package sources.\n"
+ "Note that this message will appear only one time.\n\n"
+ "File : %s"),filename_utf8);
+ //Log_Print(msg);
+
+ msgbox = msg_box_new(_("Error..."),msg,GTK_STOCK_DIALOG_ERROR,BUTTON_OK,0);
+ g_free(msg);
+ msg_box_hide_check_button(MSG_BOX(msgbox));
+ msg_box_run(MSG_BOX(msgbox));
+ gtk_widget_destroy(msgbox);
+ flag_id3lib_bugged = FALSE; // To display the message only one time
+ }
+ ET_Free_File_Tag_Item(FileTag_tmp);
+ }
+
+ }
+ }else
+ {
+ error_strip_id3v2 = ID3Tag_Strip(id3_tag,ID3TT_ID3V2);
+ if (error_strip_id3v2 != ID3E_NoError)
+ {
+ Log_Print(_("Error while removing ID3v2 tag of '%s' (%s)"),basename_utf8,Id3tag_Get_Error_Message(error_strip_id3v2));
+ error++;
+ }
+ }
+
+ /*
+ * ID3v1 tag
+ * Must be set after ID3v2 or ID3Tag_UpdateByTagType cause damage to unicode strings
+ */
+ // id3lib writes incorrectly the ID3v2 tag if unicode used when writing ID3v1 tag
+ if (FILE_WRITING_ID3V1_WRITE_TAG && number_of_frames!=0)
+ {
+ // By default id3lib converts id3tag to ISO-8859-1 (single byte character set)
+ // Note : converting UTF-16 string (two bytes character set) to ISO-8859-1
+ // remove only the second byte => a strange string appears...
+ Id3tag_Prepare_ID3v1(id3_tag);
+
+ error_update_id3v1 = ID3Tag_UpdateByTagType(id3_tag,ID3TT_ID3V1);
+ if (error_update_id3v1 != ID3E_NoError)
+ {
+ Log_Print(_("Error while updating ID3v1 tag of '%s' (%s)"),basename_utf8,Id3tag_Get_Error_Message(error_update_id3v1));
+ error++;
+ }
+ }else
+ {
+ error_strip_id3v1 = ID3Tag_Strip(id3_tag,ID3TT_ID3V1);
+ if (error_strip_id3v1 != ID3E_NoError)
+ {
+ Log_Print(_("Error while removing ID3v1 tag of '%s' (%s)"),basename_utf8,Id3tag_Get_Error_Message(error_strip_id3v1));
+ error++;
+ }
+ }
+
+ if (error == 0)
+ Log_Print(_("Updated tag of '%s'"),basename_utf8);
+
+ }
+
+ /* Free allocated data */
+ ID3Tag_Delete(id3_tag);
+ g_free(basename_utf8);
+
+ if (error) return FALSE;
+ else return TRUE;
+
+}
+
+
+gchar *Id3tag_Get_Error_Message(ID3_Err error)
+{
+ switch (error)
+ {
+ case ID3E_NoError:
+ return _("No error reported");
+ case ID3E_NoMemory:
+ return _("No available memory");
+ case ID3E_NoData:
+ return _("No data to parse");
+ case ID3E_BadData:
+ return _("Improperly formatted data");
+ case ID3E_NoBuffer:
+ return _("No buffer to write to");
+ case ID3E_SmallBuffer:
+ return _("Buffer is too small");
+ case ID3E_InvalidFrameID:
+ return _("Invalid frame ID");
+ case ID3E_FieldNotFound:
+ return _("Requested field not found");
+ case ID3E_UnknownFieldType:
+ return _("Unknown field type");
+ case ID3E_TagAlreadyAttached:
+ return _("Tag is already attached to a file");
+ case ID3E_InvalidTagVersion:
+ return _("Invalid tag version");
+ case ID3E_NoFile:
+ return _("No file to parse");
+ case ID3E_ReadOnly:
+ return _("Attempting to write to a read-only file");
+ case ID3E_zlibError:
+ return _("Error in compression/uncompression");
+ default:
+ return _("Unknown error message!");
+ }
+
+}
+
+
+
+/*
+ * As the ID3Tag_Link function of id3lib-3.8.0pre2 returns the ID3v1 tags
+ * when a file has both ID3v1 and ID3v2 tags, we first try to explicitely
+ * get the ID3v2 tags with ID3Tag_LinkWithFlags and, if we cannot get them,
+ * fall back to the ID3v1 tags.
+ * (Written by Holger Schemel).
+ */
+ID3_C_EXPORT size_t ID3Tag_Link_1 (ID3Tag *id3tag, const char *filename)
+{
+ size_t offset;
+
+# if (0) // Link the file with the both tags may cause damage to unicode strings
+//# if ( (ID3LIB_MAJOR >= 3) && (ID3LIB_MINOR >= 8) && (ID3LIB_PATCH >= 1) ) // Same test used in Id3tag_Read_File_Tag to use ID3Tag_HasTagType
+ /* No problem of priority, so we link the file with the both tags
+ * to manage => ID3Tag_HasTagType works correctly */
+ offset = ID3Tag_LinkWithFlags(id3tag,filename,ID3TT_ID3V1 | ID3TT_ID3V2);
+# elif ( (ID3LIB_MAJOR >= 3) && (ID3LIB_MINOR >= 8) )
+ /* Version 3.8.0pre2 gives priority to tag id3v1 instead of id3v2, so we
+ * try to fix it by linking the file with the id3v2 tag first. This bug
+ * was fixed in the final version of 3.8.0 but we can't know it... */
+ /* First, try to get the ID3v2 tags */
+ offset = ID3Tag_LinkWithFlags(id3tag,filename,ID3TT_ID3V2);
+ if (offset == 0)
+ {
+ /* No ID3v2 tags available => try to get the ID3v1 tags */
+ offset = ID3Tag_LinkWithFlags(id3tag,filename,ID3TT_ID3V1);
+ }
+# else
+ /* Function 'ID3Tag_LinkWithFlags' is not defined up to id3lib-.3.7.13 */
+ offset = ID3Tag_Link(id3tag,filename);
+# endif
+ //g_print("ID3 TAG SIZE: %d\t%s\n",offset,g_path_get_basename(filename));
+ return offset;
+}
+
+
+/*
+ * As the ID3Field_GetASCII function differs with the version of id3lib, we must redefine it.
+ */
+ID3_C_EXPORT size_t ID3Field_GetASCII_1(const ID3Field *field, char *buffer, size_t maxChars, size_t itemNum)
+{
+
+ /* Defined by id3lib: ID3LIB_MAJOR_VERSION, ID3LIB_MINOR_VERSION, ID3LIB_PATCH_VERSION
+ * Defined by autoconf: ID3LIB_MAJOR, ID3LIB_MINOR, ID3LIB_PATCH
+ *
+ * <= 3.7.12 : first item num is 1 for ID3Field_GetASCII
+ * = 3.7.13 : first item num is 0 for ID3Field_GetASCII
+ * >= 3.8.0 : doesn't need item num for ID3Field_GetASCII
+ */
+ //g_print("id3lib version: %d.%d.%d\n",ID3LIB_MAJOR,ID3LIB_MINOR,ID3LIB_PATCH);
+# if (ID3LIB_MAJOR >= 3)
+ // (>= 3.x.x)
+# if (ID3LIB_MINOR <= 7)
+ // (3.0.0 to 3.7.x)
+# if (ID3LIB_PATCH >= 13)
+ // (>= 3.7.13)
+ return ID3Field_GetASCII(field,buffer,maxChars,itemNum);
+# else
+ return ID3Field_GetASCII(field,buffer,maxChars,itemNum+1);
+# endif
+# else
+ // (>= to 3.8.0)
+ //return ID3Field_GetASCII(field,buffer,maxChars);
+ return ID3Field_GetASCIIItem(field,buffer,maxChars,itemNum);
+# endif
+# else
+ // Not tested (< 3.x.x)
+ return ID3Field_GetASCII(field,buffer,maxChars,itemNum+1);
+# endif
+}
+
+
+
+/*
+ * As the ID3Field_GetUNICODE function differs with the version of id3lib, we must redefine it.
+ */
+ID3_C_EXPORT size_t ID3Field_GetUNICODE_1 (const ID3Field *field, unicode_t *buffer, size_t maxChars, size_t itemNum)
+{
+
+ /* Defined by id3lib: ID3LIB_MAJOR_VERSION, ID3LIB_MINOR_VERSION, ID3LIB_PATCH_VERSION
+ * Defined by autoconf: ID3LIB_MAJOR, ID3LIB_MINOR, ID3LIB_PATCH
+ *
+ * <= 3.7.12 : first item num is 1 for ID3Field_GetUNICODE
+ * = 3.7.13 : first item num is 0 for ID3Field_GetUNICODE
+ * >= 3.8.0 : doesn't need item num for ID3Field_GetUNICODE
+ */
+ //g_print("id3lib version: %d.%d.%d\n",ID3LIB_MAJOR,ID3LIB_MINOR,ID3LIB_PATCH);
+# if (ID3LIB_MAJOR >= 3)
+ // (>= 3.x.x)
+# if (ID3LIB_MINOR <= 7)
+ // (3.0.0 to 3.7.x)
+# if (ID3LIB_PATCH >= 13)
+ // (>= 3.7.13)
+ return ID3Field_GetUNICODE(field,buffer,maxChars,itemNum);
+# else
+ return ID3Field_GetUNICODE(field,buffer,maxChars,itemNum+1);
+# endif
+# else
+ // (>= to 3.8.0)
+ return ID3Field_GetUNICODE(field,buffer,maxChars);
+ // ID3Field_GetUNICODEItem always return 0 with id3lib3.8.3, it is bug in size_t D3_FieldImpl::Get()
+ //return ID3Field_GetUNICODEItem(field,buffer,maxChars,itemNum);
+# endif
+# else
+ // Not tested (< 3.x.x)
+ return ID3Field_GetUNICODE(field,buffer,maxChars,itemNum+1);
+# endif
+}
+
+
+
+
+/*
+ * Source : "http://www.id3.org/id3v2.4.0-structure.txt"
+ *
+ * Frames that allow different types of text encoding contains a text
+ * encoding description byte. Possible encodings:
+ *
+ * $00 ISO-8859-1 [ISO-8859-1]. Terminated with $00.
+ * $01 UTF-16 [UTF-16] encoded Unicode [UNICODE] with BOM ($FF FE
+ * or $FE FF). All strings in the same frame SHALL have the same
+ * byteorder. Terminated with $00 00.
+ * $02 UTF-16BE [UTF-16] encoded Unicode [UNICODE] without BOM.
+ * Terminated with $00 00.
+ * $03 UTF-8 [UTF-8] encoded Unicode [UNICODE]. Terminated with $00.
+ *
+ * For example :
+ * T P E 1 . . . . . . . ? ? J . o . n . . G . i . n . d . i . c . k .
+ * Hex : 54 50 45 31 00 00 00 19 00 00 01 ff fe 4a 00 6f 00 6e 00 20 00 47 00 69 00 6e 00 64 00 6e 00 63 00 6b 00
+ * ^
+ * |___ UTF-16
+ */
+/*
+ * Read the content (ID3FN_TEXT, ID3FN_URL, ...) of the id3_field of the
+ * id3_frame, and convert the string if needed to UTF-8.
+ */
+gchar *Id3tag_Get_Field (const ID3Frame *id3_frame, ID3_FieldID id3_fieldid)
+{
+ ID3Field *id3_field = NULL;
+ ID3Field *id3_field_encoding = NULL;
+ size_t num_chars = 0;
+ gchar *string = NULL, *string1 = NULL;
+
+ //g_print("Id3tag_Get_Field - ID3Frame '%s'\n",ID3FrameInfo_ShortName(ID3Frame_GetID(id3_frame)));
+
+ if ( (id3_field = ID3Frame_GetField(id3_frame,id3_fieldid)) )
+ {
+ ID3_TextEnc enc = ID3TE_NONE;
+
+ // Data of the field must be a TEXT (ID3FTY_TEXTSTRING)
+ if (ID3Field_GetType(id3_field) != ID3FTY_TEXTSTRING)
+ {
+ Log_Print("Id3tag_Get_Field() must be used only for fields containing text.\n");
+ return NULL;
+ }
+
+ /*
+ * We prioritize the encoding of the field. If the encoding of the field
+ * is ISO-8859-1, it can be read with an other single byte encoding.
+ */
+ // Get encoding from content of file...
+ id3_field_encoding = ID3Frame_GetField(id3_frame,ID3FN_TEXTENC);
+ if (id3_field_encoding)
+ enc = ID3Field_GetINT(id3_field_encoding);
+ // Else, get encoding from the field
+ //enc = ID3Field_GetEncoding(id3_field);
+
+ if (enc != ID3TE_UTF16 && enc != ID3TE_UTF8) // Encoding is ISO-8859-1?
+ {
+ if (USE_NON_STANDARD_ID3_READING_CHARACTER_SET) // Override with an other character set?
+ {
+ // Encoding set by user to ???.
+ if ( strcmp(FILE_READING_ID3V1V2_CHARACTER_SET,"ISO-8859-1") == 0 )
+ {
+ enc = ID3TE_ISO8859_1;
+ }else if ( strcmp(FILE_READING_ID3V1V2_CHARACTER_SET,"UTF-16BE") == 0
+ || strcmp(FILE_READING_ID3V1V2_CHARACTER_SET,"UTF-16LE") == 0 )
+ {
+ enc = ID3TE_UTF16;
+ }else if ( strcmp(FILE_READING_ID3V1V2_CHARACTER_SET,"UTF-8") == 0 )
+ {
+ enc = ID3TE_UTF8;
+ }else
+ {
+ enc = 9999;
+ }
+ }
+ }
+
+ // Some fields, as URL, aren't encodable, so there were written using ISO characters.
+ if ( !ID3Field_IsEncodable(id3_field) )
+ {
+ enc = ID3TE_ISO8859_1;
+ }
+
+ // Action according the encoding...
+ switch ( enc )
+ {
+ case ID3TE_ISO8859_1:
+ string = g_malloc0(sizeof(char)*ID3V2_MAX_STRING_LEN+1);
+ num_chars = ID3Field_GetASCII_1(id3_field,string,ID3V2_MAX_STRING_LEN,0);
+ string1 = convert_string(string,"ISO-8859-1","UTF-8",FALSE);
+ break;
+
+ case ID3TE_UTF8: // Shouldn't work with id3lib 3.8.3 (supports only ID3v2.3, not ID3v2.4)
+ // For UTF-8, this part do the same thing that enc=9999
+ string = g_malloc0(sizeof(char)*ID3V2_MAX_STRING_LEN+1);
+ num_chars = ID3Field_GetASCII_1(id3_field,string,ID3V2_MAX_STRING_LEN,0);
+ //string1 = convert_string(string,"UTF-8","UTF-8",FALSE); // Nothing to do
+ if (g_utf8_validate(string,-1,NULL))
+ string1 = g_strdup(string);
+ break;
+
+ case ID3TE_UTF16:
+ // Id3lib (3.8.3 at least) always returns Unicode strings in UTF-16BE.
+ case ID3TE_UTF16BE:
+ string = g_malloc0(sizeof(unicode_t)*ID3V2_MAX_STRING_LEN+1);
+ num_chars = ID3Field_GetUNICODE_1(id3_field,(unicode_t *)string,ID3V2_MAX_STRING_LEN,0);
+ // "convert_string_1" as we need to pass length for UTF-16
+ string1 = convert_string_1(string,num_chars,"UTF-16BE","UTF-8",FALSE);
+ break;
+
+ case 9999:
+ string = g_malloc0(sizeof(char)*ID3V2_MAX_STRING_LEN+1);
+ num_chars = ID3Field_GetASCII_1(id3_field,string,ID3V2_MAX_STRING_LEN,0);
+ string1 = convert_string(string,FILE_READING_ID3V1V2_CHARACTER_SET,"UTF-8",FALSE);
+ break;
+
+ default:
+ string = g_malloc0(sizeof(char)*4*ID3V2_MAX_STRING_LEN+1);
+ num_chars = ID3Field_GetASCII_1(id3_field,string,ID3V2_MAX_STRING_LEN,0);
+ string1 = convert_to_utf8(string);
+ break;
+ }
+ }
+ //g_print(">>ID:%d >'%s' (string1:'%s') (num_chars:%d)\n",ID3Field_GetINT(id3_field_encoding),string,string1,num_chars);
+
+ // In case the conversion fails, try 'filename_to_display' character fix...
+ if (num_chars && !string1)
+ {
+ gchar *escaped_str = g_strescape(string, NULL);
+ Log_Print("Id3tag_Get_Field: Trying to fix string '%s' ...",escaped_str);
+ g_free(escaped_str);
+
+ string1 = filename_to_display(string);
+
+ if (string1)
+ Log_Print("OK");
+ else
+ Log_Print("KO");
+ }
+ g_free(string);
+
+ return string1;
+}
+
+
+/*
+ * Set the content (ID3FN_TEXT, ID3FN_URL, ...) of the id3_field of the
+ * id3_frame. Check also if the string must be written from UTF-8 (gtk2) in the
+ * ISO-8859-1 encoding or UTF-16.
+ *
+ * Return the encoding used as if UTF-16 was used, we musn't write the ID3v1 tag.
+ *
+ * Known problem with id3lib
+ * - [ 1016290 ] Unicode16 writing bug
+ * http://sourceforge.net/tracker/index.php?func=detail&aid=1016290&group_id=979&atid=300979
+ * For example with Latin-1 characters (like éöäüß) not saved correctly
+ * in Unicode, due to id3lib (for "é" it will write "E9 FF" instead of "EF 00")
+ */
+/*
+ * OLD NOTE : PROBLEM with ID3LIB
+ * - [ 1074169 ] Writing ID3v1 tag breaks Unicode string in v2 tags
+ * http://sourceforge.net/tracker/index.php?func=detail&aid=1074169&group_id=979&atid=100979
+ * => don't write id3v1 tag if Unicode is used, up to patch applied
+ * - [ 1073951 ] Added missing Field Encoding functions to C wrapper
+ * http://sourceforge.net/tracker/index.php?func=detail&aid=1073951&group_id=979&atid=300979
+ */
+ID3_TextEnc Id3tag_Set_Field (const ID3Frame *id3_frame, ID3_FieldID id3_fieldid, gchar *string)
+{
+ ID3Field *id3_field = NULL;
+ ID3Field *id3_field_encoding = NULL;
+ gchar *string_converted = NULL;
+
+ if ( (id3_field = ID3Frame_GetField(id3_frame,id3_fieldid)) )
+ {
+ ID3_TextEnc enc = ID3TE_NONE;
+
+ // Data of the field must be a TEXT (ID3FTY_TEXTSTRING)
+ if (ID3Field_GetType(id3_field) != ID3FTY_TEXTSTRING)
+ {
+ Log_Print("Id3tag_Set_Field() must be used only for fields containing text.");
+ return ID3TE_NONE;
+ }
+
+ /*
+ * We prioritize the rule selected in options. If the encoding of the
+ * field is ISO-8859-1, we can write it to an other single byte encoding.
+ */
+ if (FILE_WRITING_ID3V2_USE_UNICODE_CHARACTER_SET)
+ {
+ // Check if we can write the tag using ISO-8859-1 instead of UTF-16...
+ if ( (string_converted = g_convert(string, strlen(string), "ISO-8859-1",
+ "UTF-8", NULL, NULL ,NULL)) )
+ {
+ enc = ID3TE_ISO8859_1;
+ g_free(string_converted);
+ }else
+ {
+ // Force to UTF-16 as UTF-8 isn't supported
+ enc = ID3TE_UTF16;
+ }
+ } else
+ {
+ // Other encoding selected
+ // Encoding set by user to ???.
+ if ( strcmp(FILE_WRITING_ID3V2_NO_UNICODE_CHARACTER_SET,"ISO-8859-1") == 0 )
+ {
+ enc = ID3TE_ISO8859_1;
+ }else if ( strcmp(FILE_WRITING_ID3V2_NO_UNICODE_CHARACTER_SET,"UTF-16BE") == 0
+ || strcmp(FILE_WRITING_ID3V2_NO_UNICODE_CHARACTER_SET,"UTF-16LE") == 0 )
+ {
+ enc = ID3TE_UTF16;
+ }else if ( strcmp(FILE_WRITING_ID3V2_NO_UNICODE_CHARACTER_SET,"UTF-8") == 0 )
+ {
+ enc = ID3TE_UTF8;
+ }else
+ {
+ enc = 9999;
+ }
+ }
+
+ // Some fields, as URL, aren't encodable, so there were written using ISO characters!
+ if ( !ID3Field_IsEncodable(id3_field) )
+ {
+ enc = ID3TE_ISO8859_1;
+ }
+
+ // Action according the encoding...
+ switch ( enc )
+ {
+ case ID3TE_ISO8859_1:
+ // Write into ISO-8859-1
+ //string_converted = convert_string(string,"UTF-8","ISO-8859-1",TRUE);
+ string_converted = Id3tag_Rules_For_ISO_Fields(string,"UTF-8","ISO-8859-1");
+ ID3Field_SetEncoding(id3_field,ID3TE_ISO8859_1); // Not necessary for ISO-8859-1, but better to precise if field has an other encoding...
+ ID3Field_SetASCII(id3_field,string_converted);
+ g_free(string_converted);
+
+ id3_field_encoding = ID3Frame_GetField(id3_frame,ID3FN_TEXTENC);
+ if (id3_field_encoding)
+ ID3Field_SetINT(id3_field_encoding,ID3TE_ISO8859_1);
+
+ return ID3TE_ISO8859_1;
+ break;
+
+ /*** Commented as it doesn't work with id3lib 3.8.3 :
+ *** - it writes a strange UTF-8 string (2 bytes per character. Second char is FF) with a BOM
+ *** - it set the frame encoded to UTF-8 : "$03" which is OK
+ case ID3TE_UTF8: // Shouldn't work with id3lib 3.8.3 (supports only ID3v2.3, not ID3v2.4)
+ ID3Field_SetEncoding(id3_field,ID3TE_UTF8);
+ ID3Field_SetASCII(id3_field,string);
+
+ id3_field_encoding = ID3Frame_GetField(id3_frame,ID3FN_TEXTENC);
+ if (id3_field_encoding)
+ ID3Field_SetINT(id3_field_encoding,ID3TE_UTF8);
+
+ return ID3TE_UTF8;
+ break;
+ ***/
+
+ case ID3TE_UTF16:
+ //case ID3TE_UTF16BE:
+
+ /* See known problem on the top : [ 1016290 ] Unicode16 writing bug */
+ // Write into UTF-16
+ string_converted = convert_string_1(string, strlen(string), "UTF-8",
+ "UTF-16BE", FALSE);
+
+ // id3lib (3.8.3 at least) always takes big-endian input for Unicode
+ // fields, even if the field is set little-endian.
+ ID3Field_SetEncoding(id3_field,ID3TE_UTF16);
+ ID3Field_SetUNICODE(id3_field,(const unicode_t*)string_converted);
+ g_free(string_converted);
+
+ id3_field_encoding = ID3Frame_GetField(id3_frame,ID3FN_TEXTENC);
+ if (id3_field_encoding)
+ ID3Field_SetINT(id3_field_encoding,ID3TE_UTF16);
+
+ return ID3TE_UTF16;
+ break;
+
+ case 9999:
+ default:
+ //string_converted = convert_string(string,"UTF-8",FILE_WRITING_ID3V2_NO_UNICODE_CHARACTER_SET,TRUE);
+ string_converted = Id3tag_Rules_For_ISO_Fields(string,"UTF-8",FILE_WRITING_ID3V2_NO_UNICODE_CHARACTER_SET);
+ ID3Field_SetEncoding(id3_field,ID3TE_ISO8859_1);
+ ID3Field_SetASCII(id3_field,string_converted);
+ g_free(string_converted);
+
+ id3_field_encoding = ID3Frame_GetField(id3_frame,ID3FN_TEXTENC);
+ if (id3_field_encoding)
+ ID3Field_SetINT(id3_field_encoding,ID3TE_ISO8859_1);
+
+ return ID3TE_NONE;
+ break;
+ }
+ }
+
+ return ID3TE_NONE;
+}
+
+
+/*
+ * By default id3lib converts id3tag to ISO-8859-1 (single byte character set)
+ * Note : converting UTF-16 string (two bytes character set) to ISO-8859-1
+ * remove only the second byte => a strange string appears...
+ */
+void Id3tag_Prepare_ID3v1 (ID3Tag *id3_tag)
+{
+ ID3Frame *frame;
+ ID3Field *id3_field_encoding;
+ ID3Field *id3_field_text;
+
+ if ( id3_tag != NULL )
+ {
+ ID3TagIterator *id3_tag_iterator;
+ size_t num_chars = 0;
+ gchar *string, *string1, *string_converted;
+
+ id3_tag_iterator = ID3Tag_CreateIterator(id3_tag);
+ while ( NULL != (frame = ID3TagIterator_GetNext(id3_tag_iterator)) )
+ {
+ ID3_TextEnc enc = ID3TE_ISO8859_1;
+ ID3_FrameID frameid;
+
+ frameid = ID3Frame_GetID(frame);
+
+ if (frameid != ID3FID_TITLE
+ && frameid != ID3FID_LEADARTIST
+ && frameid != ID3FID_ALBUM
+ && frameid != ID3FID_YEAR
+ && frameid != ID3FID_TRACKNUM
+ && frameid != ID3FID_CONTENTTYPE
+ && frameid != ID3FID_COMMENT)
+ continue;
+
+ id3_field_encoding = ID3Frame_GetField(frame, ID3FN_TEXTENC);
+ if (id3_field_encoding != NULL)
+ enc = ID3Field_GetINT(id3_field_encoding);
+ id3_field_text = ID3Frame_GetField(frame, ID3FN_TEXT);
+
+ /* The frames in ID3TE_ISO8859_1 are already converted to the selected
+ * single-byte character set if used. So we treat only Unicode frames */
+ if ( (id3_field_text != NULL)
+ && (enc != ID3TE_ISO8859_1) )
+ {
+ // Read UTF-16 frame
+ string = g_malloc0(sizeof(unicode_t)*ID3V2_MAX_STRING_LEN+1);
+ num_chars = ID3Field_GetUNICODE_1(id3_field_text,(unicode_t *)string,ID3V2_MAX_STRING_LEN,0);
+ // "convert_string_1" as we need to pass length for UTF-16
+ string1 = convert_string_1(string,num_chars,"UTF-16BE","UTF-8",FALSE);
+
+ string_converted = Id3tag_Rules_For_ISO_Fields(string1,"UTF-8",FILE_WRITING_ID3V1_CHARACTER_SET);
+
+ if (string_converted)
+ {
+ ID3Field_SetEncoding(id3_field_text,ID3TE_ISO8859_1); // Not necessary for ISO-8859-1
+ ID3Field_SetASCII(id3_field_text,string_converted);
+ ID3Field_SetINT(id3_field_encoding,ID3TE_ISO8859_1);
+ g_free(string_converted);
+ }
+ g_free(string);
+ g_free(string1);
+ }
+ }
+ ID3TagIterator_Delete(id3_tag_iterator);
+ }
+}
+
+/*
+ * This function must be used for tag fields containing ISO data.
+ * We use specials functionalities of iconv : //TRANSLIT and //IGNORE (the last
+ * one doesn't work on my system) to force conversion to the target encoding.
+ */
+gchar *Id3tag_Rules_For_ISO_Fields (const gchar *string, const gchar *from_codeset, const gchar *to_codeset)
+{
+ gchar *string_converted = NULL;
+
+ if (!string || !from_codeset || !to_codeset)
+ return NULL;
+
+ if (FILE_WRITING_ID3V1_ICONV_OPTIONS_NO)
+ {
+ string_converted = convert_string(string,from_codeset,to_codeset,TRUE);
+
+ }else if (FILE_WRITING_ID3V1_ICONV_OPTIONS_TRANSLIT)
+ {
+ // iconv_open (3):
+ // When the string "//TRANSLIT" is appended to tocode, transliteration
+ // is activated. This means that when a character cannot be represented
+ // in the target character set, it can be approximated through one or
+ // several similarly looking characters.
+ gchar *to_enc = g_strconcat(to_codeset, "//TRANSLIT", NULL);
+ string_converted = convert_string(string,from_codeset,to_enc,TRUE);
+ g_free(to_enc);
+
+ }else if (FILE_WRITING_ID3V1_ICONV_OPTIONS_IGNORE)
+ {
+ // iconv_open (3):
+ // When the string "//IGNORE" is appended to tocode, characters that
+ // cannot be represented in the target character set will be silently
+ // discarded.
+ gchar *to_enc = g_strconcat(to_codeset, "//IGNORE", NULL);
+ string_converted = convert_string(string,from_codeset,to_enc,TRUE);
+ g_free(to_enc);
+ }
+
+ return string_converted;
+}
+
+/*
+ * Some files which contains only zeroes create an infinite loop in id3lib...
+ * To generate a file with zeroes : dd if=/dev/zero bs=1M count=6 of=test-corrupted-mp3-zero-contend.mp3
+ */
+gboolean Id3tag_Check_If_File_Is_Corrupted (gchar *filename)
+{
+ FILE *file;
+ unsigned char tmp[256];
+ unsigned char tmp0[256];
+ gint bytes_read;
+ gboolean result = TRUE;
+ gint cmp;
+
+ if (!filename)
+ return FALSE;
+
+ if ( (file=fopen(filename,"rb"))==NULL )
+ {
+ gchar *filename_utf8 = filename_to_display(filename);
+ Log_Print(_("ERROR while opening file: '%s' (%s)."),filename_utf8,g_strerror(errno));
+ g_free(filename_utf8);
+ return FALSE;
+ }
+
+ memset(&tmp0,0,256);
+ while (!feof(file))
+ {
+ bytes_read = fread(tmp, 1, 256, file);
+ if ( (cmp=memcmp(tmp,tmp0,bytes_read)) != 0)
+ {
+ result = FALSE;
+ break;
+ }
+ }
+ fclose(file);
+
+ if (result)
+ {
+ GtkWidget *msgbox = NULL;
+ gchar *msg;
+ gchar *basename;
+ gchar *basename_utf8;
+
+ basename = g_path_get_basename(filename);
+ basename_utf8 = filename_to_display(basename);
+
+ msg = g_strdup_printf(_("As the following corrupted file: '%s'\nwill cause "
+ "an error in id3lib, it will not be processed by the program."),basename_utf8);
+ msgbox = msg_box_new(_("Corrupted file..."),msg,GTK_STOCK_DIALOG_ERROR,BUTTON_CLOSE,0);
+ msg_box_hide_check_button(MSG_BOX(msgbox));
+ msg_box_run(MSG_BOX(msgbox));
+ gtk_widget_destroy(msgbox);
+ g_free(msg);
+ g_free(basename);
+ g_free(basename_utf8);
+ }
+
+ return result;
+}
+
+
+/*
+ * Function to detect if id3lib isn't bugged when writting to Unicode
+ * Returns TRUE if bugged, else FALSE
+ */
+gboolean Id3tag_Check_If_Id3lib_Is_Bugged (void)
+{
+ FILE *file;
+ unsigned char tmp[16] = {0xFF, 0xFB, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00};
+ ID3Tag *id3_tag = NULL;
+ gchar *filename;
+ gchar *result = NULL;
+ ID3Frame *id3_frame;
+ gboolean use_unicode;
+
+
+ if (!HOME_VARIABLE)
+ return FALSE;
+
+ // Create a temporary file
+ filename = g_strconcat(HOME_VARIABLE,
+ HOME_VARIABLE[strlen(HOME_VARIABLE)-1]!=G_DIR_SEPARATOR?G_DIR_SEPARATOR_S:"",
+ ".easytag"G_DIR_SEPARATOR_S"test_easytag.mp3",
+ NULL);
+ if ( (file=fopen(filename,"w+"))==NULL )
+ {
+ gchar *filename_utf8 = filename_to_display(filename);
+ Log_Print(_("ERROR while opening file: '%s' (%s)."),filename_utf8,g_strerror(errno));
+ g_free(filename_utf8);
+ return FALSE;
+ }
+ // Set data in the file
+ fwrite(&tmp,16,1,file);
+ fclose(file);
+
+ // Save state of switches as we must force to Unicode before writting
+ use_unicode = FILE_WRITING_ID3V2_USE_UNICODE_CHARACTER_SET;
+ FILE_WRITING_ID3V2_USE_UNICODE_CHARACTER_SET = TRUE;
+
+ id3_tag = ID3Tag_New();
+ ID3Tag_Link_1(id3_tag,filename);
+
+ // Create a new 'title' field for testing
+ id3_frame = ID3Frame_NewID(ID3FID_TITLE);
+ ID3Tag_AttachFrame(id3_tag,id3_frame);
+ // Use a Chinese character instead of the latin-1 character as in Id3tag_Set_Field()
+ // we try to convert the string to ISO-8859-1 even in the Unicode mode.
+ //Id3tag_Set_Field(id3_frame, ID3FN_TEXT, "é"); // This latin-1 character is written in Unicode as 'E9 FF' instead of 'E9 00' if bugged
+ Id3tag_Set_Field(id3_frame, ID3FN_TEXT, "ã‚°"); // This Chinese character is written in Unicode as 'FF FE B0 FF' instead of 'FF FE B0 30' if bugged
+
+ // Update the tag
+ ID3Tag_UpdateByTagType(id3_tag,ID3TT_ID3V2);
+ ID3Tag_Delete(id3_tag);
+
+ FILE_WRITING_ID3V2_USE_UNICODE_CHARACTER_SET = use_unicode;
+
+
+ id3_tag = ID3Tag_New();
+ ID3Tag_Link_1(id3_tag,filename);
+ // Read the written field
+ if ( (id3_frame = ID3Tag_FindFrameWithID(id3_tag,ID3FID_TITLE)) )
+ {
+ result = Id3tag_Get_Field(id3_frame,ID3FN_TEXT);
+ }
+
+ ID3Tag_Delete(id3_tag);
+ remove(filename);
+
+ // Same string found? if yes => not bugged
+ //if ( result && strcmp(result,"é")!=0 )
+ if ( result && strcmp(result,"ã‚°")!=0 )
+ {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+#endif /* ENABLE_ID3LIB */
+
+
+/*
+ * Write tag according the version selected by the user
+ */
+gboolean Id3tag_Write_File_Tag (ET_File *ETFile)
+{
+#ifdef ENABLE_ID3LIB
+ if (FILE_WRITING_ID3V2_VERSION_4)
+ return Id3tag_Write_File_v24Tag(ETFile);
+ else
+ return Id3tag_Write_File_v23Tag(ETFile);
+#else
+ return Id3tag_Write_File_v24Tag(ETFile);
+#endif
+}
+
+#endif /* ENABLE_MP3 */
+
+
+// Placed out #ifdef ENABLE_MP3 as not dependant of id3lib, and used in CDDB
+/*
+ * Returns the corresponding genre value of the input string (for ID3v1.x),
+ * else returns 0xFF (unknown genre, but not invalid).
+ */
+guchar Id3tag_String_To_Genre (gchar *genre)
+{
+ guint i;
+
+ if (genre != NULL)
+ {
+ for (i=0; i<=GENRE_MAX; i++)
+ if (strcasecmp(genre,id3_genres[i])==0)
+ return (guchar)i;
+ }
+ return (guchar)0xFF;
+}
+
+
+/*
+ * Returns the name of a genre code if found
+ * Three states for genre code :
+ * - defined (0 to GENRE_MAX)
+ * - undefined/unknown (GENRE_MAX+1 to ID3_INVALID_GENRE-1)
+ * - invalid (>ID3_INVALID_GENRE)
+ */
+gchar *Id3tag_Genre_To_String (unsigned char genre_code)
+{
+ if (genre_code>=ID3_INVALID_GENRE) /* empty */
+ return "";
+ else if (genre_code>GENRE_MAX) /* unknown tag */
+ return "Unknown";
+ else /* known tag */
+ return id3_genres[genre_code];
+}
+
diff --git a/src/id3_tag.h b/src/id3_tag.h
new file mode 100755
index 0000000..755668b
--- /dev/null
+++ b/src/id3_tag.h
@@ -0,0 +1,47 @@
+/* id3tag.h - 2001/02/16 */
+/*
+ * EasyTAG - Tag editor for MP3 and Ogg Vorbis files
+ * Copyright (C) 2001-2003 Jerome Couderc <easytag@gmail.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef __ID3TAG_H__
+#define __ID3TAG_H__
+
+
+#include <glib.h>
+#include "et_core.h"
+
+
+/****************
+ * Declarations *
+ ****************/
+
+
+
+/**************
+ * Prototypes *
+ **************/
+gboolean Id3tag_Read_File_Tag (gchar *filename, File_Tag *FileTag);
+gboolean Id3tag_Write_File_v24Tag (ET_File *ETFile);
+gboolean Id3tag_Write_File_Tag (ET_File *ETFile);
+
+gchar *Id3tag_Genre_To_String (unsigned char genre_code);
+guchar Id3tag_String_To_Genre (gchar *genre);
+
+
+#endif /* __ID3TAG_H__ */
diff --git a/src/id3lib/Makefile.am b/src/id3lib/Makefile.am
new file mode 100755
index 0000000..17c011b
--- /dev/null
+++ b/src/id3lib/Makefile.am
@@ -0,0 +1,14 @@
+CFLAGS = @CFLAGS@ @GTK_CFLAGS@
+
+noinst_LIBRARIES = libid3bugfix.a
+
+INCLUDES = -DLOCALE=\"$(localedir)\"
+
+EXTRA_DIST = \
+ patch_id3lib_3.8.3_UTF16_writing_bug.diff
+
+
+libid3bugfix_a_SOURCES = \
+ c_wrapper.cpp \
+ id3_bugfix.h
+
diff --git a/src/id3lib/Makefile.in b/src/id3lib/Makefile.in
new file mode 100644
index 0000000..6eea830
--- /dev/null
+++ b/src/id3lib/Makefile.in
@@ -0,0 +1,466 @@
+# Makefile.in generated by automake 1.9.6 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005 Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+top_builddir = ../..
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+INSTALL = @INSTALL@
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = src/id3lib
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/configure.in
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+LIBRARIES = $(noinst_LIBRARIES)
+ARFLAGS = cru
+libid3bugfix_a_AR = $(AR) $(ARFLAGS)
+libid3bugfix_a_LIBADD =
+am_libid3bugfix_a_OBJECTS = c_wrapper.$(OBJEXT)
+libid3bugfix_a_OBJECTS = $(am_libid3bugfix_a_OBJECTS)
+DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
+LTCXXCOMPILE = $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CXXFLAGS) $(CXXFLAGS)
+CXXLD = $(CXX)
+CXXLINK = $(LIBTOOL) --tag=CXX --mode=link $(CXXLD) $(AM_CXXFLAGS) \
+ $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(LIBTOOL) --tag=CC --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+SOURCES = $(libid3bugfix_a_SOURCES)
+DIST_SOURCES = $(libid3bugfix_a_SOURCES)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMDEP_FALSE = @AMDEP_FALSE@
+AMDEP_TRUE = @AMDEP_TRUE@
+AMTAR = @AMTAR@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@ @GTK_CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+ECHO = @ECHO@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+F77 = @F77@
+FFLAGS = @FFLAGS@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GOBJECT_QUERY = @GOBJECT_QUERY@
+GTK_CFLAGS = @GTK_CFLAGS@
+GTK_LIBS = @GTK_LIBS@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAINTAINER_MODE_FALSE = @MAINTAINER_MODE_FALSE@
+MAINTAINER_MODE_TRUE = @MAINTAINER_MODE_TRUE@
+MAKEINFO = @MAKEINFO@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MSGFMT = @MSGFMT@
+OBJEXT = @OBJEXT@
+OGG_CFLAGS = @OGG_CFLAGS@
+OGG_LIBS = @OGG_LIBS@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PKG_CONFIG = @PKG_CONFIG@
+POFILES = @POFILES@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+RANLIB = @RANLIB@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+VORBIS_CFLAGS = @VORBIS_CFLAGS@
+VORBIS_LIBS = @VORBIS_LIBS@
+WAVPACK_CFLAGS = @WAVPACK_CFLAGS@
+WAVPACK_LIBS = @WAVPACK_LIBS@
+XGETTEXT = @XGETTEXT@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_F77 = @ac_ct_F77@
+ac_ct_RANLIB = @ac_ct_RANLIB@
+ac_ct_STRIP = @ac_ct_STRIP@
+ac_pt_PKG_CONFIG = @ac_pt_PKG_CONFIG@
+am__fastdepCC_FALSE = @am__fastdepCC_FALSE@
+am__fastdepCC_TRUE = @am__fastdepCC_TRUE@
+am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@
+am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+datadir = @datadir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+noinst_LIBRARIES = libid3bugfix.a
+INCLUDES = -DLOCALE=\"$(localedir)\"
+EXTRA_DIST = \
+ patch_id3lib_3.8.3_UTF16_writing_bug.diff
+
+libid3bugfix_a_SOURCES = \
+ c_wrapper.cpp \
+ id3_bugfix.h
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .cpp .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
+ && exit 0; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/id3lib/Makefile'; \
+ cd $(top_srcdir) && \
+ $(AUTOMAKE) --foreign src/id3lib/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+clean-noinstLIBRARIES:
+ -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES)
+libid3bugfix.a: $(libid3bugfix_a_OBJECTS) $(libid3bugfix_a_DEPENDENCIES)
+ -rm -f libid3bugfix.a
+ $(libid3bugfix_a_AR) libid3bugfix.a $(libid3bugfix_a_OBJECTS) $(libid3bugfix_a_LIBADD)
+ $(RANLIB) libid3bugfix.a
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/c_wrapper.Po@am__quote@
+
+.cpp.o:
+@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
+@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ $<
+
+.cpp.obj:
+@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \
+@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.cpp.lo:
+@am__fastdepCXX_TRUE@ if $(LTCXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
+@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Plo"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(LTCXXCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+distclean-libtool:
+ -rm -f libtool
+uninstall-info-am:
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$tags $$unique; \
+ fi
+ctags: CTAGS
+CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ test -z "$(CTAGS_ARGS)$$tags$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$tags $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && cd $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) $$here
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \
+ list='$(DISTFILES)'; for file in $$list; do \
+ case $$file in \
+ $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \
+ $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \
+ esac; \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test "$$dir" != "$$file" && test "$$dir" != "."; then \
+ dir="/$$dir"; \
+ $(mkdir_p) "$(distdir)$$dir"; \
+ else \
+ dir=''; \
+ fi; \
+ if test -d $$d/$$file; then \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
+ fi; \
+ cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
+ else \
+ test -f $(distdir)/$$file \
+ || cp -p $$d/$$file $(distdir)/$$file \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LIBRARIES)
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-noinstLIBRARIES \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-libtool distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-exec-am:
+
+install-info: install-info-am
+
+install-man:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-info-am
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
+ clean-libtool clean-noinstLIBRARIES ctags distclean \
+ distclean-compile distclean-generic distclean-libtool \
+ distclean-tags distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am install-exec \
+ install-exec-am install-info install-info-am install-man \
+ install-strip installcheck installcheck-am installdirs \
+ maintainer-clean maintainer-clean-generic mostlyclean \
+ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \
+ pdf pdf-am ps ps-am tags uninstall uninstall-am \
+ uninstall-info-am
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/src/id3lib/c_wrapper.cpp b/src/id3lib/c_wrapper.cpp
new file mode 100755
index 0000000..1f56147
--- /dev/null
+++ b/src/id3lib/c_wrapper.cpp
@@ -0,0 +1,141 @@
+// id3lib: a C++ library for creating and manipulating id3v1/v2 tags
+// Copyright 1999, 2000 Scott Thomas Haug
+// Copyright 2002 Thijmen Klok (thijmen@id3lib.org)
+
+// This library is free software; you can redistribute it and/or modify it
+// under the terms of the GNU Library General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or (at your
+// option) any later version.
+//
+// This library 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 Library General Public
+// License for more details.
+//
+// You should have received a copy of the GNU Library General Public License
+// along with this library; if not, write to the Free Software Foundation,
+// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+// The id3lib authors encourage improvements and optimisations to be sent to
+// the id3lib coordinator. Please see the README file for details on where to
+// send such submissions. See the AUTHORS file for a list of people who have
+// contributed to id3lib. See the ChangeLog file for a list of changes to
+// id3lib. These files are distributed with id3lib at
+// http://download.sourceforge.net/id3lib/
+
+//#include <string.h>
+#include <config.h>
+
+#ifdef ENABLE_ID3LIB
+
+#include <id3.h>
+#include <id3/field.h>
+#include <id3/tag.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+#define ID3_CATCH(code) try { code; } catch (...) { }
+
+ //
+ // Tag wrappers
+ //
+
+ ID3_C_EXPORT bool CCONV
+ ID3Field_SetEncoding(ID3Field *field, ID3_TextEnc enc)
+ {
+ bool changed = false;
+ if (field)
+ {
+ ID3_CATCH(changed = reinterpret_cast<ID3_Field *>(field)->SetEncoding(enc));
+ }
+ return changed;
+ }
+
+ ID3_C_EXPORT ID3_TextEnc CCONV
+ ID3Field_GetEncoding(const ID3Field *field)
+ {
+ ID3_TextEnc enc = ID3TE_NONE;
+ if (field)
+ {
+ ID3_CATCH(enc = reinterpret_cast<const ID3_Field *>(field)->GetEncoding());
+ }
+ return enc;
+ }
+
+ ID3_C_EXPORT bool CCONV
+ ID3Field_IsEncodable(const ID3Field *field)
+ {
+ bool isEncodable = false;
+ if (field)
+ {
+ ID3_CATCH(isEncodable = reinterpret_cast<const ID3_Field *>(field)->IsEncodable());
+ }
+ return isEncodable;
+ }
+
+ ID3_C_EXPORT ID3_FieldType CCONV
+ ID3Field_GetType(const ID3Field *field)
+ {
+ ID3_FieldType fieldType = ID3FTY_NONE;
+ if (field)
+ {
+ ID3_CATCH(fieldType = reinterpret_cast<const ID3_Field *>(field)->GetType());
+ }
+ return fieldType;
+ }
+
+ /*ID3_C_EXPORT ID3_FieldID CCONV
+ ID3Field_GetID(const ID3Field *field)
+ {
+ ID3_FieldID fieldID = ID3FN_NOFIELD;
+ if (field)
+ {
+ ID3_CATCH(fieldType = reinterpret_cast<const ID3_Field *>(field)->GetID());
+ }
+ return fieldType;
+ }*/
+
+
+
+ //
+ // Header wrappers
+ //
+
+ // Call with :
+ // const Mp3_Headerinfo* headerInfo = ID3Tag_GetMp3HeaderInfo(tag);
+ ID3_C_EXPORT const Mp3_Headerinfo* CCONV
+ ID3Tag_GetMp3HeaderInfo(ID3Tag *tag)
+ {
+ const Mp3_Headerinfo* headerInfo = NULL;
+ if (tag)
+ {
+ ID3_CATCH(headerInfo = reinterpret_cast<const ID3_Tag *>(tag)->GetMp3HeaderInfo());
+ }
+ return headerInfo;
+ }
+
+ // Call with :
+ // Mp3_Headerinfo* headerInfo = malloc(sizeof(Mp3_Headerinfo));
+ // ID3Tag_GetMp3HeaderInfo(tag, headerInfo);
+ /*ID3_C_EXPORT bool CCONV
+ ID3Tag_GetMp3HeaderInfo(ID3Tag *tag, Mp3_Headerinfo* headerInfo)
+ {
+ const Mp3_Headerinfo* rem_headerInfo = NULL;
+ if (tag)
+ {
+ ID3_CATCH(rem_headerInfo = reinterpret_cast<const ID3_Tag * >(tag)->GetMp3HeaderInfo());
+ }
+ // Does GCC understand this? (VS does)
+ if (rem_headerInfo) *headerInfo=*rem_headerInfo;
+ return rem_headerInfo!=NULL;
+ }*/
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* ENABLE_ID3LIB */
diff --git a/src/id3lib/id3_bugfix.h b/src/id3lib/id3_bugfix.h
new file mode 100755
index 0000000..74e7351
--- /dev/null
+++ b/src/id3lib/id3_bugfix.h
@@ -0,0 +1,56 @@
+/*
+ * id3lib: a software library for creating and manipulating id3v1/v2 tags
+ * Copyright 1999, 2000 Scott Thomas Haug
+ * Copyright 2002 Thijmen Klok (thijmen@id3lib.org)
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Library General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This library 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 Library General Public
+ * License for more details.
+
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ * The id3lib authors encourage improvements and optimisations to be sent to
+ * the id3lib coordinator. Please see the README file for details on where to
+ * send such submissions. See the AUTHORS file for a list of people who have
+ * contributed to id3lib. See the ChangeLog file for a list of changes to
+ * id3lib. These files are distributed with id3lib at
+ * http://download.sourceforge.net/id3lib/
+ */
+
+#ifndef _ID3LIB_BUGFIX_H_
+#define _ID3LIB_BUGFIX_H_
+
+
+#ifdef ENABLE_ID3LIB
+
+#include "id3.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+ ID3_C_EXPORT bool CCONV ID3Field_SetEncoding (ID3Field *field, ID3_TextEnc enc);
+ ID3_C_EXPORT ID3_TextEnc CCONV ID3Field_GetEncoding (const ID3Field *field);
+ ID3_C_EXPORT bool CCONV ID3Field_IsEncodable (const ID3Field *field);
+ ID3_C_EXPORT ID3_FieldType CCONV ID3Field_GetType (const ID3Field *field);
+ //ID3_C_EXPORT ID3_FieldID CCONV ID3Field_GetID (const ID3Field *field);
+
+ ID3_C_EXPORT const Mp3_Headerinfo* CCONV ID3Tag_GetMp3HeaderInfo (ID3Tag *tag);
+
+#ifdef __cplusplus
+}
+#endif /*__cplusplus*/
+
+
+#endif /* ENABLE_ID3LIB */
+
+#endif /* _ID3LIB_BUGFIX_H_ */
diff --git a/src/id3lib/patch_id3lib_3.8.3_UTF16_writing_bug.diff b/src/id3lib/patch_id3lib_3.8.3_UTF16_writing_bug.diff
new file mode 100755
index 0000000..b05d2cf
--- /dev/null
+++ b/src/id3lib/patch_id3lib_3.8.3_UTF16_writing_bug.diff
@@ -0,0 +1,39 @@
+diff -ruN id3lib-3.8.3.orig/ChangeLog id3lib-3.8.3/ChangeLog
+--- id3lib-3.8.3.orig/ChangeLog 2003-03-02 01:23:00.000000000 +0100
++++ id3lib-3.8.3/ChangeLog 2006-02-22 00:33:59.946214472 +0100
+@@ -1,3 +1,8 @@
++2006-02-17 Jerome Couderc
++
++ * Patch from Spoon to fix UTF-16 writing bug
++ http://sourceforge.net/tracker/index.php?func=detail&aid=1016290&group_id=979&atid=300979
++
+ 2003-03-02 Sunday 17:38 Thijmen Klok <thijmen@id3lib.org>
+
+ * THANKS (1.20): added more people
+diff -ruN id3lib-3.8.3.orig/src/io_helpers.cpp id3lib-3.8.3/src/io_helpers.cpp
+--- id3lib-3.8.3.orig/src/io_helpers.cpp 2003-03-02 01:23:00.000000000 +0100
++++ id3lib-3.8.3/src/io_helpers.cpp 2006-02-22 00:35:02.926639992 +0100
+@@ -363,11 +363,22 @@
+ // Write the BOM: 0xFEFF
+ unicode_t BOM = 0xFEFF;
+ writer.writeChars((const unsigned char*) &BOM, 2);
++ // Patch from Spoon : 2004-08-25 14:17
++ // http://sourceforge.net/tracker/index.php?func=detail&aid=1016290&group_id=979&atid=300979
++ // Wrong code
++ //for (size_t i = 0; i < size; i += 2)
++ //{
++ // unicode_t ch = (data[i] << 8) | data[i+1];
++ // writer.writeChars((const unsigned char*) &ch, 2);
++ //}
++ // Right code
++ unsigned char *pdata = (unsigned char *) data.c_str();
+ for (size_t i = 0; i < size; i += 2)
+ {
+- unicode_t ch = (data[i] << 8) | data[i+1];
++ unicode_t ch = (pdata[i] << 8) | pdata[i+1];
+ writer.writeChars((const unsigned char*) &ch, 2);
+ }
++ // End patch
+ }
+ return writer.getCur() - beg;
+ }
diff --git a/src/id3v24_tag.c b/src/id3v24_tag.c
new file mode 100755
index 0000000..7b3c729
--- /dev/null
+++ b/src/id3v24_tag.c
@@ -0,0 +1,1499 @@
+/* id3v24_tag.c - 2007/05/25 */
+/*
+ * EasyTAG - Tag editor for MP3 and Ogg Vorbis files
+ * Copyright (C) 2001-2003 Jerome Couderc <easytag@gmail.com>
+ * Copyright (C) 2006-2007 Alexey Illarionov <littlesavage@rambler.ru>
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+
+#include <gtk/gtk.h>
+#include <glib/gi18n-lib.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <unistd.h>
+
+#include "id3_tag.h"
+#include "picture.h"
+#include "easytag.h"
+#include "browser.h"
+#include "genres.h"
+#include "setting.h"
+#include "misc.h"
+#include "et_core.h"
+#include "msgbox.h"
+#include "charset.h"
+
+#ifdef WIN32
+# include "win32/win32dep.h"
+#endif
+
+
+#ifdef ENABLE_MP3
+
+#include <id3tag.h>
+
+
+/****************
+ * Declarations *
+ ****************/
+#define MULTIFIELD_SEPARATOR " - "
+#define EASYTAG_STRING_ENCODEDBY "Encoded by"
+
+enum {
+ EASYTAG_ID3_FIELD_LATIN1 = 0x0001,
+ EASYTAG_ID3_FIELD_LATIN1FULL = 0x0002,
+ EASYTAG_ID3_FIELD_LATIN1LIST = 0x0004,
+ EASYTAG_ID3_FIELD_STRING = 0x0008,
+ EASYTAG_ID3_FIELD_STRINGFULL = 0x0010,
+ EASYTAG_ID3_FIELD_STRINGLIST = 0x0020,
+ EASYTAG_ID3_FIELD_LANGUAGE = 0x0040
+};
+
+/**************
+ * Prototypes *
+ **************/
+static int etag_guess_byteorder (const id3_ucs4_t *ustr, gchar **ret);
+static int etag_ucs42gchar (const id3_ucs4_t *usrc, unsigned is_latin, unsigned is_utf16, gchar **res);
+static int libid3tag_Get_Frame_Str (const struct id3_frame *frame, unsigned etag_field_type, gchar **retstr);
+
+static void Id3tag_delete_frames (struct id3_tag *tag, const gchar *name, int start);
+static void Id3tag_delete_txxframes (struct id3_tag *tag, const gchar *param1, int start);
+static struct id3_frame *Id3tag_findncreate_frame (struct id3_tag *tag, const gchar *name);
+static struct id3_frame *Id3tag_findncreate_txxframe (struct id3_tag *tag, const gchar *param1);
+static int id3taglib_set_field (struct id3_frame *frame, const gchar *str, enum id3_field_type type, int num, int clear, int id3v1);
+static int etag_set_tags (const gchar *str, const char *frame_name, enum id3_field_type field_type, struct id3_tag *v1tag, struct id3_tag *v2tag, gboolean *strip_tags);
+static int etag_write_tags (const gchar *filename, const struct id3_tag *v1tag, const struct id3_tag *v2tag, gboolean strip_tags);
+
+/*************
+ * Functions *
+ *************/
+
+/*
+ * Read id3v1.x / id3v2 tag and load data into the File_Tag structure.
+ * Returns TRUE on success, else FALSE.
+ * If a tag entry exists (ex: title), we allocate memory, else value stays to NULL
+ */
+gboolean Id3tag_Read_File_Tag (gchar *filename, File_Tag *FileTag)
+{
+ int tmpfile;
+ struct id3_file *file;
+ struct id3_tag *tag;
+ struct id3_frame *frame;
+ union id3_field *field;
+ gchar *string1, *string2;
+ Picture *prev_pic = NULL;
+ int i, j;
+ unsigned tmpupdate, update = 0;
+ long tagsize;
+
+
+ if (!filename || !FileTag)
+ return FALSE;
+
+ if ( (tmpfile=open(filename,O_RDONLY)) < 0 )
+ {
+ gchar *filename_utf8 = filename_to_display(filename);
+ g_print(_("ERROR while opening file: '%s' (%s).\n\a"),filename_utf8,g_strerror(errno));
+ g_free(filename_utf8);
+ return FALSE;
+ }
+
+ string1 = g_try_malloc(ID3_TAG_QUERYSIZE);
+ if (string1==NULL)
+ {
+ close(tmpfile);
+ return FALSE;
+ }
+
+ // Check if the file has an ID3v2 tag or/and an ID3v1 tags
+ // 1) ID3v2 tag
+ if (read(tmpfile, string1, ID3_TAG_QUERYSIZE) != ID3_TAG_QUERYSIZE)
+ {
+ close(tmpfile);
+ return FALSE;
+ }
+
+ if ((tagsize = id3_tag_query((id3_byte_t const *)string1, ID3_TAG_QUERYSIZE)) <= ID3_TAG_QUERYSIZE)
+ {
+ // ID3v2 tag not found!
+ update = FILE_WRITING_ID3V2_WRITE_TAG;
+ }else
+ {
+ /* ID3v2 tag found */
+ if (FILE_WRITING_ID3V2_WRITE_TAG == 0)
+ {
+ update = 1;
+ }else
+ {
+ /* Determine version if user want to upgrade old tags */
+ if (CONVERT_OLD_ID3V2_TAG_VERSION
+ && (string1 = realloc(string1, tagsize))
+ && (read(tmpfile, &string1[ID3_TAG_QUERYSIZE], tagsize - ID3_TAG_QUERYSIZE) == tagsize - ID3_TAG_QUERYSIZE)
+ && (tag = id3_tag_parse((id3_byte_t const *)string1, tagsize))
+ )
+ {
+ unsigned version = id3_tag_version(tag);
+#ifdef ENABLE_ID3LIB
+ /* Besides upgrade old tags we will downgrade id3v2.4 to id3v2.3 */
+ if ( FILE_WRITING_ID3V2_VERSION_4 )
+ {
+ update = (ID3_TAG_VERSION_MAJOR(version) < 4);
+ }else
+ {
+ update = ((ID3_TAG_VERSION_MAJOR(version) < 3)
+ | (ID3_TAG_VERSION_MAJOR(version) == 4));
+ }
+#else
+ update = (ID3_TAG_VERSION_MAJOR(version) < 4);
+#endif
+ id3_tag_delete(tag);
+ }
+ }
+ }
+
+ // 2) ID3v1 tag
+ if ( (lseek(tmpfile,-128, SEEK_END) >= 0) // Go to the beginning of ID3v1 tag
+ && (string1)
+ && (read(tmpfile, string1, 3) == 3)
+ && (string1[0] == 'T')
+ && (string1[1] == 'A')
+ && (string1[2] == 'G')
+ )
+ {
+ // ID3v1 tag found!
+ if (!FILE_WRITING_ID3V1_WRITE_TAG)
+ update = 1;
+ }else
+ {
+ // ID3v1 tag not found!
+ if (FILE_WRITING_ID3V1_WRITE_TAG)
+ update = 1;
+ }
+
+ g_free(string1);
+
+ if ((file = id3_file_fdopen(tmpfile, ID3_FILE_MODE_READONLY)) == NULL)
+ {
+ close(tmpfile);
+ return FALSE;
+ }
+
+ if ( ((tag = id3_file_tag(file)) == NULL)
+ || (tag->nframes == 0))
+ {
+ id3_file_close(file);
+ return FALSE;
+ }
+
+
+ /****************
+ * Title (TIT2) *
+ ****************/
+ if ( (frame = id3_tag_findframe(tag, ID3_FRAME_TITLE, 0)) )
+ update |= libid3tag_Get_Frame_Str(frame, EASYTAG_ID3_FIELD_STRINGLIST, &FileTag->title);
+
+ /*****************
+ * Artist (TPE1) *
+ *****************/
+ if ( (frame = id3_tag_findframe(tag, ID3_FRAME_ARTIST, 0)) )
+ update |= libid3tag_Get_Frame_Str(frame, EASYTAG_ID3_FIELD_STRINGLIST, &FileTag->artist);
+
+ /****************
+ * Album (TALB) *
+ ****************/
+ if ( (frame = id3_tag_findframe(tag, ID3_FRAME_ALBUM, 0)) )
+ update |= libid3tag_Get_Frame_Str(frame, ~0, &FileTag->album);
+
+ /************************
+ * Part of a set (TPOS) *
+ ************************/
+ if ( (frame = id3_tag_findframe(tag,"TPOS", 0)) )
+ update |= libid3tag_Get_Frame_Str(frame, ~0, &FileTag->disc_number);
+
+ /********************
+ * Year (TYER/TDRC) *
+ ********************/
+ if ( (frame = id3_tag_findframe(tag, ID3_FRAME_YEAR, 0)) )
+ {
+ update |= libid3tag_Get_Frame_Str(frame, ~0, &string1);
+ if ( string1 )
+ {
+ Strip_String(string1);
+ FileTag->year = string1;
+ }
+ }
+
+ /********************************
+ * Track and Total Track (TRCK) *
+ ********************************/
+ if ( (frame = id3_tag_findframe(tag, ID3_FRAME_TRACK, 0)) )
+ {
+ update |= libid3tag_Get_Frame_Str(frame, ~0, &string1);
+ if ( string1 )
+ {
+ string2 = g_utf8_strchr(string1,-1,'/');
+
+ if (NUMBER_TRACK_FORMATED)
+ {
+ if (string2)
+ {
+ FileTag->track_total = g_strdup_printf("%.*d",NUMBER_TRACK_FORMATED_SPIN_BUTTON,atoi(string2+1)); // Just to have numbers like this : '01', '05', '12', ...
+ *string2 = '\0'; // To cut string1
+ }
+ FileTag->track = g_strdup_printf("%.*d",NUMBER_TRACK_FORMATED_SPIN_BUTTON,atoi(string1)); // Just to have numbers like this : '01', '05', '12', ...
+ }else
+ {
+ if (string2)
+ {
+ FileTag->track_total = g_strdup(string2+1);
+ *string2 = '\0'; // To cut string1
+ }
+ FileTag->track = g_strdup(string1);
+ }
+ g_free(string1);
+
+ }
+ }
+
+ /****************
+ * Genre (TCON) *
+ ****************/
+ if ( (frame = id3_tag_findframe(tag, ID3_FRAME_GENRE, 0)) )
+ {
+ update |= libid3tag_Get_Frame_Str(frame, ~0, &string1);
+ if ( string1 )
+ {
+ /*
+ * We manipulate only the name of the genre
+ * Genre is written like this :
+ * - "(<genre_id>)" -> "(3)"
+ * - "<genre_name>" -> "Dance"
+ * - "(<genre_id>)<refinement>" -> "(3)EuroDance"
+ */
+ gchar *tmp;
+ unsigned genre = 0;
+ FileTag->genre = NULL;
+
+ if ( (string1[0]=='(') && (tmp=strchr(string1,')')) && (tmp+1) && (strlen((tmp+1))>0) )
+ /* Convert a genre written as '(3)EuroDance' into 'EuroDance' */
+ {
+ FileTag->genre = g_strdup(tmp+1);
+ } else if ( (string1[0]=='(') && strchr(string1,')') )
+ {
+ /* Convert a genre written as '(3)' into 'Dance' */
+ genre = strtol(string1+1, &tmp, 10);
+ if (*tmp != ')')
+ {
+ FileTag->genre = g_strdup(string1);
+ }
+ } else
+ {
+ genre = strtol(string1, &tmp, 10);
+ if (tmp == string1)
+ FileTag->genre = g_strdup(string1);
+ }
+
+ if (!FileTag->genre
+ && id3_genre_index(genre))
+ FileTag->genre = (gchar *)id3_ucs4_utf8duplicate(id3_genre_index(genre));
+
+ g_free(string1);
+ }
+ }
+
+ /******************
+ * Comment (COMM) *
+ ******************/
+ if ( (frame = id3_tag_findframe(tag, ID3_FRAME_COMMENT, 0)) )
+ {
+ update |= libid3tag_Get_Frame_Str(frame, /* EASYTAG_ID3_FIELD_STRING | */ EASYTAG_ID3_FIELD_STRINGFULL,
+ &FileTag->comment);
+ /*{
+ gchar *comment1 = Id3tag_Get_Field(frame,ID3FN_DESCRIPTION)
+ gchar *comment2 = Id3tag_Get_Field(id3_frame,ID3FN_LANGUAGE)
+ }*/
+ }
+
+ /*******************
+ * Composer (TCOM) *
+ *******************/
+ if ( (frame = id3_tag_findframe(tag, "TCOM", 0)) )
+ update |= libid3tag_Get_Frame_Str(frame, ~0, &FileTag->composer);
+
+ /**************************
+ * Original artist (TOPE) *
+ **************************/
+ if ( (frame = id3_tag_findframe(tag, "TOPE", 0)) )
+ update |= libid3tag_Get_Frame_Str(frame, ~0, &FileTag->orig_artist);
+
+ /*******************
+ * Copyright (TCOP)*
+ *******************/
+ if ( (frame = id3_tag_findframe(tag, "TCOP", 0)) )
+ update |= libid3tag_Get_Frame_Str(frame, ~0, &FileTag->copyright);
+
+ /**************
+ * URL (WXXX) *
+ **************/
+ if ( (frame = id3_tag_findframe(tag, "WXXX", 0)) )
+ update |= libid3tag_Get_Frame_Str(frame, EASYTAG_ID3_FIELD_LATIN1, &FileTag->url);
+
+ /*********************
+ * Encoded by (TENC) *
+ *********************/
+ if ( (frame = id3_tag_findframe(tag, "TENC", 0)) )
+ update |= libid3tag_Get_Frame_Str(frame, ~0, &FileTag->encoded_by);
+ /* Encoded by in TXXX frames */
+ string1 = NULL;
+ for (i = 0; (frame = id3_tag_findframe(tag, "TXX", i)); i++)
+ {
+ if (FileTag->encoded_by)
+ break;
+ tmpupdate = libid3tag_Get_Frame_Str(frame, ~0, &string1);
+ if (string1)
+ {
+ if (strcasestr(string1, EASYTAG_STRING_ENCODEDBY MULTIFIELD_SEPARATOR) == string1)
+ {
+ FileTag->encoded_by = g_strdup(&string1[sizeof(EASYTAG_STRING_ENCODEDBY) + sizeof(MULTIFIELD_SEPARATOR) - 2]);
+ g_free(string1);
+ update |= tmpupdate;
+ }else
+ g_free(string1);
+ }
+ }
+
+ /******************
+ * Picture (APIC) *
+ ******************/
+ for (i = 0; (frame = id3_tag_findframe(tag, "APIC", i)); i++)
+ {
+ Picture *pic;
+
+ pic = Picture_Allocate();
+ if (!prev_pic)
+ FileTag->picture = pic;
+ else
+ prev_pic->next = pic;
+ prev_pic = pic;
+
+ pic->data = NULL;
+
+ // Picture file data
+ for (j = 0; (field = id3_frame_field(frame, j)); j++)
+ {
+ switch (id3_field_type(field))
+ {
+ case ID3_FIELD_TYPE_BINARYDATA:
+ {
+ id3_length_t size;
+ id3_byte_t const *data;
+ data = id3_field_getbinarydata(field, &size);
+ if (pic->data)
+ g_free(pic->data);
+ if ( (pic->data = g_memdup(data, size)) )
+ pic->size = size;
+ }
+ break;
+ case ID3_FIELD_TYPE_INT8:
+ pic->type = id3_field_getint(field);
+ break;
+ default:
+ break;
+ }
+ }
+
+ // Picture description
+ update |= libid3tag_Get_Frame_Str(frame, EASYTAG_ID3_FIELD_STRING, &pic->description);
+ }
+
+ /**********************
+ * Lyrics (SYLC/USLT) *
+ **********************/
+ /** see also id3/misc_support.h **
+ if ( (id3_frame = ID3Tag_FindFrameWithID(id3_tag,ID3FID_SYNCEDLYRICS)) )
+ {
+ gulong size = 0;
+ guchar *data = NULL;
+ gchar *description = NULL;
+ gchar *language = NULL;
+ gint timestamp_format = 0;
+ gint sync_type = 0;
+
+ // SyncLyrics data
+ if ( (id3_field = ID3Frame_GetField(id3_frame, ID3FN_DATA)) )
+ {
+ size = ID3Field_Size(id3_field);
+ data = g_malloc(size);
+ ID3Field_GetBINARY(id3_field, data, size);
+ }
+
+ // SyncLyrics description
+ description = Id3tag_Get_Field(id3_frame, ID3FN_DESCRIPTION);
+
+ // SyncLyrics language
+ language = Id3tag_Get_Field(id3_frame, ID3FN_LANGUAGE);
+
+ // SyncLyrics timestamp field
+ if ( (id3_field = ID3Frame_GetField(id3_frame, ID3FN_TIMESTAMPFORMAT)) )
+ {
+ timestamp_format = ID3Field_GetINT(id3_field);
+ }
+
+ // SyncLyrics content type
+ if ( (id3_field = ID3Frame_GetField(id3_frame, ID3FN_CONTENTTYPE)) )
+ {
+ sync_type = ID3Field_GetINT(id3_field);
+ }
+
+ // Print data
+ // j.a. Pouwelse - pouwelse :
+ // http://sourceforge.net/tracker/index.php?func=detail&aid=401873&group_id=979&atid=300979
+ {
+ char tag[255];
+ unsigned int time;
+ luint pos = 0;
+
+ g_print("SyncLyrics/description : %s\n",description);
+ g_print("SyncLyrics/language : %s\n",language);
+ g_print("SyncLyrics/timestamp format : %s (%d)\n",timestamp_format==ID3TSF_FRAME ? "ID3TSF_FRAME" : timestamp_format==ID3TSF_MS ? "ID3TSF_MS" : "" ,timestamp_format);
+ g_print("SyncLyrics/sync type : %s (%d)\n",sync_type==ID3CT_LYRICS ? "ID3CT_LYRICS" : "",sync_type);
+
+
+ g_print("SyncLyrics size : %d\n", size);
+ g_print("Lyrics/data :\n");
+ while (pos < size)
+ {
+ strcpy(tag,data+pos);
+ //g_print("txt start=%d ",pos);
+ pos+=strlen(tag)+1; // shift string and terminating \0
+ //g_print("txt end=%d ",pos);
+ memcpy(&time,data+pos,4);
+ pos+=4;
+ //g_print("%d -> %s\n",time,tag);
+ g_print("%s",tag);
+ }
+ }
+
+ } **/
+
+ if (update)
+ FileTag->saved = FALSE;
+
+ /* Free allocated data */
+ id3_file_close(file);
+
+ return TRUE;
+}
+
+
+/* Guess byteorder of UTF-16 string that was converted to 'ustr' (some ID3
+ * tags contain UTF-16 string without BOM and in fact can be UTF16BE and
+ * UTF-16LE). Function correct byteorder, if it is needed, and return new
+ * corrected utf-8 string in 'ret'.
+ * Return value of function is 0 if byteorder was not changed
+ */
+static int
+etag_guess_byteorder(const id3_ucs4_t *ustr, gchar **ret) /* XXX */
+{
+ unsigned i, len;
+ gunichar *gstr;
+ gchar *tmp, *str, *str2;
+ const gchar *charset;
+
+ if (!ustr || !*ustr)
+ {
+ if (ret)
+ *ret = NULL;
+ return 0;
+ }
+
+ if (USE_NON_STANDARD_ID3_READING_CHARACTER_SET)
+ charset = FILE_READING_ID3V1V2_CHARACTER_SET;
+ else if (!FILE_WRITING_ID3V2_USE_UNICODE_CHARACTER_SET) /* XXX */
+ charset = FILE_WRITING_ID3V2_NO_UNICODE_CHARACTER_SET;
+ else g_get_charset(&charset);
+
+ if (!charset)
+ charset = "ISO-8859-1";
+
+ tmp = (gchar *)id3_ucs4_utf8duplicate(ustr);
+ str = g_convert(tmp, -1, charset, "UTF-8", NULL, NULL, NULL);
+ if (str)
+ {
+ g_free(str);
+ if (ret)
+ *ret = tmp;
+ else
+ free (tmp);
+ return 0; /* byteorder not changed */
+ }
+
+ for (len = 0; ustr[len]; len++);
+
+ gstr = g_try_malloc(sizeof(gunichar) * (len + 1));
+ if ( gstr == NULL )
+ {
+ if (ret)
+ *ret = tmp;
+ else
+ free(tmp);
+ return 0;
+ }
+
+ for (i = 0; i <= len; i++)
+ gstr[i] = ((ustr[i] & 0xff00) >> 8) | ((ustr[i] & 0xff) << 8);
+ str = g_ucs4_to_utf8(gstr, len, NULL, NULL, NULL);
+ g_free(gstr);
+
+ if (str == NULL)
+ {
+ if (ret)
+ *ret = tmp;
+ else
+ free(tmp);
+ return 0;
+ }
+
+ str2 = g_convert(str, -1, charset, "UTF-8", NULL, NULL, NULL);
+
+ if (str2 && *str2)
+ {
+ g_free(str2);
+ free(tmp);
+ if (ret)
+ *ret = str;
+ else
+ free(str);
+ return 1;
+ }
+
+ g_free(str);
+
+ if (ret)
+ *ret = tmp;
+ else
+ free(tmp);
+
+ return 0;
+}
+
+
+/* convert ucs4 string to utf-8 gchar in 'res' according to easytag charset
+ * conversion settings and field type.
+ * function return 0 if byteorder of utf-16 string was changed
+ */
+static int
+etag_ucs42gchar(const id3_ucs4_t *usrc, unsigned is_latin,
+ unsigned is_utf16, gchar **res)
+{
+ gchar *latinstr, *retstr;
+ int retval;
+
+ if (!usrc || !*usrc)
+ {
+ if (res)
+ *res = NULL;
+ return 0;
+ }
+
+ retval = 0, retstr = NULL;
+
+ if (is_latin && USE_NON_STANDARD_ID3_READING_CHARACTER_SET)
+ {
+ if ((latinstr = (gchar *)id3_ucs4_latin1duplicate(usrc)))
+ {
+ retstr = convert_string(latinstr, FILE_READING_ID3V1V2_CHARACTER_SET, "UTF-8", FALSE);
+ free(latinstr);
+ }
+ }else
+ {
+ if (is_utf16)
+ {
+ retval |= etag_guess_byteorder(usrc, &retstr);
+ }else
+ {
+ retstr = (gchar *)id3_ucs4_utf8duplicate(usrc);
+ }
+ }
+
+ if (res)
+ *res = retstr;
+ else
+ free (retstr);
+
+ return retval;
+}
+
+
+static int
+libid3tag_Get_Frame_Str(const struct id3_frame *frame, unsigned etag_field_type, gchar **retstr)
+{
+ const union id3_field *field;
+ unsigned i, j, strcnt;
+ gchar *ret, *tmpstr, *tmpstr2, *latinstr;
+ unsigned field_type;
+ const id3_ucs4_t *usrc;
+ unsigned is_latin, is_utf16;
+ unsigned retval;
+
+ ret = NULL;
+ retval = 0;
+ is_latin = 1, is_utf16 = 0;
+
+ for (i = 0; (field = id3_frame_field(frame, i)); i++)
+ {
+ if (id3_field_type(field) == ID3_FIELD_TYPE_TEXTENCODING)
+ {
+ is_latin = (id3_field_gettextencoding(field) == ID3_FIELD_TEXTENCODING_ISO_8859_1);
+ is_utf16 = (id3_field_gettextencoding(field) == ID3_FIELD_TEXTENCODING_UTF_16);
+ break;
+ }
+ }
+
+ for (i = 0; (field = id3_frame_field(frame, i)); i++)
+ {
+ tmpstr = tmpstr2 = NULL;
+ switch (field_type = id3_field_type(field))
+ {
+ case ID3_FIELD_TYPE_LATIN1:
+ case ID3_FIELD_TYPE_LATIN1FULL:
+ if (field_type == ID3_FIELD_TYPE_LATIN1)
+ {
+ if (!(etag_field_type & EASYTAG_ID3_FIELD_LATIN1))
+ continue;
+ }else
+ if (!(etag_field_type & EASYTAG_ID3_FIELD_LATIN1FULL))
+ continue;
+ latinstr = g_strdup(field_type == ID3_FIELD_TYPE_LATIN1 ? (gchar *)id3_field_getlatin1(field) : (gchar *)id3_field_getfulllatin1(field));
+ if (USE_NON_STANDARD_ID3_READING_CHARACTER_SET)
+ {
+ tmpstr = convert_string(latinstr, FILE_READING_ID3V1V2_CHARACTER_SET, "UTF-8", FALSE);
+ free(latinstr);
+ }
+ else
+ tmpstr = latinstr;
+ break;
+
+ case ID3_FIELD_TYPE_STRING:
+ case ID3_FIELD_TYPE_STRINGFULL:
+ if (field_type == ID3_FIELD_TYPE_STRING)
+ {
+ if (!(etag_field_type & EASYTAG_ID3_FIELD_STRING))
+ continue;
+ }else
+ if (!(etag_field_type & EASYTAG_ID3_FIELD_STRINGFULL))
+ continue;
+ usrc = (field_type == ID3_FIELD_TYPE_STRING) ? id3_field_getstring(field) : id3_field_getfullstring(field);
+ retval |= etag_ucs42gchar(usrc, is_latin, is_utf16, &tmpstr);
+ break;
+
+ case ID3_FIELD_TYPE_STRINGLIST:
+ if (!(etag_field_type & EASYTAG_ID3_FIELD_STRINGLIST))
+ continue;
+ strcnt = id3_field_getnstrings(field);
+ for (j = 0; j < strcnt; j++)
+ {
+ retval |= etag_ucs42gchar(
+ id3_field_getstrings(field, j),
+ is_latin, is_utf16, &tmpstr );
+
+ if (tmpstr2 && *tmpstr2 && g_utf8_validate(tmpstr2, -1, NULL))
+ {
+ if (tmpstr)
+ tmpstr = g_strconcat(tmpstr, " ", tmpstr2, NULL);
+ else
+ tmpstr = g_strdup(tmpstr2);
+ }
+
+ free(tmpstr2);
+ }
+
+ default:
+ break;
+ }
+ if (tmpstr && *tmpstr && g_utf8_validate(tmpstr, -1, NULL))
+ {
+ if (ret)
+ ret = g_strconcat(ret, MULTIFIELD_SEPARATOR, tmpstr, NULL);
+ else
+ ret = g_strdup(tmpstr);
+ }
+ g_free(tmpstr);
+ }
+
+ if (retstr)
+ *retstr = ret;
+ else
+ free(ret);
+
+ return retval;
+}
+
+
+/*
+ * Write the ID3 tags to the file. Returns TRUE on success, else 0.
+ */
+gboolean Id3tag_Write_File_v24Tag (ET_File *ETFile)
+{
+ File_Tag *FileTag;
+ gchar *filename, *filename_utf8;
+ gchar *basename_utf8;
+ struct id3_tag *v1tag, *v2tag;
+ struct id3_frame *frame;
+ union id3_field *field;
+ gchar *string1;
+ Picture *pic;
+ unsigned i;
+ gint error = 0;
+ gboolean strip_tags = TRUE;
+ guchar genre_value = ID3_INVALID_GENRE;
+
+
+ if (!ETFile && !ETFile->FileTag)
+ return FALSE;
+
+ FileTag = (File_Tag *)ETFile->FileTag->data;
+ filename = ((File_Name *)ETFile->FileNameCur->data)->value;
+ filename_utf8 = ((File_Name *)ETFile->FileNameCur->data)->value_utf8;
+
+ v1tag = v2tag = NULL;
+
+ if (FILE_WRITING_ID3V2_WRITE_TAG)
+ {
+ struct id3_file *file;
+ struct id3_tag *tmptag;
+ unsigned i;
+ id3_byte_t *tmpbuf = NULL;
+
+ /* Read old v2 tag */
+ if ((file = id3_file_open(filename, ID3_FILE_MODE_READWRITE)) == NULL)
+ return FALSE;
+
+ if ((tmptag = id3_file_tag(file)) == NULL)
+ {
+ id3_file_close(file);
+ return FALSE;
+ }
+
+ id3_tag_options(tmptag, ID3_TAG_OPTION_UNSYNCHRONISATION
+ | ID3_TAG_OPTION_ID3V1
+ | ID3_TAG_OPTION_COMPRESSION
+ | ID3_TAG_OPTION_APPENDEDTAG,
+ ID3_TAG_OPTION_UNSYNCHRONISATION);
+
+ /* XXX Create new tag and copy all frames*/
+ i = id3_tag_render(tmptag, NULL);
+ if ((i > 10)
+ && (tmpbuf = g_try_malloc(i))
+ && (id3_tag_render(tmptag, tmpbuf) != 0)
+ )
+ v2tag = id3_tag_parse(tmpbuf, i);
+ g_free(tmpbuf);
+
+ if (v2tag == NULL)
+ {
+ if ((v2tag = id3_tag_new()) == NULL)
+ {
+ id3_file_close(file);
+ return FALSE;
+ }
+ }
+
+ id3_file_close(file);
+
+ /* Set padding XXX */
+ if ((v2tag->paddedsize < 1024)
+ || ((v2tag->paddedsize > 4096) && (i < 1024))
+ )
+ v2tag->paddedsize = 1024;
+
+ /* Set options */
+ id3_tag_options(v2tag,
+ ID3_TAG_OPTION_UNSYNCHRONISATION
+ | ID3_TAG_OPTION_APPENDEDTAG
+ | ID3_TAG_OPTION_ID3V1
+ | ID3_TAG_OPTION_CRC
+ | ID3_TAG_OPTION_COMPRESSION,
+ ID3_TAG_OPTION_UNSYNCHRONISATION
+ );
+ if (FILE_WRITING_ID3V2_USE_CRC32)
+ id3_tag_options(v2tag, ID3_TAG_OPTION_CRC, ~0);
+ if (FILE_WRITING_ID3V2_USE_COMPRESSION)
+ id3_tag_options(v2tag, ID3_TAG_OPTION_COMPRESSION, ~0);
+ }
+
+ if (FILE_WRITING_ID3V1_WRITE_TAG)
+ {
+ v1tag = id3_tag_new();
+ if (!v1tag)
+ return FALSE;
+ id3_tag_options(v1tag, ID3_TAG_OPTION_ID3V1, ~0);
+ }
+
+
+ /*********
+ * Title *
+ *********/
+ etag_set_tags(FileTag->title, ID3_FRAME_TITLE, ID3_FIELD_TYPE_STRINGLIST, v1tag, v2tag, &strip_tags);
+
+ /**********
+ * Artist *
+ **********/
+ etag_set_tags(FileTag->artist, ID3_FRAME_ARTIST, ID3_FIELD_TYPE_STRINGLIST, v1tag, v2tag, &strip_tags);
+
+ /*********
+ * Album *
+ *********/
+ etag_set_tags(FileTag->album, ID3_FRAME_ALBUM, ID3_FIELD_TYPE_STRINGLIST, v1tag, v2tag, &strip_tags);
+
+ /***************
+ * Part of set *
+ ***************/
+ etag_set_tags(FileTag->disc_number, "TPOS", ID3_FIELD_TYPE_STRINGLIST, NULL, v2tag, &strip_tags);
+
+ /********
+ * Year *
+ ********/
+ etag_set_tags(FileTag->year, ID3_FRAME_YEAR, ID3_FIELD_TYPE_STRINGLIST, v1tag, v2tag, &strip_tags);
+
+ /*************************
+ * Track and Total Track *
+ *************************/
+ if ( FileTag->track
+ && FileTag->track_total
+ && *FileTag->track_total )
+ string1 = g_strconcat(FileTag->track,"/",FileTag->track_total,NULL);
+ else
+ string1 = NULL;
+
+ etag_set_tags(string1 ? string1 : FileTag->track, ID3_FRAME_TRACK, ID3_FIELD_TYPE_STRINGLIST, NULL, v2tag, &strip_tags);
+ etag_set_tags(FileTag->track, ID3_FRAME_TRACK, ID3_FIELD_TYPE_STRINGLIST, v1tag, NULL, &strip_tags);
+ g_free(string1);
+
+ /*********
+ * Genre *
+ **********
+ /* Genre is written like this :
+ * - "<genre_id>" -> "3"
+ * - "<genre_name>" -> "EuroDance"
+ */
+ if (FileTag->genre)
+ genre_value = Id3tag_String_To_Genre(FileTag->genre);
+
+ if (genre_value == ID3_INVALID_GENRE)
+ string1 = g_strdup(FileTag->genre);
+ else
+ string1 = g_strdup_printf("%d",genre_value);
+
+ etag_set_tags(string1, ID3_FRAME_GENRE, ID3_FIELD_TYPE_STRINGLIST, v1tag, v2tag, &strip_tags);
+ g_free(string1);
+
+ /***********
+ * Comment *
+ ***********/
+ etag_set_tags(FileTag->comment, ID3_FRAME_COMMENT, ID3_FIELD_TYPE_STRINGFULL, v1tag, v2tag, &strip_tags);
+
+ /************
+ * Composer *
+ ************/
+ etag_set_tags(FileTag->composer, "TCOM", ID3_FIELD_TYPE_STRINGLIST, NULL, v2tag, &strip_tags);
+
+ /*******************
+ * Original artist *
+ *******************/
+ etag_set_tags(FileTag->orig_artist, "TOPE", ID3_FIELD_TYPE_STRINGLIST, NULL, v2tag, &strip_tags);
+
+ /*************
+ * Copyright *
+ *************/
+ etag_set_tags(FileTag->copyright, "TCOP", ID3_FIELD_TYPE_STRINGLIST, NULL, v2tag, &strip_tags);
+
+ /*******
+ * URL *
+ *******/
+ etag_set_tags(FileTag->url, "WXXX", ID3_FIELD_TYPE_LATIN1, NULL, v2tag, &strip_tags);
+
+ /***************
+ * Encoded by *
+ ***************/
+ if ( v2tag && FileTag->encoded_by && *FileTag->encoded_by
+ && (frame = Id3tag_findncreate_txxframe(v2tag, EASYTAG_STRING_ENCODEDBY)))
+ {
+ id3taglib_set_field(frame, EASYTAG_STRING_ENCODEDBY, ID3_FIELD_TYPE_STRING, 0, 1, 0);
+ id3taglib_set_field(frame, FileTag->encoded_by, ID3_FIELD_TYPE_STRING, 1, 0, 0);
+ strip_tags = FALSE;
+ }else
+ if (v2tag)
+ Id3tag_delete_txxframes(v2tag, EASYTAG_STRING_ENCODEDBY, 0);
+
+ /***********
+ * Picture *
+ ***********/
+ Id3tag_delete_frames(v2tag, "APIC", 0);
+
+ pic = FileTag->picture;
+
+ if (v2tag)
+ {
+ while (pic)
+ {
+ if ((frame = id3_frame_new("APIC")) == NULL)
+ continue;
+
+ id3_tag_attachframe(v2tag, frame);
+ for (i = 0; (field = id3_frame_field(frame, i)); i++)
+ {
+ switch (id3_field_type(field))
+ {
+ case ID3_FIELD_TYPE_LATIN1:
+ switch (Picture_Format(pic))
+ {
+ case PICTURE_FORMAT_JPEG:
+ id3_field_setlatin1(field, (id3_latin1_t const *)"image/jpeg");
+ break;
+ case PICTURE_FORMAT_PNG:
+ id3_field_setlatin1(field, (id3_latin1_t const *)"image/png");
+ break;
+ default:
+ break;
+ }
+ break;
+ case ID3_FIELD_TYPE_INT8:
+ id3_field_setint(field, pic->type);
+ break;
+ case ID3_FIELD_TYPE_BINARYDATA:
+ id3_field_setbinarydata(field, pic->data, pic->size);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (pic->description)
+ id3taglib_set_field(frame, pic->description, ID3_FIELD_TYPE_STRING, 0, 0, 0);
+
+ strip_tags = FALSE;
+ pic = pic->next;
+ }
+ }
+
+ /****************************************
+ * File length (in milliseconds) DISCARD*
+ ****************************************/
+
+ /*********************************
+ * Update id3v1.x and id3v2 tags *
+ *********************************/
+ error |= etag_write_tags(filename, v1tag, v2tag, strip_tags);
+
+ if (v1tag)
+ id3_tag_delete(v1tag);
+ if (v2tag)
+ id3_tag_delete(v2tag);
+
+ if (error == 0)
+ {
+ basename_utf8 = g_path_get_basename(filename_utf8);
+ Log_Print(_("Updated tag of '%s'"),basename_utf8);
+ g_free(basename_utf8);
+ }
+
+ if (error) return FALSE;
+ else return TRUE;
+
+}
+
+/* Dele all frames with 'name'
+ * begining with frame num 'start' (0-based)
+ * from tag 'tag'
+ */
+static void
+Id3tag_delete_frames(struct id3_tag *tag, const gchar *name, int start)
+{
+ struct id3_frame *frame;
+
+ if (!tag || !name || !*name)
+ return;
+
+ while ((frame = id3_tag_findframe(tag, name, start)))
+ {
+ id3_tag_detachframe(tag, frame);
+ id3_frame_delete(frame);
+ }
+
+}
+
+static void
+Id3tag_delete_txxframes(struct id3_tag *tag, const gchar *param1, int start)
+{
+ int i;
+ struct id3_frame *frame;
+ union id3_field *field;
+ const id3_ucs4_t *ucs4string;
+ gchar *str;
+
+ if (!tag || !param1 || !*param1)
+ return;
+
+ for (i = start; (frame = id3_tag_findframe(tag, "TXXX", i)); )
+ if ( (field = id3_frame_field(frame, 1))
+ && (ucs4string = id3_field_getstring(field)) )
+ {
+ str = NULL;
+ if ((str = (gchar *)id3_ucs4_latin1duplicate(ucs4string))
+ && (strcasestr(str, param1) == str) )
+ {
+ g_free(str);
+ id3_tag_detachframe(tag, frame);
+ id3_frame_delete(frame);
+ }else
+ {
+ i++;
+ g_free(str);
+ }
+ }else
+ i++;
+}
+
+/* Find first frame with name 'name' in tag 'tag'
+ * create new if not found
+ */
+static struct id3_frame *
+Id3tag_findncreate_frame(struct id3_tag *tag, const gchar *name)
+{
+ struct id3_frame *frame;
+
+ if (!tag || !name || !*name)
+ return NULL;
+
+ frame = id3_tag_findframe(tag, name, 0);
+ if (!frame)
+ {
+ if ((frame = id3_frame_new(name)) == NULL)
+ return NULL;
+ id3_tag_attachframe(tag, frame);
+ }
+
+ return frame;
+}
+
+/* Find first "TXX" (user defined text information) frame in tag 'tag'
+ * with first string parameter (name) 'param'
+ * create new if not found
+ */
+static struct id3_frame *
+Id3tag_findncreate_txxframe(struct id3_tag *tag, const gchar *param1)
+{
+ const id3_ucs4_t *ucs4string;
+ struct id3_frame *frame;
+ union id3_field *field;
+ int i;
+ gchar *str;
+
+ if (!tag || !param1 || !*param1)
+ return NULL;
+
+ for (i = 0; (frame = id3_tag_findframe(tag, "TXXX", i)); i++)
+ if ( (field = id3_frame_field(frame, 1))
+ && (ucs4string = id3_field_getstring(field)) )
+ {
+ str = NULL;
+ if ((str = (gchar *)id3_ucs4_latin1duplicate(ucs4string))
+ && (strcasestr(str, param1) == str) )
+ {
+ g_free(str);
+ break;
+ }else
+ g_free(str);
+ }
+
+ if (frame == NULL)
+ {
+ if ((frame = id3_frame_new("TXXX")) == NULL)
+ return NULL;
+ id3_tag_attachframe(tag, frame);
+ }
+
+ return frame;
+}
+
+static int
+id3taglib_set_field(struct id3_frame *frame, const gchar *str,
+ enum id3_field_type type,
+ int num, int clear, int id3v1)
+{
+ union id3_field *field;
+ enum id3_field_type curtype;
+ id3_ucs4_t *buf;
+ gchar *latinstr, *encname;
+ enum id3_field_textencoding enc_field;
+ unsigned i;
+ unsigned is_set;
+
+ latinstr = NULL, buf = NULL;
+ is_set = 0;
+ enc_field = ID3_FIELD_TEXTENCODING_ISO_8859_1;
+
+ if (str)
+ {
+ /* Prepare str for writing according to easytag charset coversion settings */
+ if ((FILE_WRITING_ID3V2_USE_UNICODE_CHARACTER_SET == 0)
+ || (type == ID3_FIELD_TYPE_LATIN1)
+ || (type == ID3_FIELD_TYPE_LATIN1FULL)
+ || id3v1)
+ {
+ encname = NULL;
+ /* id3v1 fields converted using its own character set and iconv options */
+ if ( id3v1 )
+ {
+ if ( !FILE_WRITING_ID3V1_ICONV_OPTIONS_NO )
+ encname = g_strconcat(
+ FILE_WRITING_ID3V1_CHARACTER_SET,
+ FILE_WRITING_ID3V1_ICONV_OPTIONS_TRANSLIT ? "//TRANSLIT" : "//IGNORE",
+ NULL);
+ else
+ encname = g_strdup(FILE_WRITING_ID3V1_CHARACTER_SET);
+ } else
+ {
+ /* latin1 fields (such as URL) always converted with ISO-8859-1*/
+ if ((type != ID3_FIELD_TYPE_LATIN1) && (type != ID3_FIELD_TYPE_LATIN1FULL))
+ {
+ if ( FILE_WRITING_ID3V2_ICONV_OPTIONS_NO == 0)
+ encname = g_strconcat(
+ FILE_WRITING_ID3V2_NO_UNICODE_CHARACTER_SET,
+ FILE_WRITING_ID3V2_ICONV_OPTIONS_TRANSLIT ? "//TRANSLIT" : "//IGNORE",
+ NULL);
+ else
+ encname = g_strdup(FILE_WRITING_ID3V2_NO_UNICODE_CHARACTER_SET);
+ }
+ }
+
+ latinstr = convert_string(str, "UTF-8", encname ? encname : "ISO-8859-1//IGNORE", TRUE);
+ free(encname);
+ buf = id3_latin1_ucs4duplicate((id3_latin1_t const *)latinstr);
+ } else
+ {
+ if (!strcmp(FILE_WRITING_ID3V2_UNICODE_CHARACTER_SET, "UTF-16"))
+ {
+ enc_field = ID3_FIELD_TEXTENCODING_UTF_16;
+ buf = id3_utf8_ucs4duplicate((id3_utf8_t const *)str);
+ }else
+ {
+ enc_field = ID3_FIELD_TEXTENCODING_UTF_8;
+ buf = id3_utf8_ucs4duplicate((id3_utf8_t const *)str);
+ }
+ }
+ }
+
+ if (frame)
+ frame->flags &= ~ID3_FRAME_FLAG_FORMATFLAGS;
+
+ for (i = 0; (field = id3_frame_field(frame, i)); i++)
+ {
+ if (is_set && !clear)
+ break;
+
+ switch (curtype = id3_field_type(field))
+ {
+ case ID3_FIELD_TYPE_TEXTENCODING:
+ id3_field_settextencoding(field, enc_field);
+ break;
+ case ID3_FIELD_TYPE_LATIN1:
+ if (clear)
+ id3_field_setlatin1(field, NULL);
+ if ((type == curtype) && !is_set)
+ {
+ if (num == 0)
+ {
+ id3_field_setlatin1(field, (id3_latin1_t const *)latinstr);
+ is_set = 1;
+ }else
+ num--;
+ }
+ break;
+ case ID3_FIELD_TYPE_LATIN1FULL:
+ if (clear)
+ id3_field_setfulllatin1(field, NULL);
+ if ((type == curtype) && !is_set)
+ {
+ if (num == 0)
+ {
+ id3_field_setfulllatin1(field, (id3_latin1_t const *)latinstr);
+ is_set = 1;
+ }else
+ num--;
+ }
+ break;
+ case ID3_FIELD_TYPE_STRING:
+ if (clear)
+ id3_field_setstring(field, NULL);
+ if ((type == curtype) && !is_set)
+ {
+ if (num == 0)
+ {
+ id3_field_setstring(field, buf);
+ is_set = 1;
+ }else
+ num--;
+ }
+ break;
+ case ID3_FIELD_TYPE_STRINGFULL:
+ if (clear)
+ id3_field_setfullstring(field, NULL);
+ if ((type == curtype) && !is_set)
+ {
+ if (num == 0)
+ {
+ id3_field_setfullstring(field, buf);
+ is_set = 1;
+ }else
+ num--;
+ }
+ break;
+ case ID3_FIELD_TYPE_STRINGLIST:
+ if (clear)
+ id3_field_setstrings(field, 0, NULL);
+ if ((type == curtype) && !is_set)
+ {
+ if ((num == 0) && (buf))
+ {
+ id3_field_addstring(field, buf);
+ is_set = 1;
+ }else
+ num--;
+ }
+ break;
+ default:
+ break;
+ }
+ if (is_set)
+ {
+ free(latinstr);
+ free(buf);
+ latinstr = NULL, buf = NULL;
+ }
+ }
+
+ if (latinstr || buf)
+ {
+ free(latinstr);
+ free(buf);
+ return 1;
+ } else
+ return 0;
+}
+
+
+static int
+etag_set_tags(const gchar *str,
+ const char *frame_name,
+ enum id3_field_type field_type,
+ struct id3_tag *v1tag,
+ struct id3_tag *v2tag,
+ gboolean *strip_tags)
+{
+ struct id3_frame *ftmp;
+
+ if ( str && *str )
+ {
+ *strip_tags = FALSE;
+
+ if (v2tag
+ && (ftmp = Id3tag_findncreate_frame(v2tag, frame_name)))
+ id3taglib_set_field(ftmp, str, field_type, 0, 1, 0);
+ if (v1tag
+ && (ftmp = Id3tag_findncreate_frame(v1tag, frame_name)))
+ id3taglib_set_field(ftmp, str, field_type, 0, 1, 1);
+ }else
+ if (v2tag)
+ Id3tag_delete_frames(v2tag, frame_name, 0);
+
+ return 0;
+}
+
+static int
+etag_write_tags(const gchar *filename, const struct id3_tag *v1tag, const struct id3_tag *v2tag, gboolean strip_tags)
+{
+ id3_byte_t *v1buf, *v2buf;
+ id3_length_t v1size = 0, v2size = 0;
+ char tmp[ID3_TAG_QUERYSIZE];
+ int fd;
+ int curpos;
+ long filev2size, ctxsize;
+ char *ctx = NULL;
+ int err = 0;
+
+ v1buf = v2buf = NULL;
+ if ( !strip_tags )
+ {
+ /* Render v1 tag */
+ if (v1tag)
+ {
+ v1size = id3_tag_render(v1tag, NULL);
+ if (v1size == 128)
+ {
+ v1buf = g_try_malloc(v1size);
+ if (id3_tag_render(v1tag, v1buf) != v1size)
+ {
+ /* NOTREACHED */
+ g_free(v1buf);
+ v1buf = NULL;
+ }
+ }
+ }
+
+ /* Render v2 tag */
+ if (v2tag)
+ {
+ v2size = id3_tag_render(v2tag, NULL);
+ if (v2size > 10)
+ {
+ v2buf = g_try_malloc0(v2size);
+ if ((v2size = id3_tag_render(v2tag, v2buf)) == 0)
+ {
+ /* NOTREACHED */
+ g_free(v2buf);
+ v2buf = NULL;
+ }
+ }
+ }
+ }
+ if (v1buf == NULL)
+ v1size = 0;
+ if (v2buf == NULL)
+ v2size = 0;
+
+ if ((fd = open(filename, O_RDWR)) < 0)
+ {
+ err = errno;
+ g_free(v1buf);
+ g_free(v2buf);
+ return (err);
+ }
+
+ err = 1;
+
+ /* Handle Id3v1 tag */
+ if (lseek(fd, -128, SEEK_END) < 0)
+ goto out;
+ if (read(fd, tmp, ID3_TAG_QUERYSIZE) != ID3_TAG_QUERYSIZE)
+ goto out;
+
+ if ( (tmp[0] == 'T')
+ && (tmp[1] == 'A')
+ && (tmp[2] == 'G')
+ )
+ {
+ if (lseek(fd, -128, SEEK_END) < 0)
+ goto out;
+ }else
+ if (lseek(fd, 0, SEEK_END) < 0)
+ goto out;
+
+ /* Search id3v2 tags at the end of the file (before any ID3v1 tag) */
+ /* XXX: Unsafe */
+ if (lseek(fd, -ID3_TAG_QUERYSIZE, SEEK_CUR) >= 0)
+ {
+ if (read(fd, tmp, ID3_TAG_QUERYSIZE) != ID3_TAG_QUERYSIZE)
+ goto out;
+ filev2size = id3_tag_query((id3_byte_t const *)tmp, ID3_TAG_QUERYSIZE);
+ if ((filev2size > 10)
+ && lseek(fd, -filev2size, SEEK_CUR))
+ {
+ if (read(fd, tmp, ID3_TAG_QUERYSIZE) != ID3_TAG_QUERYSIZE)
+ goto out;
+ if (id3_tag_query((id3_byte_t const *)tmp, ID3_TAG_QUERYSIZE) != filev2size)
+ lseek(fd, -ID3_TAG_QUERYSIZE - filev2size, SEEK_CUR);
+ else
+ lseek(fd, -ID3_TAG_QUERYSIZE, SEEK_CUR);
+ }
+ }
+
+ /* Write id3v1 tag */
+ if (v1buf)
+ if ( write(fd, v1buf, v1size) != v1size)
+ goto out;
+
+ /* Truncate file (strip tags) */
+ if ((curpos = lseek(fd, 0, SEEK_CUR)) <= 0 )
+ goto out;
+ if ((err = ftruncate(fd, curpos)))
+ goto out;
+
+ /* Handle Id3v2 tag */
+ if (lseek(fd, 0, SEEK_SET) < 0)
+ goto out;
+
+ if (read(fd, tmp, ID3_TAG_QUERYSIZE) != ID3_TAG_QUERYSIZE)
+ goto out;
+
+ filev2size = id3_tag_query((id3_byte_t const *)tmp, ID3_TAG_QUERYSIZE);
+
+ if ( (filev2size == 0)
+ && (v2size == 0))
+ goto out;
+
+ if (filev2size == v2size)
+ {
+ if (lseek(fd, 0, SEEK_SET) < 0)
+ goto out;
+ if (write(fd, v2buf, v2size) != v2size)
+ goto out;
+ }else
+ {
+ /* XXX */
+ ctxsize = lseek(fd, 0, SEEK_END) - filev2size;
+ if ((ctx = g_try_malloc(ctxsize)) == NULL)
+ goto out;
+ if (lseek(fd, filev2size, SEEK_SET) < 0)
+ goto out;
+ if (read(fd, ctx, ctxsize) != ctxsize)
+ goto out;
+ if (lseek(fd, 0, SEEK_SET) < 0)
+ goto out;
+ if (v2buf)
+ write(fd, v2buf, v2size);
+
+ if (write(fd, ctx, ctxsize) != ctxsize)
+ {
+ g_print("OOPS\n");
+ goto out;
+ }
+
+ if ((curpos = lseek(fd, 0, SEEK_CUR)) <= 0 )
+ goto out;
+ if ((err = ftruncate(fd, curpos)))
+ goto out;
+ }
+
+ err = 0;
+out:
+ g_free(ctx);
+ lseek(fd, 0, SEEK_SET);
+ close(fd);
+ g_free(v1buf);
+ g_free(v2buf);
+ return err;
+}
+
+#endif /* ENABLE_MP3 */
diff --git a/src/libapetag/COPYING.LGPL b/src/libapetag/COPYING.LGPL
new file mode 100755
index 0000000..cf9b6b9
--- /dev/null
+++ b/src/libapetag/COPYING.LGPL
@@ -0,0 +1,510 @@
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations
+below.
+
+ When we speak of free software, we are referring to freedom of use,
+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 and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+^L
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it
+becomes a de-facto standard. To achieve this, non-free programs must
+be allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+^L
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, 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 library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete 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 distribute a copy of this License along with the
+Library.
+
+ 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 Library or any portion
+of it, thus forming a work based on the Library, 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) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+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 Library, 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 Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+^L
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you 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.
+
+ If distribution of 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 satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+^L
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at least
+ three years, to give the same user the materials specified in
+ Subsection 6a, above, for a charge no more than the cost of
+ performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be 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.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+^L
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library 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.
+
+ 9. 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 Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+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 with
+this License.
+^L
+ 11. 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 Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library 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 Library.
+
+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.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library 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.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser 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 Library
+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 Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+^L
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+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
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "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
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. 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 LIBRARY 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
+LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+^L
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms
+of the ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library.
+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.
+
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or
+your school, if any, to sign a "copyright disclaimer" for the library,
+if necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James
+ Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
+
+
diff --git a/src/libapetag/Makefile.am b/src/libapetag/Makefile.am
new file mode 100755
index 0000000..61b0f9c
--- /dev/null
+++ b/src/libapetag/Makefile.am
@@ -0,0 +1,22 @@
+CFLAGS = @CFLAGS@ @GTK_CFLAGS@
+
+noinst_LIBRARIES = libapetag.a
+
+INCLUDES = -DLOCALE=\"$(localedir)\"
+
+
+libapetag_a_SOURCES = \
+ apetaglib.c \
+ apetaglib.h \
+ is_tag.c \
+ is_tag.h \
+ info_mac.c \
+ info_mac.h \
+ info_mpc.c \
+ info_mpc.h
+
+EXTRA_DIST = \
+ COPYING.LGPL \
+ README.apetag \
+ id3v2_read.c \
+ id3v2_read.h
diff --git a/src/libapetag/Makefile.in b/src/libapetag/Makefile.in
new file mode 100644
index 0000000..68858ad
--- /dev/null
+++ b/src/libapetag/Makefile.in
@@ -0,0 +1,471 @@
+# Makefile.in generated by automake 1.9.6 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005 Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+top_builddir = ../..
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+INSTALL = @INSTALL@
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = src/libapetag
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/configure.in
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+LIBRARIES = $(noinst_LIBRARIES)
+ARFLAGS = cru
+libapetag_a_AR = $(AR) $(ARFLAGS)
+libapetag_a_LIBADD =
+am_libapetag_a_OBJECTS = apetaglib.$(OBJEXT) is_tag.$(OBJEXT) \
+ info_mac.$(OBJEXT) info_mpc.$(OBJEXT)
+libapetag_a_OBJECTS = $(am_libapetag_a_OBJECTS)
+DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(LIBTOOL) --tag=CC --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+SOURCES = $(libapetag_a_SOURCES)
+DIST_SOURCES = $(libapetag_a_SOURCES)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMDEP_FALSE = @AMDEP_FALSE@
+AMDEP_TRUE = @AMDEP_TRUE@
+AMTAR = @AMTAR@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@ @GTK_CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+ECHO = @ECHO@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+F77 = @F77@
+FFLAGS = @FFLAGS@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GOBJECT_QUERY = @GOBJECT_QUERY@
+GTK_CFLAGS = @GTK_CFLAGS@
+GTK_LIBS = @GTK_LIBS@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAINTAINER_MODE_FALSE = @MAINTAINER_MODE_FALSE@
+MAINTAINER_MODE_TRUE = @MAINTAINER_MODE_TRUE@
+MAKEINFO = @MAKEINFO@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MSGFMT = @MSGFMT@
+OBJEXT = @OBJEXT@
+OGG_CFLAGS = @OGG_CFLAGS@
+OGG_LIBS = @OGG_LIBS@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PKG_CONFIG = @PKG_CONFIG@
+POFILES = @POFILES@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+RANLIB = @RANLIB@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+VORBIS_CFLAGS = @VORBIS_CFLAGS@
+VORBIS_LIBS = @VORBIS_LIBS@
+WAVPACK_CFLAGS = @WAVPACK_CFLAGS@
+WAVPACK_LIBS = @WAVPACK_LIBS@
+XGETTEXT = @XGETTEXT@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_F77 = @ac_ct_F77@
+ac_ct_RANLIB = @ac_ct_RANLIB@
+ac_ct_STRIP = @ac_ct_STRIP@
+ac_pt_PKG_CONFIG = @ac_pt_PKG_CONFIG@
+am__fastdepCC_FALSE = @am__fastdepCC_FALSE@
+am__fastdepCC_TRUE = @am__fastdepCC_TRUE@
+am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@
+am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+datadir = @datadir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+noinst_LIBRARIES = libapetag.a
+INCLUDES = -DLOCALE=\"$(localedir)\"
+libapetag_a_SOURCES = \
+ apetaglib.c \
+ apetaglib.h \
+ is_tag.c \
+ is_tag.h \
+ info_mac.c \
+ info_mac.h \
+ info_mpc.c \
+ info_mpc.h
+
+EXTRA_DIST = \
+ COPYING.LGPL \
+ README.apetag \
+ id3v2_read.c \
+ id3v2_read.h
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
+ && exit 0; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/libapetag/Makefile'; \
+ cd $(top_srcdir) && \
+ $(AUTOMAKE) --foreign src/libapetag/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+clean-noinstLIBRARIES:
+ -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES)
+libapetag.a: $(libapetag_a_OBJECTS) $(libapetag_a_DEPENDENCIES)
+ -rm -f libapetag.a
+ $(libapetag_a_AR) libapetag.a $(libapetag_a_OBJECTS) $(libapetag_a_LIBADD)
+ $(RANLIB) libapetag.a
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/apetaglib.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/info_mac.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/info_mpc.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/is_tag.Po@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ if $(LTCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Plo"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+distclean-libtool:
+ -rm -f libtool
+uninstall-info-am:
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$tags $$unique; \
+ fi
+ctags: CTAGS
+CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ test -z "$(CTAGS_ARGS)$$tags$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$tags $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && cd $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) $$here
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \
+ list='$(DISTFILES)'; for file in $$list; do \
+ case $$file in \
+ $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \
+ $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \
+ esac; \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test "$$dir" != "$$file" && test "$$dir" != "."; then \
+ dir="/$$dir"; \
+ $(mkdir_p) "$(distdir)$$dir"; \
+ else \
+ dir=''; \
+ fi; \
+ if test -d $$d/$$file; then \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
+ fi; \
+ cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
+ else \
+ test -f $(distdir)/$$file \
+ || cp -p $$d/$$file $(distdir)/$$file \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LIBRARIES)
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-noinstLIBRARIES \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-libtool distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-exec-am:
+
+install-info: install-info-am
+
+install-man:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-info-am
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
+ clean-libtool clean-noinstLIBRARIES ctags distclean \
+ distclean-compile distclean-generic distclean-libtool \
+ distclean-tags distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am install-exec \
+ install-exec-am install-info install-info-am install-man \
+ install-strip installcheck installcheck-am installdirs \
+ maintainer-clean maintainer-clean-generic mostlyclean \
+ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \
+ pdf pdf-am ps ps-am tags uninstall uninstall-am \
+ uninstall-info-am
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/src/libapetag/README.apetag b/src/libapetag/README.apetag
new file mode 100755
index 0000000..9b3d311
--- /dev/null
+++ b/src/libapetag/README.apetag
@@ -0,0 +1,12 @@
+"apetaglib (in source) easytag" editon 0.5-pre1
+
+For current source mail to [artii <at> o2 <dot> pl]
+
+If you develop some nice patches or found some bugs
+please send me info ;-)
+
+All documentation fixes please send too (check all *.h files)
+
+this will be on dual licences GPL/LGPL (propably)
+
+now is LGPL
diff --git a/src/libapetag/apetaglib.c b/src/libapetag/apetaglib.c
new file mode 100755
index 0000000..1f5012e
--- /dev/null
+++ b/src/libapetag/apetaglib.c
@@ -0,0 +1,1065 @@
+/********************************************************************
+*
+* Copyright (c) 2002 Artur Polaczynski (Ar't) All rights reserved.
+* <artii@o2.pl> LGPL-2.1
+* $ArtId: apetaglib.c,v 1.44 2003/04/16 21:06:27 art Exp $
+********************************************************************/
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1
+ * 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <stdarg.h>
+#include <string.h>
+#include <limits.h>
+#include <assert.h>
+#include <math.h>
+#ifndef __BORLANDC__
+# include <unistd.h>
+#endif
+#include "apetaglib.h"
+#include "../genres.h"
+
+#include "is_tag.h"
+#ifdef ID3V2_READ
+# include "id3v2_read.h"
+#endif
+
+/* LOCAL STRUCTURES */
+
+/**
+ \struct _apetag_footer
+ \brief structure of APETAGEXT footer or/and header tag
+*/
+struct _apetag_footer
+{
+ unsigned char id[8]; /**< magic should equal 'APETAGEX' */
+ unsigned char version[4]; /**< version 1000 (v1.0) or 2000 (v 2.0) */
+ unsigned char length[4]; /**< the complete size of the tag, including footer, but no header for v2.0 */
+ unsigned char tagCount[4]; /**< the number of fields in the tag */
+ unsigned char flags[4]; /**< the tag flags (none currently defined for v 1.0) */
+ unsigned char reserved[8]; /**< reserved for later use */
+};
+
+/**
+ \struct _ape_mem_cnt
+ \brief internal structure for apetag
+*/
+struct _ape_mem_cnt
+{
+ struct tag **tag;
+ int countTag;
+ int memTagAlloc; // for mem container;
+ char *filename; // for info
+ struct _apetag_footer ape_header;
+ struct _apetag_footer ape_footer;
+ int currentPosition;
+};
+
+/* *
+ \struct _id3v1Tag
+ \brief for id3v1 tag
+*/
+struct _id3v1Tag
+{
+ char magic[3]; // `TAG`
+ char title[30];
+ char artist[30];
+ char album[30];
+ char year[4];
+ char comment[30]; // if ([28]==0 and [29]!=0) track = [29]
+ unsigned char genre;
+};
+
+/* LOCAL FUNCTION prototypes */
+unsigned long
+ape2long (unsigned char *p);
+void
+long2ape (unsigned char *p, const unsigned long value);
+struct tag *
+libapetag_maloc_cont_int (apetag *mem_cnt, struct tag *mTag);
+int
+libapetag_maloc_cont_text (apetag *mem_cnt, unsigned long flags,
+ long sizeName, char *name, long sizeValue, char *value);
+int
+libapetag_maloc_cont (apetag *mem_cnt, unsigned long flags,
+ long sizeName, char *name, long sizeValue, char *value);
+static int
+libapetag_qsort (struct tag **a, struct tag **b);
+int
+make_id3v1_tag(apetag *mem_cnt, struct _id3v1Tag *m);
+
+
+unsigned long
+ape2long (unsigned char *p)
+{
+ return (((unsigned long) p[0] << 0) |
+ ((unsigned long) p[1] << 8) |
+ ((unsigned long) p[2] << 16) |
+ ((unsigned long) p[3] << 24) );
+}
+
+void
+long2ape (unsigned char *p, const unsigned long value)
+{
+ p[0] = (unsigned char) (value >> 0);
+ p[1] = (unsigned char) (value >> 8);
+ p[2] = (unsigned char) (value >> 16);
+ p[3] = (unsigned char) (value >> 24);
+}
+
+
+/*
+ PL: funkcja troszczaca sie o odpowiedni± ilosc zalokowanej pamieci dla tablicy
+ PL: %mTag% przy okazji alokuje z wyprzedzeniem troche wiecej pamieci [mniej %realoc%]
+ PL: zwraca %mTag[]%
+ :NON_USER:!!!
+ */
+#define LIBAPETAG_MEM_ALLOC_AHEAD 16 /* 15 it's good for normal #of tag, Aver 4-8 */
+struct tag *
+libapetag_maloc_cont_int (apetag *mem_cnt, struct tag *mTag)
+{
+ struct tag **tag_tmp = mem_cnt->tag;
+
+ if (mem_cnt->memTagAlloc == 0) { /* init */
+ mem_cnt->tag = (struct tag **)
+ malloc (((sizeof (struct tag **)) * (LIBAPETAG_MEM_ALLOC_AHEAD)));
+ mem_cnt->memTagAlloc = LIBAPETAG_MEM_ALLOC_AHEAD;
+ mem_cnt->countTag = 0;
+ if (mem_cnt->tag == NULL) {
+ mem_cnt->memTagAlloc = mem_cnt->countTag = 0;
+ PRINT_ERR ( "ERROR->libapetag->libapetag_maloc_cont_int:malloc\n");
+ return NULL;
+ }
+ }
+
+ if ((mem_cnt->memTagAlloc) <= (mem_cnt->countTag + 1)) {
+ mem_cnt->tag = (struct tag **) realloc (mem_cnt->tag, ((sizeof (struct tag **)) *
+ (mem_cnt->memTagAlloc + LIBAPETAG_MEM_ALLOC_AHEAD)));
+ mem_cnt->memTagAlloc += LIBAPETAG_MEM_ALLOC_AHEAD;
+ }
+
+ if (mem_cnt->tag == NULL) {
+ int n;
+
+ PRINT_ERR ( "ERROR->libapetag->libapetag_maloc_cont_int:malloc\n");
+ /* free old all */
+ for (n = mem_cnt->countTag-1; n >= 0; n--) {
+ free (tag_tmp[n]->value);
+ free (tag_tmp[n]->name);
+ free (tag_tmp[n]);
+ }
+ free (tag_tmp);
+ mem_cnt->memTagAlloc = mem_cnt->countTag = 0;
+ return NULL;
+ }
+
+ mem_cnt->tag[mem_cnt->countTag] = mTag;
+ mem_cnt->countTag++;
+ return mTag;
+
+}
+#undef LIBAPETAG_MEM_ALLOC_AHEAD
+
+
+/*
+ PL: alocuje pamiec dla %mTag% przypisuje odpowiednio wartosci
+ PL: dodaje %\0% do stringów [na wszelki wypadek]
+ PL: nie dopisuje takich samych
+ PL: wszystkie sizy maja byc bez \0 (jak bedzie to doliczy jeszcze jeden)
+ :NON_USER:!!!
+ */
+int
+libapetag_maloc_cont (apetag *mem_cnt, unsigned long flags,
+ long sizeName, char *name, long sizeValue, char *value)
+{
+ struct tag *mTag;
+ // TODO:: zadbac o to zeby tu czyscilo istniejace tagi jesli value=NULL
+ if (!sizeName || !sizeValue)
+ return ATL_BADARG;
+
+ if (apefrm_getstr (mem_cnt, name) == NULL) {
+ mTag = (struct tag *) malloc (sizeof (struct tag));
+
+ if (mTag == NULL)
+ return ATL_MALOC;
+
+ mTag->value = (char *) malloc (sizeValue + 1);
+ if (mTag->value==NULL) {
+ free (mTag);
+ return ATL_MALOC;
+ }
+
+ mTag->name = (char *) malloc (sizeName + 1);
+ if (mTag->name==NULL) {
+ free (mTag->value);
+ free (mTag);
+ return ATL_MALOC;
+ }
+
+ memcpy (mTag->value, value, sizeValue);
+ memcpy (mTag->name, name, sizeName);
+ mTag->value[sizeValue] = '\0';
+ mTag->name[sizeName] = '\0';
+ mTag->sizeName = sizeName;
+ mTag->sizeValue = sizeValue;
+ mTag->flags = flags;
+
+ if (libapetag_maloc_cont_int (mem_cnt, mTag)==NULL) {
+ PRINT_ERR(">apetaglib>libapetag_maloc_cont>> int==NULL");
+ return ATL_MALOC;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ PL: jezeli nie istnieje to dodaje taga, pomija ostatnie biale znaki
+ PL: pomija jesli pusty
+ PL: ! zmienia tekst wej¶ciowy
+ :NON_USER:!!!
+*/
+int
+libapetag_maloc_cont_text (apetag *mem_cnt, unsigned long flags,
+ long sizeName, char *name, long sizeValue,
+ char *value)
+{
+ int n = sizeValue;
+
+ if (value != NULL && value[0] != '\0' && apefrm_getstr (mem_cnt, name) == NULL) {
+ while (value[--n] == ' ' || value[n] == '\0' || value[n] == '\n') {
+ value[n] = '\0';
+ }
+ return libapetag_maloc_cont (mem_cnt, flags, sizeName, name, n + 1, value);
+ }
+
+ return 0;
+}
+
+
+/*
+ PL: dodaje taga do istniejeacych o ustawionych wartosciach %flag% %name% i %value%
+ PL: wylicza odpowiednio rozmiary przy pomocy strlen!!
+ PL: wraper na %libapetag_maloc_cont%
+ PL: wszystko kopiuje sobie do pamieci
+ PL: musi byc juz w UTF-8 dla v2
+ PL: Nadpisuje istniejace
+ */
+/**
+ \brief Add text frame
+
+ add text frame/field to object apetag (if exist then overwrite)
+
+ \param mem_cnt object #apetag
+ \param flags flags stored in frame
+ \param name name of frame
+ \param value value of frame
+ \return 0 - OK else check #atl_return
+*/
+int
+apefrm_add (apetag *mem_cnt, unsigned long flags, char *name,
+ char *value)
+{
+ apefrm_remove_real (mem_cnt, name);
+ return libapetag_maloc_cont (mem_cnt, flags, strlen (name), name, strlen (value), value);
+}
+
+/*
+ PL: Prosty wraperek na maloc_cont - do zapisu binarnych
+*/
+/**
+ \brief add binary frame
+
+ add binary frame/field to object apetag (if exist then overwrite)
+
+ \param mem_cnt object #apetag
+ \param flags flags stored in frame
+ \param sizeName size of name
+ \param name name of frame
+ \param sizeValue size of value
+ \param value value of frame
+ \return 0 - OK else check #atl_return
+*/
+int
+apefrm_add_bin (apetag *mem_cnt, unsigned long flags,
+ long sizeName, char *name,
+ long sizeValue, char *value)
+{
+ apefrm_remove_real (mem_cnt, name);
+ return libapetag_maloc_cont (mem_cnt, flags, sizeName, name, sizeValue, value);
+}
+
+/*
+ PL: jak %apefrm_add ()% z tym ze nie nadpisuje istniejacych
+*/
+/**
+ \brief add frame if other (the same name) no exist
+
+ if exist "name" in ape_mem then do nothing else add frame/field to ape_mem
+
+ \param mem_cnt object #apetag
+ \param flags flags stored in frame
+ \param name name of frame
+ \param value value of frame
+ \return 0 - OK else check #atl_return
+*/
+int
+apefrm_add_noreplace (apetag *mem_cnt, unsigned long flags,
+ char *name, char *value)
+{
+ if ( apefrm_getstr (mem_cnt, name) == NULL )
+ return apefrm_add (mem_cnt, flags, name, value);
+
+ return 0;
+}
+
+/*
+ PL: wyszukuje taga o nazwie %name% i zwraca structure %struct tag%
+ PL: %APE_TAG_LIB_FIRST% i %APE_TAG_LIB_NEXT% to ulatwienie dla
+ PL: przesukiwania wszystkich istniejacych tagów
+ PL: %APE_TAG_LIB_FIRST% ustawia znacznik na pierwszy tag [0] i zwraca jego warto¶æ
+ PL: %APE_TAG_LIB_NEXT% podaje nastepny tag i zwieksza znacznik, po ostatnim funkcja zwraca %NULL%
+ PL: UWAGA!!! zwraca pointer do wewnetrznej struktury
+ PL: niczego nie zmieniac i nie free()-jowac skopiowac i dopiero
+ PL: zwraca teksty w UTF-8
+ */
+/**
+ \brief search in apetag for name and return tag
+
+ 2 special names \a APE_TAG_LIB_FIRST and \a APE_TAG_LIB_NEXT.
+ FIRST return first frame and set counter to 1
+ NEXT return ++counter frame
+\code
+for ((framka = apefrm_get(ape, APE_TAG_LIB_FIRST)); framka!=NULL;) {
+ do_something();
+ framka = apefrm_get(ape, APE_TAG_LIB_NEXT);
+}
+\endcode
+ return NULL if no more frame exist
+
+ \param mem_cnt object #apetag
+ \param name frame name for search
+ \return pointer to struct tag if name exist or NULL if don't
+ \warning don't change anything in this struct make copy and work
+*/
+struct tag *
+apefrm_get (apetag *mem_cnt, char *name)
+{
+ int n;
+ struct tag **mTag;
+
+ mTag = (mem_cnt->tag);
+
+ if (mem_cnt->countTag == 0)
+ return NULL;
+
+ if (strcmp (name, APE_TAG_LIB_FIRST) == 0) {
+ mem_cnt->currentPosition = 0;
+ return (mTag[mem_cnt->currentPosition++]);
+ }
+
+ if (strcmp (name, APE_TAG_LIB_NEXT) == 0) {
+ if (mem_cnt->currentPosition >= mem_cnt->countTag)
+ return NULL;
+ return (mTag[mem_cnt->currentPosition++]);
+ }
+
+ for (n = 0; (mem_cnt->countTag) > n; n++) {
+ if (strcasecmp (mTag[n]->name, name) == 0) {
+ return (mTag[n]);
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ PL:zwraca %mem_cnt->tag[x]->value% o ile znajdzie nazwe %name% taga
+ PL: prosty wraper na %apefrm_get %
+ PL: UWAGA zwraca pointer z wewnetrznych struktur niczego bezposrednio nie zmieniac
+ PL: i nie free()-jowac bo sie rozsypie
+ PL: zwraca tekst w UTF-8
+ */
+/**
+ \brief search in apetag for name and return string
+
+ \param mem_cnt object #apetag
+ \param name frame name for search
+ \return pointer to value of frame if name exist or NULL if don't
+ \warning don't change that string make copy before any action
+ \todo check if frame type isn't binary
+*/
+char *
+apefrm_getstr (apetag *mem_cnt, char *name)
+{
+ struct tag *mTag;
+
+ mTag = apefrm_get (mem_cnt, name);
+
+ if (mTag == NULL)
+ return NULL;
+
+ return (mTag->value);
+}
+
+/*
+ PL: usuwanie taga o nazwie zdefiniowanej w %name%
+ PL:lub wszystkich jezeli %name%=%APE_TAG_LIB_DEL_ALL%
+ PL:UWAGA mozna to napisac inaczej (sprawdzanie czy %name% OR %%special%) ale to w v1.0
+ */
+/**
+ \brief remove frame from memory
+
+ (real) remove frame from ape_mem.
+ Check #apefrm_remove for more info
+
+ \param mem_cnt object #apetag
+ \param name frame name for search and remove
+*/
+void
+apefrm_remove_real (apetag *mem_cnt, char *name)
+{
+ int n;
+ struct tag **mTag;
+
+ mTag = (mem_cnt->tag);
+
+ /* Delete all */
+ if (strcmp (name, APE_TAG_LIB_DEL_ALL) == 0) {
+ for (n = mem_cnt->countTag-1; n >= 0; n--) {
+ free (mTag[n]->name);
+ free (mTag[n]->value);
+ free (mTag[n]);
+ --mem_cnt->countTag;
+ }
+ return;
+ }
+ /* Delete only one */
+ for (n = mem_cnt->countTag-1; n >= 0; n--) {
+ if (strcasecmp (mTag[n]->name, name) == 0) {
+ free (mTag[n]->name);
+ free (mTag[n]->value);
+ free (mTag[n]);
+ mTag[n] = mTag[mem_cnt->countTag];
+ --mem_cnt->countTag;
+ /* !no return; search for all */
+ }
+ }
+
+ return;
+}
+/*
+ PL: tak jakby frejuje framke oznacza do kasacji jednak tego nie robi
+ PL: mechanizm ten g³ownie jest wykorzystywany do wczytania innych tagów
+ PL: poza wczesniej zkasowanymi aby to usun±c uzyj apefrm_remove_real
+*/
+/**
+ \brief set frame to remove
+
+ Create fake name and empty value (and set don't save flag).
+ If you use apefrm_add_norepleace then you don't change
+ this not_save_flag.
+ Only apefrm_add overwrite this.
+ [it's for id3v1 but you may using this for remove frames]
+
+ \param mem_cnt object #apetag
+ \param name frame name for search and remove
+*/
+void
+apefrm_remove (apetag *mem_cnt, char *name)
+{
+ int n;
+ struct tag **mTag;
+
+ apefrm_add (mem_cnt, 0 , name, "delete me");
+
+ mTag = (mem_cnt->tag);
+
+ for (n = 0; (mem_cnt->countTag) > n; n++) {
+ if (strcasecmp (mTag[n]->name, name) == 0) {
+ mTag[n]->sizeValue=0;
+ return;
+ }
+ }
+
+ return;
+}
+
+/*
+ PL:Wypisuje na ekran wszystko to co potrzebne do debugu
+ :NON_USER:!!!
+*/
+/**
+ debug function print all tags exclude bin (print only size for bin)
+*/
+void
+libapetag_print_mem_cnt (apetag *mem_cnt)
+{
+ int n;
+ struct tag **mTag;
+
+ mTag = (mem_cnt->tag);
+ for (n = 0; (mem_cnt->countTag) > n; n++) {
+ if ( (mTag[n]->flags & ~ITEM_TEXT) == 0 ||
+ (mTag[n]->flags & ~ITEM_LINK) == 0 ) {
+ printf (">apetaglib>PRINT>>F=%li SN=%li SV=%li N[%s] V[%s]\n",
+ mTag[n]->flags,
+ (long) mTag[n]->sizeName, (long) mTag[n]->sizeValue,
+ mTag[n]->name, mTag[n]->value);
+ } else {
+ printf (">apetaglib>PRINT>>F=%li SN=%li SV=%li N[%s] V=BINARY\n",
+ mTag[n]->flags,
+ (long) mTag[n]->sizeName, (long) mTag[n]->sizeValue,
+ mTag[n]->name);
+ }
+ }
+
+ return;
+}
+
+/*
+ PL: alokuje pamiec dla glównej struktury %struct ape_mem_cnt%
+ PL: i zeruje wszystko co trzeba
+ PL: z jakiegos powodu (mojej niewiedzy) memset nie dziala
+ PL: a w sumie dziala czyszczac troche za duzo
+*/
+/**
+ \brief initialise new object #apetag and return
+ \return new initialised object #apetag
+*/
+apetag *
+apetag_init (void)
+{
+ apetag * mem_cnt;
+
+ mem_cnt = (apetag *) malloc (sizeof (apetag));
+ if (mem_cnt == NULL) {
+ PRINT_ERR ("ERROR->libapetag->apetag_init:malloc\n");
+ return NULL;
+ }
+ mem_cnt->memTagAlloc = 0;
+ mem_cnt->countTag = 0;
+ mem_cnt->filename = NULL;
+ mem_cnt->currentPosition = 0;
+ mem_cnt->tag = NULL;
+
+ return mem_cnt;
+}
+
+/*
+ PL: Czysci z sila wodospadu wszystko co zostalo do czyszczenia
+ PL: z %struct ape_mem_cnt% wlacznie, wcze¶niej to nie by³o jasne
+*/
+/**
+ \brief free all work
+ \param mem_cnt object #apetag
+*/
+void
+apetag_free (apetag *mem_cnt)
+{
+ int n;
+
+ for (n = mem_cnt->countTag-1; n >= 0; n--)
+ {
+ free (mem_cnt->tag[n]->value);
+ free (mem_cnt->tag[n]->name);
+ free (mem_cnt->tag[n]);
+ }
+ free (mem_cnt->tag);
+ free (mem_cnt);
+ mem_cnt = NULL;
+
+ return;
+
+}
+
+
+/**
+ \brief read id3v1 and add frames
+
+ read id3v1 and add frames to ape_mem.
+ Using #apefrm_add_norepleace
+
+ \param mem_cnt object #apetag
+ \param fp file pointer
+ \return 0 - OK else check #atl_return
+*/
+int
+readtag_id3v1_fp (apetag *mem_cnt, FILE * fp)
+{
+ struct _id3v1Tag m;
+
+ if (!is_id3v1(fp))
+ return 0; /* TODO:: 0 or no_id3v1*/
+
+ fseek(fp, -128, SEEK_END);
+ if (sizeof (struct _id3v1Tag)!=fread(&m, 1, sizeof (struct _id3v1Tag), fp)){
+ PRINT_ERR( "ERROR->libapetag->readtag_id3v1_fp:fread\n");
+ return ATL_FREAD;
+ }
+
+ libapetag_maloc_cont_text(mem_cnt, 0, 5, "Title", 30, m.title);
+ libapetag_maloc_cont_text(mem_cnt, 0, 6, "Artist", 30, m.artist);
+ libapetag_maloc_cont_text(mem_cnt, 0, 5, "Album", 30, m.album);
+ libapetag_maloc_cont_text(mem_cnt, 0, 4, "Year", 4, m.year);
+ if (m.comment[28] == 0 && m.comment[29] != 0) {
+ char track[20];
+ snprintf(track, 19, "%i", m.comment[29]);
+ libapetag_maloc_cont_text(mem_cnt, 0, 5, "Track", strlen(track), track);
+ libapetag_maloc_cont_text(mem_cnt, 0, 7, "Comment", 28, m.comment);
+ } else {
+ libapetag_maloc_cont_text(mem_cnt, 0, 7, "Comment", 30, m.comment);
+ }
+ libapetag_maloc_cont_text(mem_cnt, 0, 5, "Genre",
+ strlen(genre_no(m.genre)), genre_no(m.genre));
+
+ return 0;
+}
+
+/*
+ PL: wczytuje odpowiednie fra(mk)gi do pamieci w razie koniecznosci przyciecia
+ PL: dodaje "..." na koniec
+ PL: TODO genre
+
+ PL: macro COMPUTE_ID3V1_TAG
+*/
+#define COMPUTE_ID3V1_TAG(FramkA, TagNamE, SizE, TagValuE) \
+ FramkA = apefrm_get(mem_cnt, TagNamE); \
+ if (FramkA != NULL) { \
+ memcpy (TagValuE, FramkA->value, \
+ ((FramkA->sizeValue) > SizE) ? SizE : FramkA->sizeValue ); \
+ if ((FramkA->sizeValue) > SizE) { \
+ TagValuE[SizE-1]='.'; TagValuE[SizE-2]='.'; TagValuE[SizE-3]='.'; \
+ } \
+ }
+
+int
+make_id3v1_tag(apetag *mem_cnt, struct _id3v1Tag *m)
+{
+ struct tag * framka;
+
+ if (m == NULL)
+ return ATL_BADARG;
+
+ memset(m, '\0', sizeof(struct _id3v1Tag));
+
+ memcpy (m->magic,"TAG",3);
+ COMPUTE_ID3V1_TAG(framka, "Title", 30, m->title);
+ COMPUTE_ID3V1_TAG(framka, "Artist", 30, m->artist);
+ COMPUTE_ID3V1_TAG(framka, "Album", 30, m->album);
+ COMPUTE_ID3V1_TAG(framka, "Year", 4, m->year);
+
+ if ((framka=apefrm_get(mem_cnt, "Track"))!=NULL) {
+ m->comment[29]=(unsigned char) atoi(framka->value);
+ m->comment[28]='\0';
+ COMPUTE_ID3V1_TAG(framka, "Comment", 28, m->comment);
+ } else {
+ COMPUTE_ID3V1_TAG(framka, "Comment", 30, m->comment);
+ }
+
+ return 0;
+}
+
+/*
+ PL: silnik tego liba
+ PL: %filename% jest w tej chwili tylko dla id3v2 f..k
+ PL: %ape_mem_cnt% moze byc nie zainicjalizowany ale wtedy musi byc = NULL
+*/
+/**
+ \brief read file and add frames
+
+ \param mem_cnt object #apetag
+ \param filename
+ \param fp
+ \param flag
+ \return 0 - OK else check #atl_return
+*/
+int
+apetag_read_fp(apetag *mem_cnt, FILE * fp, char *filename, int flag)
+{
+ int id3v1 = 0;
+ int apeTag2 = 0;
+ unsigned char *buff;
+ struct _apetag_footer ape_footer;
+ size_t savedFilePosition, buffLength;
+
+ unsigned char *end;
+ unsigned long tagCount;
+ unsigned char *p;
+
+ savedFilePosition = ftell(fp);
+
+ id3v1 = is_id3v1(fp);
+
+ if (mem_cnt == NULL) {
+ PRINT_ERR( ">apetaglib>READ_FP>FATAL>apetag_init()\n");
+ fseek(fp, savedFilePosition, SEEK_SET);
+ return ATL_NOINIT;
+ }
+
+ fseek(fp, id3v1 ? -128 - sizeof (ape_footer) : -sizeof (ape_footer), SEEK_END);
+ if (sizeof (ape_footer) != fread(&ape_footer, 1, sizeof (ape_footer), fp)){
+ PRINT_ERR( "ERROR->libapetag->apetag_read_fp:fread1\n");
+ fseek(fp, savedFilePosition, SEEK_SET);
+ return ATL_FREAD;
+ }
+
+ if (!(flag & DONT_READ_TAG_APE) &&
+ (memcmp(ape_footer.id, "APETAGEX", sizeof (ape_footer.id)) == 0))
+ {
+ PRINT_D9(">apetaglib>READ_FP>>%s: ver %li len %li # %li fl %lx v1=%i v2=%i ape=%i[v%i]\n",
+ filename, ape2long(ape_footer.version),
+ ape2long(ape_footer.length),
+ ape2long(ape_footer.tagCount),
+ ape2long(ape_footer.flags),
+ is_id3v1 (fp), is_id3v2 (fp), is_ape (fp), is_ape_ver (fp));
+
+ apeTag2 = ape2long(ape_footer.version);
+ buffLength = is_ape(fp) + 128;
+ buff = (unsigned char *) malloc(buffLength);
+ if (buff == NULL) {
+ PRINT_ERR( "ERROR->libapetag->apetag_read_fp:malloc\n");
+ return ATL_MALOC;
+ }
+
+ fseek(fp, id3v1 ? -ape2long(ape_footer.length) -
+ 128 : -ape2long(ape_footer.length), SEEK_END);
+ memset(buff, 0, buffLength);
+ if (ape2long(ape_footer.length) != fread(buff, 1, ape2long(ape_footer.length), fp)) {
+ PRINT_ERR( "ERROR->libapetag->apetag_read_fp:fread2\n");
+ fseek(fp, savedFilePosition, SEEK_SET);
+ free(buff);
+ return ATL_FREAD;
+ }
+
+ tagCount = ape2long(ape_footer.tagCount);
+
+ end = buff + ape2long(ape_footer.length) - sizeof (ape_footer);
+
+ for (p = buff; p < end && tagCount--;) {
+ /* 8 = sizeof( sizeValue+flags ) */
+ unsigned long flag = ape2long(p + 4);
+ unsigned long sizeValue = ape2long(p);
+ unsigned long sizeName;
+ char *name = p + 8;
+ char *value;
+
+ sizeName = strlen(p + 8);
+ value = p + sizeName + 8 + 1;
+ if (apeTag2 == 1000 && value[sizeValue - 1] == '\0') {
+ libapetag_maloc_cont(mem_cnt, flag,
+ sizeName, name,
+ sizeValue - 1, value);
+ } else {
+ libapetag_maloc_cont(mem_cnt, flag,
+ sizeName, name,
+ sizeValue, value);
+ }
+ p += (sizeName + sizeValue + 8 + 1);
+ }
+
+ free(buff);
+ } else { /* if no ape tag */
+ PRINT_D5(">apetaglib>READ_FP>>%s: v1=%i v2=%i ape=%i[v%i]\n",
+ filename, is_id3v1 (fp), is_id3v2 (fp), is_ape (fp), is_ape_ver (fp));
+ }
+
+#ifdef ID3V2_READ
+ if (!(flag & DONT_READ_TAG_ID3V2) && filename!=NULL && is_id3v2(fp)!=0) {
+ readtag_id3v2(mem_cnt, filename);
+ }
+#endif
+ if (!(flag & DONT_READ_TAG_ID3V1) && (id3v1)) {
+ readtag_id3v1_fp(mem_cnt, fp);
+ }
+
+ fseek(fp, savedFilePosition, SEEK_SET);
+ return 0;
+}
+
+/*
+ PL: wraper na apetag_read_fp
+ PL: otwiera plik wczytuje co trzeba i zamyka
+ PL: dobre do wczytywania informacji ktore sa potrzebne pozniej bez fatygi otwierania pliku
+*/
+/**
+ \brief read file and add frames
+
+ \param mem_cnt object #apetag
+ \param filename file name
+ \param flag
+ \return 0 - OK else check #atl_return
+*/
+int
+apetag_read (apetag *mem_cnt, char *filename,int flag)
+{
+ FILE *fp;
+
+ if (mem_cnt==NULL) {
+ PRINT_ERR(">apetaglib>READ>FATAL>apetag_init()\n");
+ return ATL_NOINIT;
+ }
+
+ fp = fopen (filename, "rb");
+ if (fp == NULL)
+ return ATL_FOPEN;
+
+ apetag_read_fp (mem_cnt, fp, filename,flag);
+
+ fclose (fp);
+
+ return 0;
+}
+
+/*
+ PL: Funkcja dla qsorta ze specjalnymi wyjatkami
+ PL: uzywana w apetag_save
+ :NON_USER:!!!
+*/
+static int
+libapetag_qsort (struct tag **a, struct tag **b)
+{
+ char *sorting[] = { "Artist", "Year", "Album", "Track", "Title", "Genre", NULL, NULL };
+ int n, m;
+
+ if (!a || !b || !*a || !*b) {
+ PRINT_ERR ("ERROR->libapetag->apetag_qsort:*a ||*b = NULL : FATAL PLEASE REPORT!!!\n");
+ return 0;
+ }
+ for (n = 0; sorting[n] != NULL; n++) {
+ if (strcasecmp ((*a)->name, sorting[n]) == 0)
+ break;
+ }
+ if (sorting[n] == NULL)
+ n += (*a)->sizeValue + 1; /* n = max entries of sorting + size of tag */
+
+ for (m = 0; sorting[m] != NULL; m++) {
+ if (strcasecmp ((*b)->name, sorting[m]) == 0)
+ break;
+ }
+ if (sorting[m] == NULL)
+ m += (*b)->sizeValue + 1; /* m = max entries */
+
+ if (n == m)
+ return 0;
+ if (n > m)
+ return 1;
+ else
+ return -1;
+}
+
+#ifdef USE_CHSIZE
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <io.h>
+/* on winblows we don't have truncate (and ftruncate) but have chsize() */
+void
+truncate (char *filename, size_t fileSize)
+{
+ int handle;
+
+ handle = open (filename, O_RDWR | O_CREAT,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
+ if (handle != -1) {
+ if (chsize (handle, fileSize) != 0) {
+ PRINT_ERR ("Error truncatng file\n");
+ }
+ close (handle);
+ }
+
+}
+
+#endif
+
+/*
+ PL: domyslne %flag% = APE_TAG_V2 + SAVE_NEW_OLD_APE_TAG
+*/
+/**
+ \brief save apetag to file
+
+ \param filename file name
+ \param mem_cnt object #apetag
+ \param flag flags for read/save
+ \return 0 - OK else check #atl_return
+ \warning for ape tag v 1 you must add frames in iso-1
+ for v 2 this must be in utf-8
+ \todo PL: v9 sprawdzac flagi w footer i na tej podstawie zmieniac skipBytes
+ bez domniemywania ze v2 ma zawsze oba
+
+*/
+int
+apetag_save (char *filename, apetag *mem_cnt, int flag)
+{
+ FILE *fp;
+ struct _id3v1Tag id3v1_tag;
+ int id3v1;
+ int apeTag, saveApe2;
+ int tagCount = 0;
+ int realCountTag = 0;
+ struct _apetag_footer ape_footer;
+ long skipBytes;
+ unsigned char *buff, *p;
+ struct tag **mTag;
+ size_t tagSSize = 32;
+ int n;
+ char temp[4];
+
+ if (mem_cnt==NULL) {
+ PRINT_ERR("ERROR->apetaglib>apetag_save::apetag_init()\n");
+ return ATL_NOINIT;
+ }
+
+ fp = fopen (filename, "rb+");
+ if (fp == NULL) {
+ PRINT_ERR ( "ERROR->apetaglib->apetag_save::fopen (r+)\n");
+ return ATL_FOPEN;
+ }
+
+ skipBytes = 0;
+ id3v1 = is_id3v1 (fp);
+ apeTag = is_ape (fp);
+ saveApe2 = !(flag & APE_TAG_V1); // (flag & APE_TAG_V2) ? 1 : (flag & APE_TAG_V1);
+
+ if (id3v1) {
+ fseek (fp, -128, SEEK_END);
+ fread (&id3v1_tag, 1, sizeof (struct _id3v1Tag), fp);
+ skipBytes += id3v1;
+ }
+ skipBytes += apeTag;
+
+ if (!(flag & SAVE_NEW_APE_TAG)) {
+ apetag_read_fp (mem_cnt, fp, filename, flag);
+ }
+
+ mTag = (mem_cnt->tag);
+ qsort( mTag , mem_cnt->countTag , sizeof(struct tag *),
+ (int (*)(const void *,const void *))libapetag_qsort);
+
+ for (n = 0; (mem_cnt->countTag) > n; n++) {
+ if (mTag[n]->sizeValue != 0) {
+ tagSSize += ((long) mTag[n]->sizeName + (long) mTag[n]->sizeValue);
+ tagSSize += 4 + 4 + 1 + (saveApe2 ? 0 : 1); // flag & sizeValue & \0
+ realCountTag++; // count not deleted tag (exl. not real)
+ }
+ }
+ if (!!(flag & SAVE_CREATE_ID3V1_TAG )) {
+ make_id3v1_tag(mem_cnt, &id3v1_tag);
+ tagSSize += 128;
+ }
+ //PRINT_D4 (">apetaglib>SAVE>>: size %li %i %i %i\n", tagSSize,
+ // mem_cnt->countTag, flag, saveApe2);
+ buff = (unsigned char *) malloc (tagSSize + (saveApe2 ? 32 : 0));
+ p = buff;
+
+ if (buff == NULL) {
+ PRINT_ERR ("ERROR->libapetag->apetag_save::malloc");
+ return ATL_MALOC;
+ }
+ memset (ape_footer.id, 0, sizeof (ape_footer));
+ memcpy (ape_footer.id, "APETAGEX", sizeof (ape_footer.id));
+ long2ape (ape_footer.flags, 0l);
+ if (!!(flag & SAVE_CREATE_ID3V1_TAG ))
+ long2ape (ape_footer.length, tagSSize-128);
+ else
+ long2ape (ape_footer.length, tagSSize);
+ //long2ape(ape_footer.tagCount, mem_cnt->countTag);
+ long2ape(ape_footer.tagCount, realCountTag);
+ long2ape (ape_footer.version, (saveApe2 ? 2000 : 1000));
+ if (saveApe2) {
+ long2ape (ape_footer.flags, HEADER_THIS_IS + HEADER_IS + FOOTER_IS);
+ memcpy (p, ape_footer.id, sizeof (ape_footer));
+ p += sizeof (ape_footer);
+ }
+
+ mTag = (mem_cnt->tag);
+ for (n = 0; (mem_cnt->countTag) > n; n++) {
+ if (saveApe2) {
+ long2ape (temp, mTag[n]->sizeValue);
+ } else {
+ /* TODO:convert UTF8 to ASCII mTag[n]->value */
+ long2ape (temp, (mTag[n]->sizeValue) + 1);
+ }
+
+ if (mTag[n]->sizeValue != 0) {
+ memcpy (p, temp, 4);
+ p += 4;
+ long2ape (temp, (saveApe2!=0) ? mTag[n]->flags : 0l );
+ memcpy (p, temp, 4);
+ p += 4;
+
+ memcpy (p, mTag[n]->name, mTag[n]->sizeName);
+ p += mTag[n]->sizeName;
+ memcpy (p, "\0", 1);
+ p++;
+ memcpy (p, mTag[n]->value, mTag[n]->sizeValue);
+ p += mTag[n]->sizeValue;
+
+ if (!saveApe2) {
+ memcpy (p, "\0", 1);
+ p++;
+ }
+ tagCount++;
+ }
+ } /* for */
+
+ if (saveApe2)
+ long2ape (ape_footer.flags, FOOTER_THIS_IS + FOOTER_IS + HEADER_IS);
+
+ memcpy (p, ape_footer.id, sizeof (ape_footer));
+ p += sizeof (ape_footer);
+
+ if (!!(flag & SAVE_CREATE_ID3V1_TAG )) {
+ memcpy (p, &id3v1_tag , sizeof (struct _id3v1Tag));
+ }
+
+ /* write tag to file and truncate */
+ if (!(flag & SAVE_FAKE_SAVE)) {
+ size_t fileSize;
+ size_t newFileSize;
+ size_t writedBytes;
+
+ fseek (fp, 0, SEEK_END);
+ fileSize = ftell (fp);
+ fseek (fp, fileSize - skipBytes, SEEK_SET);
+ if (tagCount != 0) {
+ newFileSize = (fileSize - skipBytes + tagSSize + (saveApe2 ? 32 : 0));
+ writedBytes = fwrite (buff, 1, tagSSize + (saveApe2 ? 32 : 0), fp);
+ if (writedBytes != tagSSize + (saveApe2 ? 32 : 0)) {
+ PRINT_ERR ("FATAL_ERROR->libapetag->apetag_save::fwrite [data lost]");
+ fclose (fp);
+ free (buff);
+ return ATL_FWRITE;
+ }
+ fseek (fp, newFileSize, SEEK_SET);
+ PRINT_D4 (">apetaglib>SAVE>> write:%i == tag:%i file: %i->%i\n",
+ writedBytes, tagSSize + (saveApe2 ? 32 : 0), fileSize, newFileSize);
+ } else {
+ newFileSize = (fileSize - skipBytes);
+ }
+ fflush (fp);
+ fclose (fp);
+ /* ftruncate don't work */
+ truncate (filename, newFileSize);
+ } else { /* !!SAVE_FAKE_SAVE */
+ libapetag_print_mem_cnt (mem_cnt);
+ }
+ free (buff);
+
+ return 0;
+}
diff --git a/src/libapetag/apetaglib.h b/src/libapetag/apetaglib.h
new file mode 100755
index 0000000..15ec6e9
--- /dev/null
+++ b/src/libapetag/apetaglib.h
@@ -0,0 +1,277 @@
+/********************************************************************
+*
+* Copyright (c) 2002 Artur Polaczynski (Ar't) All rights reserved.
+* <artii@o2.pl> LGPL-2.1
+* $ArtId: apetaglib.h,v 1.30 2003/04/16 21:06:27 art Exp $
+********************************************************************/
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1
+ * 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#ifndef _APETAGLIB_H
+#define _APETAGLIB_H
+
+/** \file
+ \brief All function related to apetag
+*/
+
+// Uncomment this line to enable debug messages
+//#define APE_TAG_DEBUG
+
+#ifdef __WATCOMC__ // Watcom don't like || in ifdef and use #if define() || define()
+#define USE_CHSIZE
+#define strcasecmp(a,b) stricmp(a,b)
+#define index(a,b) strchr(a,b)
+#endif
+
+#ifdef __WIN32__
+#define USE_CHSIZE
+#define strcasecmp(a,b) stricmp(a,b)
+#define index(a,b) strchr(a,b)
+#define S_IRGRP S_IRUSR
+#define S_IWGRP S_IWUSR
+#endif
+
+/**\{*/
+#ifdef APE_TAG_DEBUG
+#define PRINT_D(x) fprintf( stdout, x )
+#define PRINT_D1(x,a) fprintf( stdout, x, a )
+#define PRINT_D2(x,a,b) fprintf( stdout, x, a,b)
+#define PRINT_D3(x,a,b,c) fprintf( stdout, x, a,b,c)
+#define PRINT_D4(x,a,b,c,d) fprintf( stdout, x, a,b,c,d)
+#define PRINT_D5(x,a,b,c,d,e) fprintf( stdout, x, a,b,c,d,e)
+#define PRINT_D6(x,a,b,c,d,e,f) fprintf( stdout, x, a,b,c,d,e,f)
+#define PRINT_D7(x,a,b,c,d,e,f,g) fprintf( stdout, x, a,b,c,d,e,f,g)
+#define PRINT_D8(x,a,b,c,d,e,f,g,i) fprintf( stdout, x, a,b,c,d,e,f,g,i)
+#define PRINT_D9(x,a,b,c,d,e,f,g,i,j) fprintf( stdout, x ,a,b,c,d,e,f,g,i,j )
+#else
+#define PRINT_D(x)
+#define PRINT_D1(x,a)
+#define PRINT_D2(x,a,b)
+#define PRINT_D3(x,a,b,c)
+#define PRINT_D4(x,a,b,c,d)
+#define PRINT_D5(x,a,b,c,d,e)
+#define PRINT_D6(x,a,b,c,d,e,f)
+#define PRINT_D7(x,a,b,c,d,e,f,g)
+#define PRINT_D8(x,a,b,c,d,e,f,g,i)
+#define PRINT_D9(x,a,b,c,d,e,f,g,i,j)
+#endif /*APE_TAG_DEBUG*/
+
+#define PRINT_ERR(x) fprintf( stderr, x )
+#define PRINT_ERR1(x, a) fprintf( stderr, x ,a)
+/**\}*/
+
+/** version of apetaglib defined in one place */
+#define APETAGLIB_VERSION "0.5pre1"
+
+
+/*from winamp mpc plugin*/
+/** \name frame names */
+/**\{*/
+#define APE_TAG_FIELD_TITLE "Title"
+#define APE_TAG_FIELD_SUBTITLE "Subtitle"
+#define APE_TAG_FIELD_ARTIST "Artist"
+#define APE_TAG_FIELD_ALBUM "Album"
+#define APE_TAG_FIELD_DEBUTALBUM "Debut Album"
+#define APE_TAG_FIELD_PUBLISHER "Publisher"
+#define APE_TAG_FIELD_CONDUCTOR "Conductor"
+#define APE_TAG_FIELD_COMPOSER "Composer"
+#define APE_TAG_FIELD_COMMENT "Comment"
+#define APE_TAG_FIELD_YEAR "Year"
+#define APE_TAG_FIELD_RECORDDATE "Record Date"
+#define APE_TAG_FIELD_RECORDLOCATION "Record Location"
+#define APE_TAG_FIELD_TRACK "Track"
+#define APE_TAG_FIELD_GENRE "Genre"
+#define APE_TAG_FIELD_COVER_ART_FRONT "Cover Art (front)"
+#define APE_TAG_FIELD_NOTES "Notes"
+#define APE_TAG_FIELD_LYRICS "Lyrics"
+#define APE_TAG_FIELD_COPYRIGHT "Copyright"
+#define APE_TAG_FIELD_PUBLICATIONRIGHT "Publicationright"
+#define APE_TAG_FIELD_FILE "File"
+#define APE_TAG_FIELD_MEDIA "Media"
+#define APE_TAG_FIELD_EANUPC "EAN/UPC"
+#define APE_TAG_FIELD_ISRC "ISRC"
+#define APE_TAG_FIELD_RELATED_URL "Related"
+#define APE_TAG_FIELD_ABSTRACT_URL "Abstract"
+#define APE_TAG_FIELD_BIBLIOGRAPHY_URL "Bibliography"
+#define APE_TAG_FIELD_BUY_URL "Buy URL"
+#define APE_TAG_FIELD_ARTIST_URL "Artist URL"
+#define APE_TAG_FIELD_PUBLISHER_URL "Publisher URL"
+#define APE_TAG_FIELD_FILE_URL "File URL"
+#define APE_TAG_FIELD_COPYRIGHT_URL "Copyright URL"
+#define APE_TAG_FIELD_INDEX "Index"
+#define APE_TAG_FIELD_INTROPLAY "Introplay"
+#define APE_TAG_FIELD_MJ_METADATA "Media Jukebox Metadata"
+#define APE_TAG_FIELD_DUMMY "Dummy"
+/**\}*/
+
+#define APE_TAG_LIB_FIRST "\02" /**< is using by #apefrm_get to get first frame */
+#define APE_TAG_LIB_NEXT "\03" /**< is using by #apefrm_get to get next frame you may check all frames this way */
+#define APE_TAG_LIB_DEL_ALL "\04" /**< is using by #apefrm_remove_real for removing all frames */
+
+
+/**
+ \name #apetag_save flags
+ \note default is #APE_TAG_V2 + #SAVE_NEW_OLD_APE_TAG + #SAVE_REMOVE_ID3V1
+*/
+/**\{*/
+#define APE_TAG_V1 (1 << 1)
+#define APE_TAG_V2 (1 << 2)
+#define SAVE_NEW_APE_TAG (1 << 3)
+#define SAVE_NEW_OLD_APE_TAG (1 << 4)
+#define SAVE_REMOVE_ID3V1 (1 << 5)
+#define SAVE_CREATE_ID3V1_TAG (1 << 6)
+#define SAVE_FAKE_SAVE (1 << 7)
+/* apetag_read(_fp) flags - default read all (ape,id3v1,id3v2(if compiled)) */
+#define DONT_READ_TAG_APE (1 << 8)
+#define DONT_READ_TAG_ID3V1 (1 << 9)
+#define DONT_READ_TAG_ID3V2 (1 << 10)
+/**\}*/
+
+
+/**
+ \name #atl_return
+ \brief return codes from all functions
+ \{
+*/
+#define ATL_OK 0 /**< not using :) */
+#define ATL_FOPEN 1 /**< can't open file */
+#define ATL_FREAD 2 /**< can't read from file */
+#define ATL_FWRITE 3 /**< can't write to file (written bytes != bytes to write) */
+#define ATL_MALOC 4 /**< can't allocate memory */
+#define ATL_BADARG 5 /**< bad function argument */
+#define ATL_NOINIT 6 /**< not inited struct by apetag_init */
+/** \} */
+
+/**
+ \struct tag
+ \brief tag structure
+
+ i you get this <b>don't</b> change anything. copy all values/strings
+*/
+struct tag
+{
+ char *name; /**< name of tag */
+ char *value; /**< value of tag */
+ size_t sizeName; /**< size of name in tag */
+ size_t sizeValue; /**< size of value in tag */
+ unsigned long flags; /**< flags of tag */
+};
+
+
+/**
+ object apetag used to store information about tag.
+ main object store **#tag , number of frames, current position for
+ APE_TAG_LIB_NEXT, etc
+ \brief object apetag used to store information about tag
+**/
+typedef struct _ape_mem_cnt apetag;
+
+
+/*
+ * function:
+ * apetag_*: for load/save/init file and structure
+ * apefrm_*: for add del edit one (or more)
+ * frame(, field, entry) in tag
+ */
+
+/* read file and add frames */
+int
+apetag_read (apetag *mem_cnt, char *filename, int flag) ;
+
+/* read file and add frames */
+int
+apetag_read_fp (apetag *mem_cnt, FILE * fp, char *filename, int flag) ;
+
+/* initialise new object #apetag and return */
+apetag *
+apetag_init (void) ;
+
+/* free #apetag object */
+void
+apetag_free (apetag *mem_cnt) ;
+
+/* save apetag to file */
+int
+apetag_save (char *filename, apetag *mem_cnt, int flag) ;
+
+
+/* Add text frame */
+int
+apefrm_add (apetag *mem_cnt, unsigned long flags, char *name, char *value) ;
+
+/* add binary frame */
+int
+apefrm_add_bin (apetag *mem_cnt, unsigned long flags,
+ long sizeName, char *name, long sizeValue, char *value);
+
+/* add frame if other (the same name) no exist */
+int
+apefrm_add_noreplace (apetag *mem_cnt, unsigned long flags, char *name, char *value) ;
+
+/* search in apetag for name and return tag */
+struct tag *
+apefrm_get (apetag *mem_cnt, char *name) ;
+
+/* search in apetag for name and return string */
+char *
+apefrm_getstr (apetag *mem_cnt, char *name) ;
+
+/* remove frame from memory */
+void
+apefrm_remove_real (apetag *mem_cnt, char *name) ;
+
+
+/**
+ \def apefrm_fake_remove(mem_cnt,name)
+ \brief set frame to remove
+ \deprecated remove in 0.5
+*/
+#define apefrm_fake_remove(mem_cnt,name) apefrm_remove(mem_cnt,name)
+
+/* set frame to remove */
+void
+apefrm_remove (apetag *mem_cnt, char *name);
+
+/* read id3v1 and add frames */
+int
+readtag_id3v1_fp (apetag *mem_cnt, FILE * fp) ;
+
+/** \name flags in frames and headers */
+/**\{*/
+//================================
+#define HEADER_IS 0x80000000
+#define HEADER_NOT 0x00000000
+#define HEADER_THIS_IS 0x20000000
+//================================
+#define FOOTER_IS 0x00000000
+#define FOOTER_NOT 0x40000000
+#define FOOTER_THIS_IS 0x00000000
+//================================
+#define ITEM_TEXT 0x00000000
+#define ITEM_BIN 0x00000002
+#define ITEM_LINK 0x00000004
+#define ITEM_URL 0x00000004 // for compability ITEM_LINK
+//================================
+#define TAG_RW 0x00000000
+#define TAG_RO 0x00000001
+/**\}*/
+
+/* debug function print all tags exclude bin (print only size for bin) */
+void
+libapetag_print_mem_cnt (apetag *mem_cnt);
+
+#endif /* _APETAGLIB_H */
diff --git a/src/libapetag/id3v2_read.c b/src/libapetag/id3v2_read.c
new file mode 100755
index 0000000..f4c8a64
--- /dev/null
+++ b/src/libapetag/id3v2_read.c
@@ -0,0 +1,385 @@
+/********************************************************************
+*
+* Copyright (c) 2002 Artur Polaczynski (Ar't) All rights reserved.
+* <artii@o2.pl> LGPL-2.1
+* $ArtId: id3v2_read.c,v 1.16 2003/04/13 11:24:10 art Exp $
+********************************************************************/
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1
+ * 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#ifdef ID3V2_READ
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <stdarg.h>
+#include <string.h>
+#include <limits.h>
+#include <assert.h>
+#include <math.h>
+#ifndef __BORLANDC__
+# include <unistd.h>
+#endif
+
+#include "apetaglib.h"
+#include <id3.h>
+
+struct id3vtwo2ape {
+ ID3_FrameID frame; //ID3FID_ALBUM etc
+ ID3_FieldID field_type; //ID3FN_TEXT etc
+ const char *APEName;
+ int special; // 0-no 1-???
+};
+
+#define APETAG_TYPE_COMMENT 1
+#define APETAG_TYPE_USER 2
+#define APETAG_TYPE_GENRE 3
+
+
+struct id3vtwo2ape convert[] = {
+ {ID3FID_ALBUM, ID3FN_TEXT, APE_TAG_FIELD_ALBUM,0},
+ {ID3FID_BAND, ID3FN_TEXT, "Band",0},
+ {ID3FID_BPM, ID3FN_TEXT, "BPM",0},
+ {ID3FID_COMPOSER, ID3FN_TEXT, APE_TAG_FIELD_COMPOSER,0},
+ {ID3FID_CONDUCTOR, ID3FN_TEXT, APE_TAG_FIELD_CONDUCTOR,0},
+ {ID3FID_CONTENTGROUP, ID3FN_TEXT, "Content Group",0},
+ {ID3FID_COPYRIGHT, ID3FN_TEXT, APE_TAG_FIELD_COPYRIGHT,0},
+ {ID3FID_DATE, ID3FN_TEXT, APE_TAG_FIELD_RECORDDATE,0},
+ {ID3FID_ENCODEDBY, ID3FN_TEXT, "Encoded By",0},
+ {ID3FID_ENCODERSETTINGS, ID3FN_TEXT, "Encoder",0},
+ {ID3FID_FILEOWNER, ID3FN_TEXT, "File Owner",0},
+ {ID3FID_FILETYPE, ID3FN_TEXT, "File Type",0},
+ {ID3FID_INITIALKEY, ID3FN_TEXT, "Initial Key",0},
+ {ID3FID_ISRC, ID3FN_TEXT, APE_TAG_FIELD_ISRC,0},
+ {ID3FID_LANGUAGE, ID3FN_TEXT, "Language",0},
+ {ID3FID_LEADARTIST, ID3FN_TEXT, APE_TAG_FIELD_ARTIST,0},
+ {ID3FID_LYRICIST, ID3FN_TEXT, "Lyricist",0},
+ {ID3FID_MEDIATYPE, ID3FN_TEXT, APE_TAG_FIELD_MEDIA,0},
+ {ID3FID_MIXARTIST, ID3FN_TEXT, "Mix Artist",0},
+ {ID3FID_NETRADIOOWNER, ID3FN_TEXT, "Internet Radio Owner",0},
+ {ID3FID_NETRADIOSTATION, ID3FN_TEXT, "Internet Radio Station",0},
+ {ID3FID_ORIGALBUM, ID3FN_TEXT, "Original Album",0},
+ {ID3FID_ORIGARTIST, ID3FN_TEXT, "Original Artist",0},
+ {ID3FID_ORIGFILENAME, ID3FN_TEXT, "Original Filename",0},
+ {ID3FID_ORIGLYRICIST, ID3FN_TEXT, "Original Lyricist",0},
+ {ID3FID_ORIGYEAR, ID3FN_TEXT, "Original Artist",0},
+ {ID3FID_PARTINSET, ID3FN_TEXT, "Part",0},
+ {ID3FID_PLAYLISTDELAY, ID3FN_TEXT, "Playlist Delay",0},
+ {ID3FID_PUBLISHER, ID3FN_TEXT, APE_TAG_FIELD_PUBLISHER,0},
+ {ID3FID_RECORDINGDATES, ID3FN_TEXT, APE_TAG_FIELD_RECORDDATE,0},
+ {ID3FID_SIZE, ID3FN_TEXT, "Size",0},
+// {ID3FID_SONGLEN, ID3FN_TEXT, "Song Length",0}, // don't like this in apetag
+ {ID3FID_SUBTITLE, ID3FN_TEXT, APE_TAG_FIELD_SUBTITLE,0},
+ {ID3FID_TIME, ID3FN_TEXT, "Time",0},
+ {ID3FID_TITLE, ID3FN_TEXT, APE_TAG_FIELD_TITLE,0},
+ {ID3FID_TRACKNUM, ID3FN_TEXT, APE_TAG_FIELD_TRACK,0},
+ {ID3FID_YEAR, ID3FN_TEXT, APE_TAG_FIELD_YEAR,0},
+
+ {ID3FID_WWWAUDIOFILE, ID3FN_URL , APE_TAG_FIELD_FILE_URL,0},
+ {ID3FID_WWWARTIST, ID3FN_URL , APE_TAG_FIELD_ARTIST_URL,0},
+ {ID3FID_WWWAUDIOSOURCE, ID3FN_URL , "Source URL",0},
+ {ID3FID_WWWCOMMERCIALINFO, ID3FN_URL , APE_TAG_FIELD_BUY_URL,0},
+ {ID3FID_WWWCOPYRIGHT, ID3FN_URL , APE_TAG_FIELD_COPYRIGHT_URL,0},
+ {ID3FID_WWWPUBLISHER, ID3FN_URL , APE_TAG_FIELD_PUBLISHER_URL,0},
+ {ID3FID_WWWPAYMENT, ID3FN_URL , "Payment",0},
+ {ID3FID_WWWRADIOPAGE, ID3FN_URL , "Web Radio URL",0},
+
+ {ID3FID_COMMENT, ID3FN_TEXT, APE_TAG_FIELD_COMMENT, APETAG_TYPE_COMMENT},
+ {ID3FID_UNSYNCEDLYRICS, ID3FN_TEXT, APE_TAG_FIELD_LYRICS, APETAG_TYPE_COMMENT},
+ {ID3FID_USERTEXT, ID3FN_TEXT, "dummy", APETAG_TYPE_USER},
+ {ID3FID_WWWUSER, ID3FN_URL , APE_TAG_FIELD_RELATED_URL, APETAG_TYPE_USER},
+ {ID3FID_CONTENTTYPE, ID3FN_TEXT, APE_TAG_FIELD_GENRE, APETAG_TYPE_GENRE},
+
+
+
+// {ID3FID_PICTURE, ID3FN_DATA, ,0},
+// {ID3FID_SYNCEDLYRICS, ID3FN_DATA, APE_TAG_FIELD_LYRICS,0},
+// {ID3FID_INVOLVEDPEOPLE, ID3FN_DATA, "Involved People",0}, // type ?
+// {ID3FID_CDID, ID3FN_DATA, "CDID",0}, // ???
+// {ID3FID_TERMSOFUSE, ID3FN_TEXT, "Terms Of Use",0}, // type ?
+};
+
+int
+libapetag_convertID3v2toAPE(const ID3Frame * frame,
+ char **item_, size_t *item_len,
+ char **value_, size_t *value_len,
+ unsigned long *flags);
+
+
+/*
+ use ALOCATE to alocate dynamic memory for frame value
+ this check size of frame and alocate mem and zeroed this mem
+*/
+
+#define ALOCATE(FielD,ValuE,SizeValuE) \
+ SizeValuE = ID3Field_Size(FielD); \
+ ValuE = (SizeValuE!=0) ? (char *) malloc((SizeValuE)+1) : NULL ; \
+ if ((SizeValuE)!=0) ValuE[0] = '\0';
+/*
+ ALOCATE_ITEM its the same ase ALOCATE but for frame name (Item)
+*/
+#define ALOCATE_ITEM(IteM, APENamE, ItemSizE) \
+ ItemSizE = strlen(APENamE) ; \
+ IteM = (ItemSizE!=0) ? (char *) malloc((ItemSizE)+1) : NULL ; \
+ if ((ItemSizE)!=0) IteM[0] = '\0';
+
+int
+libapetag_convertID3v2toAPE (const ID3Frame * frame,
+ char **item_, size_t * item_len,
+ char **value_, size_t * value_len,
+ unsigned long *flags)
+{
+ ID3Field *text;
+ ID3Field *desc;
+ ID3Field *url;
+// ID3Field *bin; // will be implemented some day
+
+ char *item = NULL;
+ char *value = NULL;
+
+ ID3_FrameID frameid = ID3Frame_GetID (frame);
+ unsigned int i;
+
+ *flags = ITEM_TEXT;
+
+ for (i = 0; i < sizeof (convert) / sizeof (struct id3vtwo2ape); i++)
+ if (frameid == convert[i].frame)
+ break;
+
+
+ if (convert[i].field_type == ID3FN_TEXT) {
+ switch (convert[i].special) {
+ case APETAG_TYPE_COMMENT: /* Comments and unsynced lyrics */
+ if ((text = ID3Frame_GetField(frame, ID3FN_TEXT)) != NULL) {
+ ALOCATE(text, value, *value_len);
+ ID3Field_GetASCII(text, value, *value_len);
+ }
+ ALOCATE_ITEM(item, convert[i].APEName, *item_len);
+ strncpy(item, convert[i].APEName, *item_len);
+ item[*item_len]='\0';
+ //break;
+ if ((text = ID3Frame_GetField (frame, ID3FN_DESCRIPTION)) != NULL) {
+ char *value_ds=NULL;
+ int value_len2;
+ if (ID3Field_Size(text) != 0) {
+ ALOCATE(text, value_ds, value_len2);
+ ID3Field_GetASCII(text, value_ds, value_len2);
+ if ( strcmp(value_ds, STR_V1_COMMENT_DESC) == 0 ) {
+ value_len2 = 0;
+ value[0]='\0';
+ } else {
+ item = (char *) realloc( item, (*item_len) + value_len2 + 3);
+ item[(*item_len)++]='-'; item[(*item_len)]='\0';
+ strncpy(item + (*item_len),value_ds ,(value_len2 + 1));
+ (*item_len)+=value_len2;
+ }
+ free(value_ds);
+ }
+ }
+ break;
+
+ case APETAG_TYPE_USER: /* User texts */
+ if ((text = ID3Frame_GetField(frame, ID3FN_TEXT)) != NULL) {
+ ALOCATE(text, value, *value_len);
+ ID3Field_GetASCII(text, value, *value_len);
+ }
+ if ((desc = ID3Frame_GetField(frame, ID3FN_DESCRIPTION)) != NULL) {
+ ALOCATE(desc, item, *item_len);
+ ID3Field_GetASCII(desc, item, *item_len);
+ }
+ break;
+
+ case APETAG_TYPE_GENRE: /* genre */
+ if ((text = ID3Frame_GetField(frame, ID3FN_TEXT)) != NULL) {
+ char *p;
+ int j;
+ ALOCATE(text, value, *value_len);
+ ID3Field_GetASCII(text, value, *value_len);
+ ALOCATE_ITEM(item, convert[i].APEName, *item_len);
+ strncpy(item, convert[i].APEName, *item_len);
+ value[*value_len]='\0';
+ p = value;
+ if (*p == '(') {
+ p++;
+ while (*p && (*p >= '0' && *p <= '9'))
+ p++;
+ if (*p && *p == ')') {
+ p++;
+ } else {
+ p = value;
+ }
+ *value_len -= (p-value); // corect lenght of frame
+ if (*p != '\0') { // copy in place
+ for (j = 0; *p != '\0'; j++) {
+ value[j] = *p;
+ p++;
+ }
+ value[j] = '\0';
+ }
+ }
+ }
+ break;
+
+ default: /* normal text tags */
+ if ((text = ID3Frame_GetField(frame, ID3FN_TEXT)) != NULL) {
+ ALOCATE(text, value, *value_len);
+ ID3Field_GetASCII(text, value, *value_len);
+ ALOCATE_ITEM(item, convert[i].APEName, *item_len);
+ strncpy(item, convert[i].APEName, *item_len);
+ }
+ break;
+
+ } /* <- switch( convert[i].special ) */
+
+ item[*item_len]='\0';
+ value[*value_len]='\0';
+ } else
+ if (convert[i].field_type == ID3FN_URL) {
+ *flags = ITEM_URL;
+ /* TODO: set ape_tag_URL in flag */
+ /* user url */
+ if (convert[i].special == APETAG_TYPE_USER) {
+ if ((url = ID3Frame_GetField(frame, ID3FN_URL)) != NULL) {
+ ALOCATE(url, value, *value_len);
+ ID3Field_GetASCII(url, value, *value_len);
+ }
+ if ((desc = ID3Frame_GetField(frame, ID3FN_DESCRIPTION)) != NULL) {
+ ALOCATE(desc, item, *item_len);
+ ID3Field_GetASCII(desc, item, *item_len);
+ }
+ /* normal url */
+ } else {
+ if ((url = ID3Frame_GetField (frame, ID3FN_URL)) != NULL) {
+ ALOCATE(url, value, *value_len);
+ ID3Field_GetASCII(url, value, *value_len);
+ ALOCATE_ITEM(item, convert[i].APEName, *item_len);
+ strncpy(item, convert[i].APEName, *item_len);
+ }
+ }
+
+ item[*item_len]='\0';
+ value[*value_len]='\0';
+ } else { //convert[i].field_type
+ item = NULL;
+ value = NULL;
+ PRINT_D (">id3v2_read>other\n");
+ }
+ *item_ = item;
+ *value_ = value;
+
+ if (!(value==NULL || (*value_len)==0) && value[(*value_len)-1]=='\0')
+ (*value_len)--;
+
+ return 0;
+}
+
+// Reads ID3v2.x tag
+// idea of this come from "tag" by Case <case@mobiili.net>
+int readtag_id3v2 ( apetag *mem_cnt, char* fileName )
+{
+ ID3Tag* tag;
+ ID3Frame* frame;
+ ID3TagIterator* iter;
+ char* item = NULL;
+ int itemSize;
+ char* value = NULL;
+ int valueSize;
+ unsigned long flags;
+
+ // first - check of id3tag v2 and init
+ if ( (tag = ID3Tag_New ()) == NULL )
+ return 1;
+ // on some casses its weerrryyy slooowwwwlyyy 65k file take 2-5 sec
+ ID3Tag_LinkWithFlags ( tag, fileName, ID3TT_ID3V2 );
+
+ if ( tag == NULL ) {
+ ID3Tag_Delete (tag);
+ return 0;
+ }
+
+ if ( !ID3Tag_HasTagType ( tag, ID3TT_ID3V2 ) ) {
+ ID3Tag_Delete (tag);
+ return 0;
+ }
+
+ if ( (iter = ID3Tag_CreateIterator (tag)) == NULL ) {
+ ID3Tag_Delete (tag);
+ return 0;
+ }
+
+ while ( (frame = ID3TagIterator_GetNext (iter)) != NULL ) {
+
+ libapetag_convertID3v2toAPE ( frame, &item, &itemSize, &value, &valueSize, &flags);
+
+ if ( !item || !value || item[0] == '\0' || value[0] == '\0' )
+ continue;
+
+ if ( !value || value[0] != '\0' ) {
+ PRINT_D4(">id3v2_read>[i%i]%s: [v%i]'%s'\n",itemSize,item,valueSize,value);
+ if ( apefrm_getstr (mem_cnt, item) == NULL ) /* noreplece !!! */
+ apefrm_add_bin (mem_cnt, flags, itemSize, item, valueSize ,value);
+ }
+ free ( value );
+ free ( item );
+ }
+
+ ID3TagIterator_Delete (iter);
+ ID3Tag_Delete (tag);
+
+ return 0;
+}
+
+#endif // ID3V2_READ
+
+
+
+#if 0
+static ID3_FrameDef ID3_FrameDefs[] =
+{
+ /* frames to implement */
+ // short long
+ // frame id id id field defs description
+ {ID3FID_AUDIOCRYPTO, "CRA", "AENC",ID3FD_Unimplemented, "Audio encryption"},
+ {ID3FID_BUFFERSIZE, "BUF", "RBUF",ID3FD_Unimplemented, "Recommended buffer size"},
+ {ID3FID_CDID, "MCI", "MCDI",ID3FD_Unimplemented, "Music CD identifier"},
+ {ID3FID_COMMERCIAL, "" , "COMR",ID3FD_Unimplemented, "Commercial"},
+ {ID3FID_CRYPTOREG, "" , "ENCR",ID3FD_Registration, "Encryption method registration"},
+ {ID3FID_EQUALIZATION, "EQU", "EQUA",ID3FD_Unimplemented, "Equalization"},
+ {ID3FID_EVENTTIMING, "ETC", "ETCO",ID3FD_Unimplemented, "Event timing codes"},
+ {ID3FID_GENERALOBJECT, "GEO", "GEOB",ID3FD_GEO, "General encapsulated object"},
+ {ID3FID_GROUPINGREG, "" , "GRID",ID3FD_Registration, "Group identification registration"},
+ {ID3FID_INVOLVEDPEOPLE, "IPL", "IPLS",ID3FD_InvolvedPeople,"Involved people list"},
+ {ID3FID_LINKEDINFO, "LNK", "LINK",ID3FD_LinkedInfo, "Linked information"},
+ {ID3FID_METACOMPRESSION, "CDM", "" ,ID3FD_CDM, "Compressed data meta frame"},
+ {ID3FID_METACRYPTO, "CRM", "" ,ID3FD_Unimplemented, "Encrypted meta frame"},
+ {ID3FID_MPEGLOOKUP, "MLL", "MLLT",ID3FD_Unimplemented, "MPEG location lookup table"},
+ {ID3FID_OWNERSHIP, "" , "OWNE",ID3FD_Unimplemented, "Ownership frame"},
+ {ID3FID_PICTURE, "PIC", "APIC",ID3FD_Picture, "Attached picture"},
+ {ID3FID_PLAYCOUNTER, "CNT", "PCNT",ID3FD_PlayCounter, "Play counter"},
+ {ID3FID_POPULARIMETER, "POP", "POPM",ID3FD_Popularimeter, "Popularimeter"},
+ {ID3FID_POSITIONSYNC, "" , "POSS",ID3FD_Unimplemented, "Position synchronisation frame"},
+ {ID3FID_PRIVATE, "" , "PRIV",ID3FD_Private, "Private frame"},
+ {ID3FID_REVERB, "REV", "RVRB",ID3FD_Unimplemented, "Reverb"},
+ {ID3FID_SYNCEDLYRICS, "SLT", "SYLT",ID3FD_SyncLyrics, "Synchronized lyric/text"},
+ {ID3FID_SYNCEDTEMPO, "STC", "SYTC",ID3FD_Unimplemented, "Synchronized tempo codes"},
+ {ID3FID_TERMSOFUSE, "" , "USER",ID3FD_TermsOfUse, "Terms of use"},
+ {ID3FID_UNIQUEFILEID, "UFI", "UFID",ID3FD_UFI, "Unique file identifier"},
+ {ID3FID_VOLUMEADJ, "RVA", "RVAD",ID3FD_Unimplemented, "Relative volume adjustment"},
+
+};
+#endif // 0
diff --git a/src/libapetag/id3v2_read.h b/src/libapetag/id3v2_read.h
new file mode 100755
index 0000000..16d433d
--- /dev/null
+++ b/src/libapetag/id3v2_read.h
@@ -0,0 +1,42 @@
+/********************************************************************
+*
+* Copyright (c) 2002 Artur Polaczynski (Ar't) All rights reserved.
+* <artii@o2.pl> LGPL-2.1
+* $ArtId: id3v2_read.h,v 1.6 2003/04/04 20:06:40 art Exp $
+********************************************************************/
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1
+ * 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#ifndef _ID3V2_READ_H
+#define _ID3V2_READ_H
+
+/** \file id3v2_read.h
+ \brief reading id3v2 tag to ape_cnt
+*/
+
+#ifdef ID3V2_READ
+/**
+ reading id3v2 tag to #mem_cnt
+
+ \param mem_cnt stucture ape_mem_cnt
+ \param fileName file name (no file pointer becase libid3 using filename in c mode)
+*/
+int readtag_id3v2 ( apetag *mem_cnt, char* fileName );
+#endif
+
+#endif /* _ID3V2_READ_H */
+
diff --git a/src/libapetag/info_mac.c b/src/libapetag/info_mac.c
new file mode 100755
index 0000000..1b8b5e2
--- /dev/null
+++ b/src/libapetag/info_mac.c
@@ -0,0 +1,151 @@
+/********************************************************************
+*
+* Copyright (c) 2002 Artur Polaczynski (Ar't) All rights reserved.
+* <artii@o2.pl> LGPL-2.1
+* $ArtId: info_mac.c,v 1.15 2003/04/13 11:24:10 art Exp $
+********************************************************************/
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1
+ * 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "info_mac.h"
+#include "is_tag.h"
+
+#define MAC_FORMAT_FLAG_8_BIT 1 // 8-bit wave
+#define MAC_FORMAT_FLAG_CRC 2 // new CRC32 error detection
+#define MAC_FORMAT_FLAG_HAS_PEAK_LEVEL 4 // u-long Peak_Level after the header
+#define MAC_FORMAT_FLAG_24_BIT 8 // 24-bit wave
+#define MAC_FORMAT_FLAG_HAS_SEEK_ELEMENTS 16 // number of seek elements after the peak level
+#define MAC_FORMAT_FLAG_CREATE_WAV_HEADER 32 // wave header not stored
+
+struct macHeader {
+ char id[4]; // should equal 'MAC '
+ unsigned short ver; // version number * 1000 (3.81 = 3810)
+ unsigned short compLevel; // the compression level
+ unsigned short formatFlags; // any format flags (for future use)
+ unsigned short channels; // the number of channels (1 or 2)
+ unsigned long sampleRate; // the sample rate (typically 44100)
+ unsigned long headerBytesWAV; // the bytes after the MAC header that compose the WAV header
+ unsigned long terminatingBytesWAV; // the bytes after that raw data (for extended info)
+ unsigned long totalFrames; // the number of frames in the file
+ unsigned long finalFrameBlocks; // the number of samples in the final frame
+ unsigned long peakLevel;
+ unsigned short seekElements;
+};
+
+
+// local prototypes
+static int
+monkey_samples_per_frame(unsigned int versionid, unsigned int compressionlevel);
+static const char *
+monkey_stringify(unsigned int profile);
+
+static const char *
+monkey_stringify(unsigned int profile)
+{
+ static const char na[] = "unknown";
+ static const char *Names[] = {
+ na, "Fast", "Normal", "High", "Extra-High", "Insane"
+ };
+ unsigned int profile2 = profile/1000;
+
+ return (profile2 >= sizeof (Names) / sizeof (*Names)) ? na : Names[(profile2)];
+}
+
+
+static int
+monkey_samples_per_frame(unsigned int versionid, unsigned int compressionlevel)
+{
+ if (versionid >= 3950) {
+ return 294912; // 73728 * 4
+ } else if (versionid >= 3900) {
+ return 73728;
+ } else if ((versionid >= 3800) && (compressionlevel == COMPRESSION_LEVEL_EXTRA_HIGH)) {
+ return 73728;
+ } else {
+ return 9216;
+ }
+}
+
+/*
+ return 0; Info has all info
+ return 1; File not found
+ return 2; no MAC file
+*/
+int
+info_mac_read(const char *fn, StreamInfoMac * Info)
+{
+ unsigned int HeaderData[32];
+ FILE *tmpFile = NULL;
+ long SkipSizeID3;
+ struct macHeader * header;
+
+ // load file
+ tmpFile = fopen(fn, "rb");
+
+ if (tmpFile == NULL)
+ return 1; // file not found or read-protected
+
+ // skip id3v2
+ SkipSizeID3 = is_id3v2(tmpFile);
+ fseek(tmpFile, SkipSizeID3, SEEK_SET);
+ fread((void *) HeaderData, sizeof (int), 16, tmpFile);
+ fseek(tmpFile, 0, SEEK_END);
+ Info->FileSize = ftell(tmpFile);
+ fclose(tmpFile);
+
+ if (0 != memcmp(HeaderData, "MAC", 3))
+ return 2; // no monkeyAudio file
+
+ header= (struct macHeader *) HeaderData;
+
+ Info->Version = Info->EncoderVersion = header->ver;
+ Info->Channels = header->channels;
+ Info->SampleFreq = header->sampleRate;
+ Info->Flags = header->formatFlags;
+ Info->SamplesPerFrame = monkey_samples_per_frame(header->ver, header->compLevel);
+ Info->BitsPerSample = (header->formatFlags & MAC_FORMAT_FLAG_8_BIT)
+ ? 8 : ((header->formatFlags & MAC_FORMAT_FLAG_24_BIT) ? 24 : 16);
+
+ Info->PeakLevel = header->peakLevel;
+// Info->PeakRatio = Info->PakLevel / pow(2, Info->bitsPerSample - 1);
+ Info->Frames = header->totalFrames;
+ Info->Samples = (Info->Frames - 1) * Info->SamplesPerFrame +
+ header->finalFrameBlocks;
+
+ Info->Duration = Info->SampleFreq > 0 ?
+ ((float)Info->Samples / Info->SampleFreq)*1000 : 0;
+
+ Info->Compresion = header->compLevel;
+ Info->CompresionName = monkey_stringify(Info->Compresion);
+
+ Info->UncompresedSize = Info->Samples * Info->Channels *
+ (Info->BitsPerSample / 8);
+
+ Info->CompresionRatio =
+ (Info->UncompresedSize + header->headerBytesWAV) > 0 ?
+ Info->FileSize / (float) (Info->UncompresedSize +
+ header->headerBytesWAV) : 0. ;
+
+ Info->Bitrate = Info->Duration > 0 ? (((Info->Samples *
+ Info->Channels * Info->BitsPerSample) / (float) Info->Duration) *
+ Info->CompresionRatio) * 1000 : 0;
+
+ Info->PeakRatio=Info->ByteLength=0;
+ return 0;
+}
diff --git a/src/libapetag/info_mac.h b/src/libapetag/info_mac.h
new file mode 100755
index 0000000..a17e47c
--- /dev/null
+++ b/src/libapetag/info_mac.h
@@ -0,0 +1,96 @@
+/********************************************************************
+*
+* Copyright (c) 2002 Artur Polaczynski (Ar't) All rights reserved.
+* <artii@o2.pl> LGPL-2.1
+* $ArtId: info_mac.h,v 1.6 2003/04/13 11:24:10 art Exp $
+********************************************************************/
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1
+ * 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#ifndef INFO_MAC_H
+#define INFO_MAC_H
+
+/** \file info_mac.h
+ \brief Get information from MonkeyAudio file.
+
+ Usage:
+ \code
+ StreamInfoMac Info;
+
+ if (info_mac_read(fn, &Info)) {
+ printf("File \"%s\" not found or is read protected!\n", fn);
+ return;
+ }
+ printf("%",Info.fields...);
+ \endcode
+*/
+
+/**
+ \name Compression level
+*/
+/*\{*/
+#define COMPRESSION_LEVEL_FAST 1000 /**< fast */
+#define COMPRESSION_LEVEL_NORMAL 2000 /**< optimal average time/compression ratio */
+#define COMPRESSION_LEVEL_HIGH 3000 /**< higher compression ratio */
+#define COMPRESSION_LEVEL_EXTRA_HIGH 4000 /**< very slowly */
+#define COMPRESSION_LEVEL_INSANE 5000 /**< ??? */
+/*\}*/
+
+/** All information from mac file
+ * \struct StreamInfoMac
+**/
+typedef struct
+{
+ unsigned int ByteLength; /**< file length - tags size */
+ unsigned int FileSize; /**< real file size */
+ int SampleFreq; /**< sample frequency */
+ unsigned int Channels; /**< number of chanels */
+ int Duration; /**< duratiom in ms */
+
+ unsigned int Version; /**< version of current file */
+ unsigned int Bitrate; /**< bitrate of current file (bps) */
+ unsigned int Compresion; /**< compresion profile */
+ unsigned int Flags; /**< flags */
+
+ unsigned int Frames; /**< number of frames */
+ unsigned int SamplesPerFrame; /**< samples per frame */
+ unsigned int Samples; /**< number of samples */
+ unsigned int BitsPerSample; /**< bits per sample */
+ unsigned int UncompresedSize; /**< uncomprese size of file */
+ float CompresionRatio; /**< compresion ratio */
+
+ unsigned int PeakLevel; /**< peak level */
+ float PeakRatio; /**< peak ratio */
+
+ const char *CompresionName; /**< compresion profile as string */
+
+ unsigned int EncoderVersion; /**< version of encoder used */
+} StreamInfoMac;
+
+/**
+ Read all mac info from filename
+
+ \param fn File name
+ \param Info StreamInfoMac Structure for all information
+ \retval 0 ok
+ \retval 1 file not found or write protected
+ \retval 2 not monkey's audio file
+*/
+int
+info_mac_read(const char *fn, StreamInfoMac * Info);
+
+#endif /* INFO_MAC_H */
diff --git a/src/libapetag/info_mpc.c b/src/libapetag/info_mpc.c
new file mode 100755
index 0000000..18ee71b
--- /dev/null
+++ b/src/libapetag/info_mpc.c
@@ -0,0 +1,198 @@
+/********************************************************************
+*
+* Copyright (c) 2002 Artur Polaczynski (Ar't) All rights reserved.
+* <artii@o2.pl> LGPL-2.1
+* $ArtId: info_mpc.c,v 1.12 2003/04/13 11:24:10 art Exp $
+********************************************************************/
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1
+ * 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+/*
+ Some portions of code or/and ideas come from
+ winamp plugins, xmms plugins, mppdec decoder
+ thanks:
+ -Frank Klemm <Frank.Klemm@uni-jena.de>
+ -Andree Buschmann <Andree.Buschmann@web.de>
+ -Thomas Juerges <thomas.juerges@astro.ruhr-uni-bochum.de>
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "info_mpc.h"
+#include "is_tag.h"
+
+
+/*
+*.MPC,*.MP+,*.MPP
+*/
+static const char *
+profile_stringify(unsigned int profile); // profile is 0...15, where 7...13 is used
+
+
+static const char *
+profile_stringify(unsigned int profile) // profile is 0...15, where 7...13 is used
+{
+ static const char na[] = "n.a.";
+ static const char *Names[] = {
+ na, "Experimental", na, na,
+ na, na, na, "Telephone",
+ "Thumb", "Radio", "Standard", "Xtreme",
+ "Insane", "BrainDead", "BrainDead+", "BrainDead++"
+ };
+
+ return profile >=
+ sizeof (Names) / sizeof (*Names) ? na : Names[profile];
+}
+
+int
+read_file_header_fp(FILE *fp, StreamInfoMpc * Info)
+{
+
+return 0;
+}
+
+/*
+ return 0; Info has all info
+ return 1; File not found
+ return 2; no Mpc file
+*/
+int
+info_mpc_read(const char *fn, StreamInfoMpc * Info)
+{
+ unsigned int HeaderData[16];
+ FILE *tmpFile = NULL;
+ long SkipSizeID3;
+
+ // load file
+ tmpFile = fopen(fn, "rb");
+
+ if (tmpFile == NULL)
+ return 1; // file not found or read-protected
+ // skip id3v2
+ SkipSizeID3=is_id3v2(tmpFile);
+ fseek(tmpFile,SkipSizeID3 , SEEK_SET);
+ fread((void *) HeaderData, sizeof (int), 16, tmpFile);
+ fseek(tmpFile, 0, SEEK_END);
+ Info->FileSize=ftell(tmpFile);
+ // stream size
+ Info->ByteLength = Info->FileSize-is_id3v1(tmpFile)-is_ape(tmpFile)-SkipSizeID3;
+
+ fclose(tmpFile);
+
+ if (0 != memcmp(HeaderData, "MP+", 3))
+ return 2; // no musepack file
+
+ Info->StreamVersion = HeaderData[0] >> 24;
+ if (Info->StreamVersion >= 7) {
+ const long samplefreqs[4] = { 44100, 48000, 37800, 32000 };
+
+ // read the file-header (SV7 and above)
+ Info->Bitrate = 0;
+ Info->Frames = HeaderData[1];
+ Info->SampleFreq = samplefreqs[(HeaderData[2] >> 16) & 0x0003];
+ Info->MaxBand = (HeaderData[2] >> 24) & 0x003F;
+ Info->MS = (HeaderData[2] >> 30) & 0x0001;
+ Info->Profile = (HeaderData[2] << 8) >> 28;
+ Info->IS = (HeaderData[2] >> 31) & 0x0001;
+ Info->BlockSize = 1;
+
+ Info->EncoderVersion = (HeaderData[6] >> 24) & 0x00FF;
+ Info->Channels = 2;
+ // gain
+ Info->EstPeakTitle = HeaderData[2] & 0xFFFF; // read the ReplayGain data
+ Info->GainTitle = (HeaderData[3] >> 16) & 0xFFFF;
+ Info->PeakTitle = HeaderData[3] & 0xFFFF;
+ Info->GainAlbum = (HeaderData[4] >> 16) & 0xFFFF;
+ Info->PeakAlbum = HeaderData[4] & 0xFFFF;
+ // gaples
+ Info->IsTrueGapless = (HeaderData[5] >> 31) & 0x0001; // true gapless: used?
+ Info->LastFrameSamples = (HeaderData[5] >> 20) & 0x07FF; // true gapless: valid samples for last frame
+
+ if (Info->EncoderVersion == 0) {
+ sprintf(Info->Encoder, "<= 1.05"); // Buschmann 1.7.x, Klemm <= 1.05
+ } else {
+ switch (Info->EncoderVersion % 10) {
+ case 0:
+ sprintf(Info->Encoder, "%u.%u",
+ Info->EncoderVersion / 100,
+ Info->EncoderVersion / 10 % 10);
+ break;
+ case 2:
+ case 4:
+ case 6:
+ case 8:
+ sprintf(Info->Encoder, "%u.%02u Beta",
+ Info->EncoderVersion / 100,
+ Info->EncoderVersion % 100);
+ break;
+ default:
+ sprintf(Info->Encoder, "%u.%02u Alpha",
+ Info->EncoderVersion / 100,
+ Info->EncoderVersion % 100);
+ break;
+ }
+ }
+ // estimation, exact value needs too much time
+ Info->Bitrate = (long) (Info->ByteLength) * 8. * Info->SampleFreq / (1152 * Info->Frames - 576);
+
+ } else {
+ // read the file-header (SV6 and below)
+ Info->Bitrate = ((HeaderData[0] >> 23) & 0x01FF) * 1000; // read the file-header (SV6 and below)
+ Info->MS = (HeaderData[0] >> 21) & 0x0001;
+ Info->IS = (HeaderData[0] >> 22) & 0x0001;
+ Info->StreamVersion = (HeaderData[0] >> 11) & 0x03FF;
+ Info->MaxBand = (HeaderData[0] >> 6) & 0x001F;
+ Info->BlockSize = (HeaderData[0]) & 0x003F;
+
+ Info->Profile = 0;
+ //gain
+ Info->GainTitle = 0; // not supported
+ Info->PeakTitle = 0;
+ Info->GainAlbum = 0;
+ Info->PeakAlbum = 0;
+ //gaples
+ Info->LastFrameSamples = 0;
+ Info->IsTrueGapless = 0;
+
+ if (Info->StreamVersion >= 5)
+ Info->Frames = HeaderData[1]; // 32 bit
+ else
+ Info->Frames = (HeaderData[1] >> 16); // 16 bit
+
+ Info->EncoderVersion = 0;
+ Info->Encoder[0] = '\0';
+#if 0
+ if (Info->StreamVersion == 7)
+ return ERROR_CODE_SV7BETA; // are there any unsupported parameters used?
+ if (Info->Bitrate != 0)
+ return ERROR_CODE_CBR;
+ if (Info->IS != 0)
+ return ERROR_CODE_IS;
+ if (Info->BlockSize != 1)
+ return ERROR_CODE_BLOCKSIZE;
+#endif
+ if (Info->StreamVersion < 6) // Bugfix: last frame was invalid for up to SV5
+ Info->Frames -= 1;
+
+ Info->SampleFreq = 44100; // AB: used by all files up to SV7
+ Info->Channels = 2;
+ }
+
+ Info->ProfileName=profile_stringify(Info->Profile);
+
+ Info->Duration = (int) (Info->Frames * 1152 / (Info->SampleFreq/1000.0));
+ return 0;
+}
diff --git a/src/libapetag/info_mpc.h b/src/libapetag/info_mpc.h
new file mode 100755
index 0000000..330cbf0
--- /dev/null
+++ b/src/libapetag/info_mpc.h
@@ -0,0 +1,96 @@
+/********************************************************************
+*
+* Copyright (c) 2002 Artur Polaczynski (Ar't) All rights reserved.
+* <artii@o2.pl> LGPL-2.1
+* $ArtId: info_mpc.h,v 1.8 2003/04/13 11:24:10 art Exp $
+********************************************************************/
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1
+ * 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#ifndef INFO_MPC_H
+#define INFO_MPC_H
+
+/** \file info_mpc.h
+ \brief Get information from MusePack file.
+
+ Usage:
+ \code
+ StreamInfoMpc Info;
+
+ if (info_mpc_read(fn, &Info)) {
+ printf("File \"%s\" not found or is read protected!\n", fn);
+ return;
+ }
+ printf("%",Info.fields);
+ \endcode
+*/
+
+
+/** All information from mpc file
+ * \struct StreamInfoMpc
+**/
+typedef struct
+{
+ unsigned int ByteLength; /**< file length - tags size */
+ unsigned int FileSize; /**< real file size */
+ int SampleFreq; /**< Sample frequency */
+ unsigned int Channels; /**< channels =2 */
+ int Duration; /**< duratiom in ms */
+
+ unsigned int StreamVersion; /**< Streamversion of current file */
+ unsigned int Bitrate; /**< bitrate of current file (bps) */
+ unsigned int Frames; /**< number of frames contained */
+ unsigned int MS; /**< Mid/Side Stereo (0: off, 1: on) */
+ unsigned int Profile; /**< quality profile */
+ unsigned int MaxBand; /**< maximum band-index used (0...31) */
+ unsigned int IS; /**< Intensity Stereo (0: off, 1: on) */
+ unsigned int BlockSize; /**< only needed for SV4...SV6 -> not supported */
+
+ const char *ProfileName; /**< Profile name */
+ unsigned int EncoderVersion; /**< version of encoder used */
+ char Encoder[256]; /**< Encoder Version in string */
+
+ // ReplayGain related data
+ short GainTitle; /**< Gain Title */
+ short GainAlbum; /**< Gain Album */
+ unsigned short PeakAlbum; /**< Peak value of Album */
+ unsigned short PeakTitle; /**< Peak value of Title */
+ unsigned short EstPeakTitle; /**< Estimated Peak value of Title */
+
+ // true gapless stuff
+ unsigned int IsTrueGapless; /**< is true gapless used? */
+ unsigned int LastFrameSamples; /**< number of valid samples within last frame */
+} StreamInfoMpc;
+
+/** \def StreamInfo is only for compatible before 0.4alpha4
+ \deprecated removed in 0.5
+*/
+#define StreamInfo StreamInfoMpc
+
+/**
+ Read all mpc info from filename
+
+ \param fn File name
+ \param Info StreamInfoMpc Structure for all information
+ \retval 0 ok
+ \retval 1 file not found or write protected
+ \retval 2 not musepack audio file
+*/
+int
+info_mpc_read(const char *fn, StreamInfoMpc *Info);
+
+#endif /* INFO_MPC_H */
diff --git a/src/libapetag/is_tag.c b/src/libapetag/is_tag.c
new file mode 100755
index 0000000..dc56023
--- /dev/null
+++ b/src/libapetag/is_tag.c
@@ -0,0 +1,175 @@
+/********************************************************************
+*
+* Copyright (c) 2002 Artur Polaczynski (Ar't) All rights reserved.
+* <artii@o2.pl> LGPL-2.1
+* $ArtId: is_tag.c,v 1.10 2003/04/16 21:06:27 art Exp $
+********************************************************************/
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1
+ * 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <stdarg.h>
+#include <string.h>
+#include <limits.h>
+#include <assert.h>
+#include "is_tag.h"
+
+unsigned long
+is_tag_ape2long (unsigned char *p);
+
+/*
+ PL: czy dany plik ma taga odpowiednio id3v1, id3v2 i ape ???
+ PL: nie zmienia pozycji w pliku !!!
+*/
+
+/**
+ return size of all id3v1 tags (some bugy tagers add this again and
+ again tag) 0 no tag at all
+
+ \param fp File pointer
+ \return Return size of id3v1 tag (in bytes) 0 no tag at all
+*/
+int
+is_id3v1 (FILE * fp)
+{
+ int n=0;
+ char buf[16];
+ size_t savedFilePosition;
+
+ savedFilePosition = ftell (fp);
+ fseek (fp, 0, SEEK_END);
+ do {
+ n++;
+ memset (buf, 0, sizeof (buf));
+ fseek (fp, ((-128)*n) - 3 , SEEK_END);
+ fread (&buf, 1, sizeof (buf), fp);
+ if (memcmp (buf, "APETAGEX",8) == 0) /*APE.TAG.EX*/
+ break;
+ } while (memcmp (buf+3, "TAG", 3) == 0);
+
+ fseek (fp, savedFilePosition, SEEK_SET);
+ return (n-1)*128;
+}
+
+/**
+ return size of tag id3v2 on begining of file.
+ check for buggy tagers (2 or more tags)
+
+ \param fp File pointer
+ \return Return size of id3v2 tag (in bytes)
+ (some bugy tagers add this again and again ) 0 no tag at all
+*/
+int
+is_id3v2 (FILE * fp)
+{
+ char buf[16];
+ size_t savedFilePosition;
+ long id3v2size=0;
+
+ savedFilePosition = ftell (fp);
+ fseek (fp, 0, SEEK_SET);
+ do {
+ memset (buf, 0, sizeof (buf));
+ fseek (fp, id3v2size, SEEK_SET);
+ fread (&buf, 1, sizeof (buf), fp);
+ if (memcmp (buf, "ID3", 3) != 0) {
+ break;
+ }
+ /* ID3v2 tag skipeer $49 44 33 yy yy xx zz zz zz zz [zz size + this 10 bytes] */
+ id3v2size += 10 + (((long) (buf[9])) | ((long) (buf[8]) << 7) |
+ ((long) (buf[7]) << 14) | ((long) (buf[6]) << 21));
+ } while(memcmp (buf, "ID3", 3) == 0);
+
+ fseek (fp, savedFilePosition, SEEK_SET);
+ return (int) id3v2size;
+}
+
+
+/**
+ return 0 or 1000 or 2000 this is version of ape tag 0 no tag
+
+ \param fp File pointer
+ \return Version of ape tag if any, else 0
+*/
+int
+is_ape_ver (FILE * fp)
+{
+ char unsigned buf[32];
+ size_t savedFilePosition;
+
+ savedFilePosition = ftell (fp);
+ memset (buf, 0, sizeof (buf));
+
+ fseek (fp, (is_id3v1 (fp) ? -32 - 128 : -32), SEEK_END);
+ fread (&buf, 1, sizeof (buf), fp);
+ if (memcmp (buf, "APETAGEX", 8) != 0) {
+ fseek (fp, savedFilePosition, SEEK_SET);
+ return 0;
+ }
+
+ fseek (fp, savedFilePosition, SEEK_SET);
+ return (int) is_tag_ape2long (buf + 8);
+}
+
+#define IS_TAG_FOOTER_NOT 0x40000000
+
+/**
+ return size of ape tag id3v1 is not counting
+
+ \param fp File pointer
+ \return Size of ape tag if any, else 0
+*/
+int
+is_ape (FILE * fp)
+{
+ char unsigned buf[32];
+ size_t savedFilePosition;
+
+ savedFilePosition = ftell (fp);
+ memset (buf, 0, sizeof (buf));
+
+ fseek (fp, (is_id3v1 (fp) ? -32 - 128 : -32), SEEK_END);
+ fread (&buf, 1, sizeof (buf), fp);
+ if (memcmp (buf, "APETAGEX", 8) != 0) {
+ fseek (fp, savedFilePosition, SEEK_SET);
+ return 0;
+ }
+
+ fseek (fp, savedFilePosition, SEEK_SET);
+ /* WARNING! macabra code */
+ return (int) (is_tag_ape2long (buf + 8 + 4) +
+ (
+ ( (is_tag_ape2long (buf + 8) == 2000) &&
+ !(is_tag_ape2long (buf + 8 + 4 + 8) & IS_TAG_FOOTER_NOT)
+ ) ? 32 : 0
+ ) /* footer size = 32 */
+ );
+}
+
+
+unsigned long
+is_tag_ape2long (unsigned char *p)
+{
+
+ return (((unsigned long) p[0] << 0) |
+ ((unsigned long) p[1] << 8) |
+ ((unsigned long) p[2] << 16) |
+ ((unsigned long) p[3] << 24) );
+
+}
diff --git a/src/libapetag/is_tag.h b/src/libapetag/is_tag.h
new file mode 100755
index 0000000..bb04061
--- /dev/null
+++ b/src/libapetag/is_tag.h
@@ -0,0 +1,41 @@
+/********************************************************************
+*
+* Copyright (c) 2002 Artur Polaczynski (Ar't) All rights reserved.
+* <artii@o2.pl> LGPL-2.1
+* $ArtId: is_tag.h,v 1.7 2003/04/13 11:24:10 art Exp $
+********************************************************************/
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1
+ * 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#ifndef _IS_TAG_H
+#define _IS_TAG_H
+
+/** \file is_tag.h
+ \brief Function for check if tag is avilable
+
+ All is_* function restore file positon on return
+*/
+
+int is_id3v1 (FILE * fp);
+
+int is_id3v2 (FILE * fp);
+
+int is_ape (FILE * fp);
+
+int is_ape_ver (FILE * fp);
+
+#endif /* _IS_TAG_H */
diff --git a/src/libmpg123/Makefile.am b/src/libmpg123/Makefile.am
new file mode 100755
index 0000000..697fa03
--- /dev/null
+++ b/src/libmpg123/Makefile.am
@@ -0,0 +1,24 @@
+CFLAGS = @CFLAGS@ @GTK_CFLAGS@
+
+noinst_LIBRARIES = libmpg123.a
+
+INCLUDES = -DLOCALE=\"$(localedir)\"
+
+
+libmpg123_a_SOURCES = \
+ common.c \
+ dxhead.h \
+ dxhead.c \
+ getbits.c \
+ getbits.h \
+ huffman.h \
+ l2tables.h \
+ layer1.c \
+ layer2.c \
+ layer3.c \
+ mpg123.c \
+ mpg123.h
+
+
+EXTRA_DIST = \
+ README
diff --git a/src/libmpg123/Makefile.in b/src/libmpg123/Makefile.in
new file mode 100644
index 0000000..203fdc7
--- /dev/null
+++ b/src/libmpg123/Makefile.in
@@ -0,0 +1,476 @@
+# Makefile.in generated by automake 1.9.6 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005 Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+top_builddir = ../..
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+INSTALL = @INSTALL@
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = src/libmpg123
+DIST_COMMON = README $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/configure.in
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+LIBRARIES = $(noinst_LIBRARIES)
+ARFLAGS = cru
+libmpg123_a_AR = $(AR) $(ARFLAGS)
+libmpg123_a_LIBADD =
+am_libmpg123_a_OBJECTS = common.$(OBJEXT) dxhead.$(OBJEXT) \
+ getbits.$(OBJEXT) layer1.$(OBJEXT) layer2.$(OBJEXT) \
+ layer3.$(OBJEXT) mpg123.$(OBJEXT)
+libmpg123_a_OBJECTS = $(am_libmpg123_a_OBJECTS)
+DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(LIBTOOL) --tag=CC --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+SOURCES = $(libmpg123_a_SOURCES)
+DIST_SOURCES = $(libmpg123_a_SOURCES)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMDEP_FALSE = @AMDEP_FALSE@
+AMDEP_TRUE = @AMDEP_TRUE@
+AMTAR = @AMTAR@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@ @GTK_CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+ECHO = @ECHO@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+F77 = @F77@
+FFLAGS = @FFLAGS@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GOBJECT_QUERY = @GOBJECT_QUERY@
+GTK_CFLAGS = @GTK_CFLAGS@
+GTK_LIBS = @GTK_LIBS@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAINTAINER_MODE_FALSE = @MAINTAINER_MODE_FALSE@
+MAINTAINER_MODE_TRUE = @MAINTAINER_MODE_TRUE@
+MAKEINFO = @MAKEINFO@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MSGFMT = @MSGFMT@
+OBJEXT = @OBJEXT@
+OGG_CFLAGS = @OGG_CFLAGS@
+OGG_LIBS = @OGG_LIBS@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PKG_CONFIG = @PKG_CONFIG@
+POFILES = @POFILES@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+RANLIB = @RANLIB@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+VORBIS_CFLAGS = @VORBIS_CFLAGS@
+VORBIS_LIBS = @VORBIS_LIBS@
+WAVPACK_CFLAGS = @WAVPACK_CFLAGS@
+WAVPACK_LIBS = @WAVPACK_LIBS@
+XGETTEXT = @XGETTEXT@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_F77 = @ac_ct_F77@
+ac_ct_RANLIB = @ac_ct_RANLIB@
+ac_ct_STRIP = @ac_ct_STRIP@
+ac_pt_PKG_CONFIG = @ac_pt_PKG_CONFIG@
+am__fastdepCC_FALSE = @am__fastdepCC_FALSE@
+am__fastdepCC_TRUE = @am__fastdepCC_TRUE@
+am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@
+am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+datadir = @datadir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+noinst_LIBRARIES = libmpg123.a
+INCLUDES = -DLOCALE=\"$(localedir)\"
+libmpg123_a_SOURCES = \
+ common.c \
+ dxhead.h \
+ dxhead.c \
+ getbits.c \
+ getbits.h \
+ huffman.h \
+ l2tables.h \
+ layer1.c \
+ layer2.c \
+ layer3.c \
+ mpg123.c \
+ mpg123.h
+
+EXTRA_DIST = \
+ README
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
+ && exit 0; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/libmpg123/Makefile'; \
+ cd $(top_srcdir) && \
+ $(AUTOMAKE) --foreign src/libmpg123/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+clean-noinstLIBRARIES:
+ -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES)
+libmpg123.a: $(libmpg123_a_OBJECTS) $(libmpg123_a_DEPENDENCIES)
+ -rm -f libmpg123.a
+ $(libmpg123_a_AR) libmpg123.a $(libmpg123_a_OBJECTS) $(libmpg123_a_LIBADD)
+ $(RANLIB) libmpg123.a
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/common.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dxhead.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/getbits.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/layer1.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/layer2.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/layer3.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mpg123.Po@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ if $(LTCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Plo"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+distclean-libtool:
+ -rm -f libtool
+uninstall-info-am:
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$tags $$unique; \
+ fi
+ctags: CTAGS
+CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ test -z "$(CTAGS_ARGS)$$tags$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$tags $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && cd $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) $$here
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \
+ list='$(DISTFILES)'; for file in $$list; do \
+ case $$file in \
+ $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \
+ $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \
+ esac; \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test "$$dir" != "$$file" && test "$$dir" != "."; then \
+ dir="/$$dir"; \
+ $(mkdir_p) "$(distdir)$$dir"; \
+ else \
+ dir=''; \
+ fi; \
+ if test -d $$d/$$file; then \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
+ fi; \
+ cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
+ else \
+ test -f $(distdir)/$$file \
+ || cp -p $$d/$$file $(distdir)/$$file \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LIBRARIES)
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-noinstLIBRARIES \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-libtool distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-exec-am:
+
+install-info: install-info-am
+
+install-man:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-info-am
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
+ clean-libtool clean-noinstLIBRARIES ctags distclean \
+ distclean-compile distclean-generic distclean-libtool \
+ distclean-tags distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am install-exec \
+ install-exec-am install-info install-info-am install-man \
+ install-strip installcheck installcheck-am installdirs \
+ maintainer-clean maintainer-clean-generic mostlyclean \
+ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \
+ pdf pdf-am ps ps-am tags uninstall uninstall-am \
+ uninstall-info-am
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/src/libmpg123/README b/src/libmpg123/README
new file mode 100644
index 0000000..1a66a66
--- /dev/null
+++ b/src/libmpg123/README
@@ -0,0 +1,35 @@
+/*
+ * Code taken from :
+ */
+
+/*
+ * Mpeg Layer-3 audio decoder
+ * --------------------------
+ * copyright (c) 1995-1999 by Michael Hipp.
+ * All rights reserved.
+ *
+ */
+
+/*
+ * and :
+ */
+
+/* XMMS - Cross-platform multimedia player
+ * Copyright (C) 1998-2000 Peter Alm, Mikael Alm, Olle Hallnas, Thomas Nilsson and 4Front Technologies
+ * Copyright (C) 1999,2000 Håvard Kvålen
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
diff --git a/src/libmpg123/common.c b/src/libmpg123/common.c
new file mode 100755
index 0000000..80c299d
--- /dev/null
+++ b/src/libmpg123/common.c
@@ -0,0 +1,746 @@
+/***
+#include <ctype.h>
+#include <stdlib.h>
+#include <signal.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "config.h"
+***/
+
+/* #ifdef HAVE_MMAP */
+/***
+# if 0
+#include <sys/mman.h>
+#ifndef MAP_FAILED
+#define MAP_FAILED ( (void *) -1 )
+#endif
+#endif
+***/
+
+#include "mpg123.h"
+/***
+#include "id3.h"
+#include "id3_header.h"
+***/
+
+/* max = 1728 */
+#define MAXFRAMESIZE 1792
+
+int tabsel_123[2][3][16] =
+{
+ {
+ {0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448,},
+ {0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384,},
+ {0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320,}},
+
+ {
+ {0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256,},
+ {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160,},
+ {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160,}}
+};
+
+long mpg123_freqs[9] =
+{44100, 48000, 32000, 22050, 24000, 16000, 11025, 12000, 8000};
+
+struct bitstream_info bsi;
+
+extern gint mpg123_bitrate, mpg123_frequency, mpg123_length;
+extern gchar *mpg123_title, *mpg123_filename;
+extern gboolean mpg123_stereo;
+
+static int fsizeold = 0, ssize;
+static unsigned char bsspace[2][MAXFRAMESIZE + 512]; /* MAXFRAMESIZE */
+static unsigned char *bsbuf = bsspace[1], *bsbufold;
+///static int bsnum = 0;
+
+unsigned char *mpg123_pcm_sample;
+int mpg123_pcm_point = 0;
+
+static FILE *filept;
+///static int filept_opened;
+
+///static int get_fileinfo(void);
+
+/***
+static int fullread(FILE * fd, unsigned char *buf, int count)
+{
+ int ret, cnt = 0;
+
+ while (cnt < count)
+ {
+ if (fd)
+ ret = fread(buf + cnt, 1, count - cnt, fd);
+ else
+ ret = mpg123_http_read(buf + cnt, count - cnt);
+ if (ret < 0)
+ return ret;
+ if (ret == 0)
+ break;
+ cnt += ret;
+ }
+ return cnt;
+}
+
+#define HDRCMPMASK 0xfffffd00
+***/
+
+/* #ifdef HAVE_MMAP */
+# if 0
+/***
+static unsigned char *mapbuf;
+static unsigned char *mappnt;
+static unsigned char *mapend;
+
+static int stream_init(void)
+{
+ long len;
+
+ len = get_fileinfo();
+ if (len < 0)
+ return -1;
+
+ mappnt = mapbuf =
+ mmap(NULL, len, PROT_READ, MAP_SHARED, filept, 0);
+ if (!mapbuf || mapbuf == MAP_FAILED)
+ return -1;
+
+ mapend = mapbuf + len;
+
+ return 0;
+}
+
+static void stream_rewind(void)
+{
+ mappnt = mapbuf;
+}
+
+void mpg123_stream_close(void)
+{
+ if (filept)
+ {
+ munmap(mapbuf, mapend - mapbuf);
+ close(filept);
+ }
+ else
+ mpg123_http_close();
+
+}
+
+static int stream_head_read(unsigned long *newhead)
+{
+ unsigned long nh;
+
+ if (filept)
+ {
+
+ if (mappnt + 4 > mapend)
+ return FALSE;
+
+ nh = (*mappnt++) << 24;
+ nh |= (*mappnt++) << 16;
+ nh |= (*mappnt++) << 8;
+ nh |= (*mappnt++);
+
+ *newhead = nh;
+
+ }
+ else
+ {
+ unsigned char hbuf[4];
+
+ if (fullread(filept, hbuf, 4) != 4)
+ return FALSE;
+
+ *newhead = ((unsigned long) hbuf[0] << 24) |
+ ((unsigned long) hbuf[1] << 16) |
+ ((unsigned long) hbuf[2] << 8) |
+ (unsigned long) hbuf[3];
+ }
+ return TRUE;
+
+}
+
+static int stream_head_shift(unsigned long *head)
+{
+
+ if (filept)
+ {
+ if (mappnt + 1 > mapend)
+ return FALSE;
+ *head <<= 8;
+ *head |= *mappnt++;
+ *head &= 0xffffffff;
+ }
+ else
+ {
+ unsigned char hbuf;
+
+ if (fullread(filept, &hbuf, 1) != 1)
+ return 0;
+ *head <<= 8;
+ *head |= hbuf;
+ *head &= 0xffffffff;
+ }
+ return TRUE;
+}
+
+static int stream_mpg123_read_frame_body(unsigned char *buf,
+ int size)
+{
+ if (filept)
+ {
+#if 1
+ if (mappnt + size > mapend)
+ return FALSE;
+#else
+ long l;
+
+ if (size > (mapend - mappnt))
+ {
+ l = mapend - mappnt;
+ memcpy(buf, mappnt, l);
+ memset(buf + l, 0, size - l);
+ }
+ else
+#endif
+ memcpy(buf, mappnt, size);
+
+ mappnt += size;
+ }
+ else
+ {
+ long l;
+
+ if ((l = fullread(filept, buf, size)) != size)
+ {
+ if (l <= 0)
+ return 0;
+ memset(buf + l, 0, size - l);
+ }
+ }
+
+ return TRUE;
+}
+
+static int stream_back_bytes(int bytes)
+{
+ if ((mappnt - bytes) < mapbuf || (mappnt - bytes + 4) > mapend)
+ return -1;
+ mappnt -= bytes;
+ return 0;
+}
+
+static long stream_tell(void)
+{
+ return mappnt - mapbuf;
+}
+
+void mpg123_stream_jump_to_frame(struct frame *fr, int frame)
+{
+ if (mapbuf + frame * (fr->framesize + 4) < mapend)
+ {
+ mpg123__init();
+ stream_rewind();
+ mpg123_read_frame(fr);
+ mappnt = mapbuf + frame * (fr->framesize + 4);
+
+ mpg123_read_frame(fr);
+ }
+}
+
+void mpg123_stream_jump_to_byte(struct frame *fr, int byte)
+{
+ if (mapbuf + byte < mapend)
+ {
+ mappnt = mapbuf + byte;
+ mpg123_read_frame(fr);
+ }
+}
+
+int mpg123_stream_check_for_xing_header(struct frame *fr, XHEADDATA * xhead)
+{
+ unsigned char *head_data;
+ int ret = 0;
+
+ stream_back_bytes(fr->framesize + 4);
+
+ if (mappnt + (fr->framesize + 4) < mapend)
+ {
+ ret = mpg123_get_xing_header(xhead, mappnt);
+ mappnt += fr->framesize + 4;
+ }
+ return ret;
+}
+***/
+#else
+/***
+static int stream_init(void)
+{
+ if (get_fileinfo() < 0)
+ return -1;
+ return 0;
+}
+
+void mpg123_stream_close(void)
+{
+*//* if (flags & READER_FD_OPENED) *//*
+ if (filept)
+ fclose(filept);
+ else
+ mpg123_http_close();
+}
+***/
+
+/****************************************
+ * HACK,HACK,HACK: step back <num> frames
+ * can only work if the 'stream' isn't a real stream but a file
+ */
+/***
+static int stream_back_bytes(int bytes)
+{
+ if (fseek(filept, -bytes, SEEK_CUR) < 0)
+ return -1;
+ return 0;
+}
+
+static int stream_head_read(unsigned long *newhead)
+{
+ unsigned char hbuf[4];
+
+ if (fullread(filept, hbuf, 4) != 4)
+ return FALSE;
+
+ *newhead = ((unsigned long) hbuf[0] << 24) |
+ ((unsigned long) hbuf[1] << 16) |
+ ((unsigned long) hbuf[2] << 8) |
+ (unsigned long) hbuf[3];
+
+ return TRUE;
+}
+
+static int stream_head_shift(unsigned long *head)
+{
+ unsigned char hbuf;
+
+ if (fullread(filept, &hbuf, 1) != 1)
+ return 0;
+ *head <<= 8;
+ *head |= hbuf;
+ *head &= 0xffffffff;
+ return 1;
+}
+
+static int stream_mpg123_read_frame_body(unsigned char *buf,
+ int size)
+{
+ long l;
+
+ if ((l = fullread(filept, buf, size)) != size)
+ {
+ if (l <= 0)
+ return 0;
+ memset(buf + l, 0, size - l);
+ }
+ return 1;
+}
+
+static long stream_tell(void)
+{
+ return ftell(filept);
+}
+
+static void stream_rewind(void)
+{
+ fseek(filept, 0, SEEK_SET);
+}
+
+int mpg123_stream_jump_to_frame(struct frame *fr, int frame)
+{
+ if (!filept)
+ return -1;
+ mpg123_read_frame_init();
+ fseek(filept, frame * (fr->framesize + 4), SEEK_SET);
+ mpg123_read_frame(fr);
+ return 0;
+}
+
+int mpg123_stream_jump_to_byte(struct frame *fr, int byte)
+{
+ if (!filept)
+ return -1;
+ fseek(filept, byte, SEEK_SET);
+ mpg123_read_frame(fr);
+ return 0;
+}
+***/
+
+int mpg123_stream_check_for_xing_header(struct frame *fr, XHEADDATA * xhead)
+{
+ unsigned char *head_data;
+ int ret;
+
+ fseek(filept, -(fr->framesize + 4), SEEK_CUR);
+ head_data = g_malloc(fr->framesize + 4);
+ fread(head_data, 1, fr->framesize + 4, filept);
+ ret = mpg123_get_xing_header(xhead, head_data);
+ g_free(head_data);
+ return ret;
+}
+
+#endif
+
+/***
+static int get_fileinfo(void)
+{
+ guchar buf[3];
+
+ if (filept == NULL)
+ return -1;
+ if (fseek(filept, 0, SEEK_END) < 0)
+ return -1;
+
+ mpg123_info->filesize = ftell(filept);
+ if (fseek(filept, -128, SEEK_END) < 0)
+ return -1;
+ if (fullread(filept, buf, 3) != 3)
+ return -1;
+ if (!strncmp(buf, "TAG", 3))
+ mpg123_info->filesize -=128;
+ if (fseek(filept, 0, SEEK_SET) < 0)
+ return -1;
+
+ if (mpg123_info->filesize <= 0)
+ return -1;
+
+ return mpg123_info->filesize;
+}
+***/
+
+/***
+void mpg123_read_frame_init(void)
+{
+ memset(bsspace[0],0,MAXFRAMESIZE + 512);
+ memset(bsspace[1],0,MAXFRAMESIZE + 512);
+ mpg123_info->output_audio = FALSE;
+}
+***/
+
+/*
+ * Function read_id3v2_tag (head)
+ *
+ * Read ID3v2 tag from stream. Return TRUE upon success, or FALSE if
+ * an error occurred.
+ *
+ */
+/***
+static gboolean read_id3v2_tag(unsigned long head)
+{
+ struct
+ {
+ char id3[3];
+ struct id3_taghdr_t tag;
+ } id3header;
+ char *id3buf;
+ int hdrsize;
+ id3_t *id3d;
+ struct id3tag_t tag;
+ *//*
+ * Read ID3tag header.
+ *//*
+ *(unsigned long *) &id3header = g_htonl(head);
+ if (fullread(filept, ((char *) &id3header) + sizeof (head),
+ sizeof (id3header) - sizeof (head))
+ != sizeof (id3header) - sizeof (head))
+ return FALSE;
+
+ hdrsize = ID3_GET_SIZE28(g_ntohl(id3header.tag.th_size));
+
+ *//*
+ * A invalid header could fool us into requesting insane
+ * amounts of memory. Make sure the header size is
+ * reasonable.
+ *//*
+ if ((mpg123_info->filesize && hdrsize > mpg123_info->filesize) ||
+ (!mpg123_info->filesize && hdrsize > 1000000))
+ return FALSE;
+
+ if (mpg123_cfg.disable_id3v2)
+ {
+ guint8 *tmp = g_malloc(hdrsize);
+ gboolean ret;
+ ret = (fullread(filept, tmp, hdrsize) == hdrsize);
+ g_free(tmp);
+ return ret;
+ }
+
+ id3buf = g_malloc(hdrsize + sizeof (id3header));
+ memcpy(id3buf, &id3header, sizeof (id3header));
+
+ *//*
+ * Read ID3tag body.
+ *//*
+ if (fullread(filept, id3buf + sizeof (id3header), hdrsize) != hdrsize)
+ {
+ g_free(id3buf);
+ return FALSE;
+ }
+
+ *//*
+ * Get info from tag.
+ *//*
+ if ((id3d = id3_open_mem(id3buf, 0)) != NULL)
+ {
+ mpg123_get_id3v2(id3d, &tag);
+ if (!mpg123_info->first_frame)
+ {
+ char *songname = mpg123_title;
+ mpg123_title =
+ mpg123_format_song_title(&tag, mpg123_filename);
+ mpg123_ip.set_info(mpg123_title, mpg123_length,
+ mpg123_bitrate * 1000,
+ mpg123_frequency, mpg123_stereo);
+ if (songname)
+ g_free(songname);
+ }
+ else
+ {
+ mpg123_title = mpg123_format_song_title(&tag,
+ mpg123_filename);
+ }
+ id3_close(id3d);
+ }
+ g_free(id3buf);
+
+ return TRUE;
+}
+***/
+
+int mpg123_head_check(unsigned long head)
+{
+ if ((head & 0xffe00000) != 0xffe00000)
+ return FALSE;
+ if (!((head >> 17) & 3))
+ return FALSE;
+ if (((head >> 12) & 0xf) == 0xf)
+ return FALSE;
+ if (!((head >> 12) & 0xf))
+ return FALSE;
+ if (((head >> 10) & 0x3) == 0x3)
+ return FALSE;
+ if (((head >> 19) & 1) == 1 && ((head >> 17) & 3) == 3 && ((head >> 16) & 1) == 1)
+ return FALSE;
+ if ((head & 0xffff0000) == 0xfffe0000)
+ return FALSE;
+
+ return TRUE;
+}
+
+/*****************************************************************
+ * read next frame
+ */
+/***
+int mpg123_read_frame(struct frame *fr)
+{
+ unsigned long newhead;
+
+ fsizeold = fr->framesize; *//* for Layer3 *//*
+
+ if (!stream_head_read(&newhead))
+ return FALSE;
+
+ if (!mpg123_head_check(newhead) || !mpg123_decode_header(fr, newhead))
+ {
+ int try = 0;
+
+ do
+ {
+ try++;
+ if ((newhead & 0xffffff00) == ('I' << 24) + ('D' << 16) + ('3' << 8))
+ {
+ read_id3v2_tag(newhead);
+ if (!stream_head_read(&newhead))
+ return FALSE;
+ }
+ else if (!stream_head_shift(&newhead))
+ return 0;
+
+ }
+ while ((!mpg123_head_check(newhead) ||
+ !mpg123_decode_header(fr,newhead)) &&
+ try < (256 * 1024));
+ if(try >= (256 * 1024))
+ return FALSE;
+
+ mpg123_info->filesize -= try;
+ }
+ *//* flip/init buffer for Layer 3 *//*
+ bsbufold = bsbuf;
+ bsbuf = bsspace[bsnum] + 512;
+ bsnum = (bsnum + 1) & 1;
+
+ if (!stream_mpg123_read_frame_body(bsbuf, fr->framesize))
+ return 0;
+
+ bsi.bitindex = 0;
+ bsi.wordpointer = (unsigned char *) bsbuf;
+
+
+ return 1;
+
+}
+***/
+
+/*
+ * the code a header and write the information
+ * into the frame structure
+ */
+int mpg123_decode_header(struct frame *fr, unsigned long newhead)
+{
+ if (newhead & (1 << 20))
+ {
+ fr->lsf = (newhead & (1 << 19)) ? 0x0 : 0x1;
+ fr->mpeg25 = 0;
+ }
+ else
+ {
+ fr->lsf = 1;
+ fr->mpeg25 = 1;
+ }
+ fr->lay = 4 - ((newhead >> 17) & 3);
+ if (fr->mpeg25)
+ {
+ fr->sampling_frequency = 6 + ((newhead >> 10) & 0x3);
+ }
+ else
+ fr->sampling_frequency = ((newhead >> 10) & 0x3) + (fr->lsf * 3);
+ fr->error_protection = ((newhead >> 16) & 0x1) ^ 0x1;
+
+ if (fr->mpeg25) /* allow Bitrate change for 2.5 ... */
+ fr->bitrate_index = ((newhead >> 12) & 0xf);
+
+ fr->bitrate_index = ((newhead >> 12) & 0xf);
+ fr->padding = ((newhead >> 9) & 0x1);
+ fr->extension = ((newhead >> 8) & 0x1);
+ fr->mode = ((newhead >> 6) & 0x3);
+ fr->mode_ext = ((newhead >> 4) & 0x3);
+ fr->copyright = ((newhead >> 3) & 0x1);
+ fr->original = ((newhead >> 2) & 0x1);
+ fr->emphasis = newhead & 0x3;
+
+ fr->stereo = (fr->mode == MPG_MD_MONO) ? 1 : 2;
+
+ ssize = 0;
+
+ if (!fr->bitrate_index)
+ return (0);
+
+ switch (fr->lay)
+ {
+ case 1:
+ fr->do_layer = mpg123_do_layer1;
+ mpg123_init_layer2(); /* inits also shared tables with layer1 */
+ fr->framesize = (long) tabsel_123[fr->lsf][0][fr->bitrate_index] * 12000;
+ fr->framesize /= mpg123_freqs[fr->sampling_frequency];
+ fr->framesize = ((fr->framesize + fr->padding) << 2) - 4;
+ break;
+ case 2:
+ fr->do_layer = mpg123_do_layer2;
+ mpg123_init_layer2(); /* inits also shared tables with layer1 */
+ fr->framesize = (long) tabsel_123[fr->lsf][1][fr->bitrate_index] * 144000;
+ fr->framesize /= mpg123_freqs[fr->sampling_frequency];
+ fr->framesize += fr->padding - 4;
+ break;
+ case 3:
+ fr->do_layer = mpg123_do_layer3;
+ if (fr->lsf)
+ ssize = (fr->stereo == 1) ? 9 : 17;
+ else
+ ssize = (fr->stereo == 1) ? 17 : 32;
+ if (fr->error_protection)
+ ssize += 2;
+ fr->framesize = (long) tabsel_123[fr->lsf][2][fr->bitrate_index] * 144000;
+ fr->framesize /= mpg123_freqs[fr->sampling_frequency] << (fr->lsf);
+ fr->framesize = fr->framesize + fr->padding - 4;
+ break;
+ default:
+ return (0);
+ }
+ if(fr->framesize > MAXFRAMESIZE)
+ return 0;
+ return 1;
+}
+
+/***
+void mpg123_open_stream(char *bs_filenam, int fd)
+{
+ filept_opened = 1;
+ if (!strncasecmp(bs_filenam, "http://", 7))
+ {
+ filept = NULL;
+ mpg123_http_open(bs_filenam);
+ mpg123_info->filesize = 0;
+ }
+ else
+ {
+ if ((filept = fopen(bs_filenam, "rb")) != NULL)
+ {
+ if (stream_init() == -1)
+ {
+ mpg123_info->eof = 1;
+ }
+ }
+ else
+ mpg123_info->eof = 1;
+ }
+
+}
+***/
+
+void mpg123_set_pointer(long backstep)
+{
+ bsi.wordpointer = bsbuf + ssize - backstep;
+ if (backstep)
+ memcpy(bsi.wordpointer, bsbufold + fsizeold - backstep, backstep);
+ bsi.bitindex = 0;
+}
+
+double mpg123_compute_bpf(struct frame *fr)
+{
+ double bpf;
+
+ switch (fr->lay)
+ {
+ case 1:
+ bpf = tabsel_123[fr->lsf][0][fr->bitrate_index];
+ bpf *= 12000.0 * 4.0;
+ bpf /= mpg123_freqs[fr->sampling_frequency] << (fr->lsf);
+ break;
+ case 2:
+ case 3:
+ bpf = tabsel_123[fr->lsf][fr->lay - 1][fr->bitrate_index];
+ bpf *= 144000;
+ bpf /= mpg123_freqs[fr->sampling_frequency] << (fr->lsf);
+ break;
+ default:
+ bpf = 1.0;
+ }
+
+ return bpf;
+}
+
+/***
+int mpg123_calc_numframes(struct frame *fr)
+{
+ return (int) (mpg123_info->filesize / mpg123_compute_bpf(fr));
+}
+
+double mpg123_relative_pos(void)
+{
+ if (!filept || !mpg123_info->filesize)
+ return 0;
+ return ((double) stream_tell()) / mpg123_info->filesize;
+}
+***/
diff --git a/src/libmpg123/dxhead.c b/src/libmpg123/dxhead.c
new file mode 100755
index 0000000..21b5ceb
--- /dev/null
+++ b/src/libmpg123/dxhead.c
@@ -0,0 +1,165 @@
+/* ---- DXhead.c --------------------------------------------
+ *
+ *
+ * decoder MPEG Layer III handle Xing header
+ *
+ * mod 12/7/98 add vbr scale
+ *
+ * Copyright 1998 Xing Technology Corp.
+ * -----------------------------------------------------------
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <float.h>
+#include <math.h>
+#include "dxhead.h"
+
+/* 4 Xing
+ * 4 flags
+ * 4 frames
+ * 4 bytes
+ * 100 toc
+ */
+
+/*-------------------------------------------------------------*/
+static int ExtractI4(unsigned char *buf)
+{
+ int x;
+
+ /* big endian extract */
+ x = buf[0];
+ x <<= 8;
+ x |= buf[1];
+ x <<= 8;
+ x |= buf[2];
+ x <<= 8;
+ x |= buf[3];
+ return x;
+}
+
+/*-------------------------------------------------------------*/
+int mpg123_get_xing_header(XHEADDATA * X, unsigned char *buf)
+{
+ int i, head_flags;
+ int h_id, h_mode, h_sr_index;
+ static int sr_table[4] =
+ {44100, 48000, 32000, 99999};
+
+ /* get Xing header data */
+ X->flags = 0; /* clear to null incase fail */
+
+ /* get selected MPEG header data */
+ h_id = (buf[1] >> 3) & 1;
+ h_sr_index = (buf[2] >> 2) & 3;
+ h_mode = (buf[3] >> 6) & 3;
+
+
+
+ /* determine offset of header */
+ if (h_id)
+ { /* mpeg1 */
+ if (h_mode != 3)
+ buf += (32 + 4);
+ else
+ buf += (17 + 4);
+ }
+ else
+ { /* mpeg2 */
+ if (h_mode != 3)
+ buf += (17 + 4);
+ else
+ buf += (9 + 4);
+ }
+
+ if (buf[0] != 'X')
+ return 0; /* fail */
+ if (buf[1] != 'i')
+ return 0; /* header not found */
+ if (buf[2] != 'n')
+ return 0;
+ if (buf[3] != 'g')
+ return 0;
+ buf += 4;
+
+ X->h_id = h_id;
+ X->samprate = sr_table[h_sr_index];
+ if (h_id == 0)
+ X->samprate >>= 1;
+ head_flags = X->flags = ExtractI4(buf);
+ buf += 4; /* get flags */
+
+ if (head_flags & FRAMES_FLAG)
+ {
+ X->frames = ExtractI4(buf);
+ buf += 4;
+ }
+ if (head_flags & BYTES_FLAG)
+ {
+ X->bytes = ExtractI4(buf);
+ buf += 4;
+ }
+
+ if (head_flags & TOC_FLAG)
+ {
+ if (X->toc != NULL)
+ {
+ for (i = 0; i < 100; i++)
+ X->toc[i] = buf[i];
+ }
+ buf += 100;
+ }
+
+ X->vbr_scale = -1;
+ if (head_flags & VBR_SCALE_FLAG)
+ {
+ X->vbr_scale = ExtractI4(buf);
+ buf += 4;
+ }
+
+
+/*if( X->toc != NULL ) {
+ *for(i=0;i<100;i++) {
+ * if( (i%10) == 0 ) printf("\n");
+ * printf(" %3d", (int)(X->toc[i]));
+ *}
+ *}
+ */
+
+ return 1; /* success */
+}
+
+/*-------------------------------------------------------------*/
+int mpg123_seek_point(unsigned char TOC[100], int file_bytes, float percent)
+{
+
+ /* interpolate in TOC to get file seek point in bytes */
+ int a, seekpoint;
+ float fa, fb, fx;
+
+ if (percent < 0.0f)
+ percent = 0.0f;
+
+ if (percent > 100.0f)
+ percent = 100.0f;
+
+ a = (int) percent;
+
+ if (a > 99)
+ a = 99;
+ fa = TOC[a];
+
+ if (a < 99)
+ {
+ fb = TOC[a + 1];
+ }
+ else
+ {
+ fb = 256.0f;
+ }
+
+ fx = fa + (fb - fa) * (percent - a);
+ seekpoint = (int) ((1.0f / 256.0f) * fx * file_bytes);
+ return seekpoint;
+}
+
+/*-------------------------------------------------------------*/
diff --git a/src/libmpg123/dxhead.h b/src/libmpg123/dxhead.h
new file mode 100755
index 0000000..bcbc66a
--- /dev/null
+++ b/src/libmpg123/dxhead.h
@@ -0,0 +1,60 @@
+/*---- DXhead.h --------------------------------------------
+
+decoder MPEG Layer III
+handle Xing header
+
+Copyright 1998 Xing Technology Corp.
+-----------------------------------------------------------*/
+/* A Xing header may be present in the ancillary
+ * data field of the first frame of an mp3 bitstream
+ * The Xing header (optionally) contains
+ * frames total number of audio frames in the bitstream
+ * bytes total number of bytes in the bitstream
+ * toc table of contents
+
+ * toc (table of contents) gives seek points
+ * for random access
+ * the ith entry determines the seek point for
+ * i-percent duration
+ * seek point in bytes = (toc[i]/256.0) * total_bitstream_bytes
+ * e.g. half duration seek point = (toc[50]/256.0) * total_bitstream_bytes
+ */
+
+#define FRAMES_FLAG 0x0001
+#define BYTES_FLAG 0x0002
+#define TOC_FLAG 0x0004
+#define VBR_SCALE_FLAG 0x0008
+
+#define FRAMES_AND_BYTES (FRAMES_FLAG | BYTES_FLAG)
+
+/* structure to receive extracted header
+ * toc may be NULL
+ */
+typedef struct
+{
+ int h_id; /* from MPEG header, 0=MPEG2, 1=MPEG1 */
+ int samprate; /* determined from MPEG header */
+ int flags; /* from Xing header data */
+ int frames; /* total bit stream frames from Xing header data */
+ int bytes; /* total bit stream bytes from Xing header data */
+ int vbr_scale; /* encoded vbr scale from Xing header data */
+ unsigned char *toc; /* pointer to unsigned char toc_buffer[100] */
+ /* may be NULL if toc not desired */
+}
+XHEADDATA;
+
+int mpg123_get_xing_header(XHEADDATA * X, unsigned char *buf);
+
+/* return 0=fail, 1=success
+ * X structure to receive header data (output)
+ * buf bitstream input
+ */
+
+int mpg123_seek_point(unsigned char TOC[100], int file_bytes, float percent);
+
+/* return seekpoint in bytes (may be at eof if percent=100.0)
+ * TOC = table of contents from Xing header
+ * file_bytes = number of bytes in mp3 file
+ * percent = play time percentage of total playtime. May be
+ * fractional (e.g. 87.245)
+ */
diff --git a/src/libmpg123/getbits.c b/src/libmpg123/getbits.c
new file mode 100755
index 0000000..a071b5e
--- /dev/null
+++ b/src/libmpg123/getbits.c
@@ -0,0 +1,126 @@
+#include "mpg123.h"
+
+#if 0
+static void check_buffer_range(int size)
+{
+ int pos = (bsi.wordpointer-bsbuf) + (size >> 3);
+
+ if( pos >= fsizeold) {
+ fprintf(stderr, "Pointer out of range (%d,%d)!\n", pos, fsizeold);
+ }
+}
+#endif
+
+void mpg123_backbits(int number_of_bits)
+{
+ bsi.bitindex -= number_of_bits;
+ bsi.wordpointer += (bsi.bitindex>>3);
+ bsi.bitindex &= 0x7;
+}
+
+int mpg123_getbitoffset(void)
+{
+ return (-bsi.bitindex)&0x7;
+}
+
+int mpg123_getbyte(void)
+{
+#ifdef DEBUG_GETBITS
+ if(bsi.bitindex)
+ fprintf(stderr,"getbyte called unsynched!\n");
+#endif
+ return *bsi.wordpointer++;
+}
+
+unsigned int mpg123_getbits(int number_of_bits)
+{
+ unsigned long rval;
+
+#ifdef DEBUG_GETBITS
+ fprintf(stderr, "g%d", number_of_bits);
+#endif
+
+ if(!number_of_bits)
+ return 0;
+
+#if 0
+ check_buffer_range(number_of_bits + bsi.bitindex);
+#endif
+
+ {
+ rval = bsi.wordpointer[0];
+ rval <<= 8;
+ rval |= bsi.wordpointer[1];
+ rval <<= 8;
+ rval |= bsi.wordpointer[2];
+
+ rval <<= bsi.bitindex;
+ rval &= 0xffffff;
+
+ bsi.bitindex += number_of_bits;
+
+ rval >>= (24-number_of_bits);
+
+ bsi.wordpointer += (bsi.bitindex >> 3);
+ bsi.bitindex &= 7;
+ }
+
+#ifdef DEBUG_GETBITS
+ fprintf(stderr,":%x ",rval);
+#endif
+
+ return rval;
+}
+
+unsigned int mpg123_getbits_fast(int number_of_bits)
+{
+ unsigned int rval;
+#ifdef DEBUG_GETBITS
+ fprintf(stderr,"g%d",number_of_bits);
+#endif
+
+#if 0
+ check_buffer_range(number_of_bits+bsi.bitindex);
+#endif
+
+ rval = (unsigned char) (bsi.wordpointer[0] << bsi.bitindex);
+ rval |= ((unsigned int) bsi.wordpointer[1] << bsi.bitindex) >> 8;
+ rval <<= number_of_bits;
+ rval >>= 8;
+
+ bsi.bitindex += number_of_bits;
+
+ bsi.wordpointer += (bsi.bitindex >> 3);
+ bsi.bitindex &= 7;
+
+#ifdef DEBUG_GETBITS
+ fprintf(stderr,":%x ",rval);
+#endif
+ return rval;
+}
+
+unsigned int mpg123_get1bit(void)
+{
+ unsigned char rval;
+
+#ifdef DEBUG_GETBITS
+ fprintf(stderr,"g%d",1);
+#endif
+
+#if 0
+ check_buffer_range(1+bsi.bitindex);
+#endif
+
+ rval = *bsi.wordpointer << bsi.bitindex;
+
+ bsi.bitindex++;
+ bsi.wordpointer += (bsi.bitindex >> 3);
+ bsi.bitindex &= 7;
+
+#ifdef DEBUG_GETBITS
+ fprintf(stderr,":%d ",rval >> 7);
+#endif
+
+ return rval>>7;
+}
+
diff --git a/src/libmpg123/getbits.h b/src/libmpg123/getbits.h
new file mode 100755
index 0000000..1e5cf65
--- /dev/null
+++ b/src/libmpg123/getbits.h
@@ -0,0 +1,46 @@
+
+/*
+ * This does the same as getbits.c but with defines to
+ * force inlining
+ */
+
+#define mpg123_backbits(nob) \
+do { \
+ bsi.bitindex -= nob; \
+ bsi.wordpointer += (bsi.bitindex >> 3); \
+ bsi.bitindex &= 0x7; \
+} while (0)
+
+#define mpg123_getbitoffset() ((-bsi.bitindex) & 0x7)
+#define mpg123_getbyte() (*bsi.wordpointer++)
+
+#define mpg123_getbits(nob) \
+ (rval = bsi.wordpointer[0], \
+ rval <<= 8, \
+ rval |= bsi.wordpointer[1], \
+ rval <<= 8, \
+ rval |= bsi.wordpointer[2], \
+ rval <<= bsi.bitindex, \
+ rval &= 0xffffff, \
+ bsi.bitindex += (nob), \
+ rval >>= (24-(nob)), \
+ bsi.wordpointer += (bsi.bitindex>>3), \
+ bsi.bitindex &= 7, \
+ rval)
+
+#define mpg123_getbits_fast(nob) \
+ (rval = (unsigned char) (bsi.wordpointer[0] << bsi.bitindex), \
+ rval |= ((unsigned long) bsi.wordpointer[1] << bsi.bitindex) >> 8, \
+ rval <<= (nob), \
+ rval >>= 8, \
+ bsi.bitindex += (nob), \
+ bsi.wordpointer += (bsi.bitindex >> 3), \
+ bsi.bitindex &= 7, \
+ rval)
+
+#define mpg123_get1bit() \
+ (rval_uc = *bsi.wordpointer << bsi.bitindex, \
+ bsi.bitindex++, \
+ bsi.wordpointer += (bsi.bitindex>>3), \
+ bsi.bitindex &= 7, \
+ rval_uc >> 7)
diff --git a/src/libmpg123/huffman.h b/src/libmpg123/huffman.h
new file mode 100755
index 0000000..83a230b
--- /dev/null
+++ b/src/libmpg123/huffman.h
@@ -0,0 +1,329 @@
+
+/*
+ * huffman tables ... recalcualted to work with my optimzed
+ * decoder scheme (MH)
+ *
+ * probably we could save a few bytes of memory, because the
+ * smaller tables are often the part of a bigger table
+ */
+
+struct newhuff
+{
+ unsigned int linbits;
+ short *table;
+};
+
+static short tab0[] =
+{
+ 0
+};
+
+static short tab1[] =
+{
+ -5, -3, -1, 17, 1, 16, 0
+};
+
+static short tab2[] =
+{
+ -15, -11, -9, -5, -3, -1, 34, 2, 18, -1, 33, 32, 17, -1, 1,
+ 16, 0
+};
+
+static short tab3[] =
+{
+ -13, -11, -9, -5, -3, -1, 34, 2, 18, -1, 33, 32, 16, 17, -1,
+ 1, 0
+};
+
+static short tab5[] =
+{
+ -29, -25, -23, -15, -7, -5, -3, -1, 51, 35, 50, 49, -3, -1, 19,
+ 3, -1, 48, 34, -3, -1, 18, 33, -1, 2, 32, 17, -1, 1, 16,
+ 0
+};
+
+static short tab6[] =
+{
+ -25, -19, -13, -9, -5, -3, -1, 51, 3, 35, -1, 50, 48, -1, 19,
+ 49, -3, -1, 34, 2, 18, -3, -1, 33, 32, 1, -1, 17, -1, 16,
+ 0
+};
+
+static short tab7[] =
+{
+ -69, -65, -57, -39, -29, -17, -11, -7, -3, -1, 85, 69, -1, 84, 83,
+ -1, 53, 68, -3, -1, 37, 82, 21, -5, -1, 81, -1, 5, 52, -1,
+ 80, -1, 67, 51, -5, -3, -1, 36, 66, 20, -1, 65, 64, -11, -7,
+ -3, -1, 4, 35, -1, 50, 3, -1, 19, 49, -3, -1, 48, 34, 18,
+ -5, -1, 33, -1, 2, 32, 17, -1, 1, 16, 0
+};
+
+static short tab8[] =
+{
+ -65, -63, -59, -45, -31, -19, -13, -7, -5, -3, -1, 85, 84, 69, 83,
+ -3, -1, 53, 68, 37, -3, -1, 82, 5, 21, -5, -1, 81, -1, 52,
+ 67, -3, -1, 80, 51, 36, -5, -3, -1, 66, 20, 65, -3, -1, 4,
+ 64, -1, 35, 50, -9, -7, -3, -1, 19, 49, -1, 3, 48, 34, -1,
+ 2, 32, -1, 18, 33, 17, -3, -1, 1, 16, 0
+};
+
+static short tab9[] =
+{
+ -63, -53, -41, -29, -19, -11, -5, -3, -1, 85, 69, 53, -1, 83, -1,
+ 84, 5, -3, -1, 68, 37, -1, 82, 21, -3, -1, 81, 52, -1, 67,
+ -1, 80, 4, -7, -3, -1, 36, 66, -1, 51, 64, -1, 20, 65, -5,
+ -3, -1, 35, 50, 19, -1, 49, -1, 3, 48, -5, -3, -1, 34, 2,
+ 18, -1, 33, 32, -3, -1, 17, 1, -1, 16, 0
+};
+
+static short tab10[] =
+{
+ -125, -121, -111, -83, -55, -35, -21, -13, -7, -3, -1, 119, 103, -1, 118,
+ 87, -3, -1, 117, 102, 71, -3, -1, 116, 86, -1, 101, 55, -9, -3,
+ -1, 115, 70, -3, -1, 85, 84, 99, -1, 39, 114, -11, -5, -3, -1,
+ 100, 7, 112, -1, 98, -1, 69, 53, -5, -1, 6, -1, 83, 68, 23,
+ -17, -5, -1, 113, -1, 54, 38, -5, -3, -1, 37, 82, 21, -1, 81,
+ -1, 52, 67, -3, -1, 22, 97, -1, 96, -1, 5, 80, -19, -11, -7,
+ -3, -1, 36, 66, -1, 51, 4, -1, 20, 65, -3, -1, 64, 35, -1,
+ 50, 3, -3, -1, 19, 49, -1, 48, 34, -7, -3, -1, 18, 33, -1,
+ 2, 32, 17, -1, 1, 16, 0
+};
+
+static short tab11[] =
+{
+ -121, -113, -89, -59, -43, -27, -17, -7, -3, -1, 119, 103, -1, 118, 117,
+ -3, -1, 102, 71, -1, 116, -1, 87, 85, -5, -3, -1, 86, 101, 55,
+ -1, 115, 70, -9, -7, -3, -1, 69, 84, -1, 53, 83, 39, -1, 114,
+ -1, 100, 7, -5, -1, 113, -1, 23, 112, -3, -1, 54, 99, -1, 96,
+ -1, 68, 37, -13, -7, -5, -3, -1, 82, 5, 21, 98, -3, -1, 38,
+ 6, 22, -5, -1, 97, -1, 81, 52, -5, -1, 80, -1, 67, 51, -1,
+ 36, 66, -15, -11, -7, -3, -1, 20, 65, -1, 4, 64, -1, 35, 50,
+ -1, 19, 49, -5, -3, -1, 3, 48, 34, 33, -5, -1, 18, -1, 2,
+ 32, 17, -3, -1, 1, 16, 0
+};
+
+static short tab12[] =
+{
+ -115, -99, -73, -45, -27, -17, -9, -5, -3, -1, 119, 103, 118, -1, 87,
+ 117, -3, -1, 102, 71, -1, 116, 101, -3, -1, 86, 55, -3, -1, 115,
+ 85, 39, -7, -3, -1, 114, 70, -1, 100, 23, -5, -1, 113, -1, 7,
+ 112, -1, 54, 99, -13, -9, -3, -1, 69, 84, -1, 68, -1, 6, 5,
+ -1, 38, 98, -5, -1, 97, -1, 22, 96, -3, -1, 53, 83, -1, 37,
+ 82, -17, -7, -3, -1, 21, 81, -1, 52, 67, -5, -3, -1, 80, 4,
+ 36, -1, 66, 20, -3, -1, 51, 65, -1, 35, 50, -11, -7, -5, -3,
+ -1, 64, 3, 48, 19, -1, 49, 34, -1, 18, 33, -7, -5, -3, -1,
+ 2, 32, 0, 17, -1, 1, 16
+};
+
+static short tab13[] =
+{
+ -509, -503, -475, -405, -333, -265, -205, -153, -115, -83, -53, -35, -21, -13, -9,
+ -7, -5, -3, -1, 254, 252, 253, 237, 255, -1, 239, 223, -3, -1, 238,
+ 207, -1, 222, 191, -9, -3, -1, 251, 206, -1, 220, -1, 175, 233, -1,
+ 236, 221, -9, -5, -3, -1, 250, 205, 190, -1, 235, 159, -3, -1, 249,
+ 234, -1, 189, 219, -17, -9, -3, -1, 143, 248, -1, 204, -1, 174, 158,
+ -5, -1, 142, -1, 127, 126, 247, -5, -1, 218, -1, 173, 188, -3, -1,
+ 203, 246, 111, -15, -7, -3, -1, 232, 95, -1, 157, 217, -3, -1, 245,
+ 231, -1, 172, 187, -9, -3, -1, 79, 244, -3, -1, 202, 230, 243, -1,
+ 63, -1, 141, 216, -21, -9, -3, -1, 47, 242, -3, -1, 110, 156, 15,
+ -5, -3, -1, 201, 94, 171, -3, -1, 125, 215, 78, -11, -5, -3, -1,
+ 200, 214, 62, -1, 185, -1, 155, 170, -1, 31, 241, -23, -13, -5, -1,
+ 240, -1, 186, 229, -3, -1, 228, 140, -1, 109, 227, -5, -1, 226, -1,
+ 46, 14, -1, 30, 225, -15, -7, -3, -1, 224, 93, -1, 213, 124, -3,
+ -1, 199, 77, -1, 139, 184, -7, -3, -1, 212, 154, -1, 169, 108, -1,
+ 198, 61, -37, -21, -9, -5, -3, -1, 211, 123, 45, -1, 210, 29, -5,
+ -1, 183, -1, 92, 197, -3, -1, 153, 122, 195, -7, -5, -3, -1, 167,
+ 151, 75, 209, -3, -1, 13, 208, -1, 138, 168, -11, -7, -3, -1, 76,
+ 196, -1, 107, 182, -1, 60, 44, -3, -1, 194, 91, -3, -1, 181, 137,
+ 28, -43, -23, -11, -5, -1, 193, -1, 152, 12, -1, 192, -1, 180, 106,
+ -5, -3, -1, 166, 121, 59, -1, 179, -1, 136, 90, -11, -5, -1, 43,
+ -1, 165, 105, -1, 164, -1, 120, 135, -5, -1, 148, -1, 119, 118, 178,
+ -11, -3, -1, 27, 177, -3, -1, 11, 176, -1, 150, 74, -7, -3, -1,
+ 58, 163, -1, 89, 149, -1, 42, 162, -47, -23, -9, -3, -1, 26, 161,
+ -3, -1, 10, 104, 160, -5, -3, -1, 134, 73, 147, -3, -1, 57, 88,
+ -1, 133, 103, -9, -3, -1, 41, 146, -3, -1, 87, 117, 56, -5, -1,
+ 131, -1, 102, 71, -3, -1, 116, 86, -1, 101, 115, -11, -3, -1, 25,
+ 145, -3, -1, 9, 144, -1, 72, 132, -7, -5, -1, 114, -1, 70, 100,
+ 40, -1, 130, 24, -41, -27, -11, -5, -3, -1, 55, 39, 23, -1, 113,
+ -1, 85, 7, -7, -3, -1, 112, 54, -1, 99, 69, -3, -1, 84, 38,
+ -1, 98, 53, -5, -1, 129, -1, 8, 128, -3, -1, 22, 97, -1, 6,
+ 96, -13, -9, -5, -3, -1, 83, 68, 37, -1, 82, 5, -1, 21, 81,
+ -7, -3, -1, 52, 67, -1, 80, 36, -3, -1, 66, 51, 20, -19, -11,
+ -5, -1, 65, -1, 4, 64, -3, -1, 35, 50, 19, -3, -1, 49, 3,
+ -1, 48, 34, -3, -1, 18, 33, -1, 2, 32, -3, -1, 17, 1, 16,
+ 0
+};
+
+static short tab15[] =
+{
+-495, -445, -355, -263, -183, -115, -77, -43, -27, -13, -7, -3, -1, 255, 239,
+ -1, 254, 223, -1, 238, -1, 253, 207, -7, -3, -1, 252, 222, -1, 237,
+ 191, -1, 251, -1, 206, 236, -7, -3, -1, 221, 175, -1, 250, 190, -3,
+ -1, 235, 205, -1, 220, 159, -15, -7, -3, -1, 249, 234, -1, 189, 219,
+ -3, -1, 143, 248, -1, 204, 158, -7, -3, -1, 233, 127, -1, 247, 173,
+ -3, -1, 218, 188, -1, 111, -1, 174, 15, -19, -11, -3, -1, 203, 246,
+ -3, -1, 142, 232, -1, 95, 157, -3, -1, 245, 126, -1, 231, 172, -9,
+ -3, -1, 202, 187, -3, -1, 217, 141, 79, -3, -1, 244, 63, -1, 243,
+ 216, -33, -17, -9, -3, -1, 230, 47, -1, 242, -1, 110, 240, -3, -1,
+ 31, 241, -1, 156, 201, -7, -3, -1, 94, 171, -1, 186, 229, -3, -1,
+ 125, 215, -1, 78, 228, -15, -7, -3, -1, 140, 200, -1, 62, 109, -3,
+ -1, 214, 227, -1, 155, 185, -7, -3, -1, 46, 170, -1, 226, 30, -5,
+ -1, 225, -1, 14, 224, -1, 93, 213, -45, -25, -13, -7, -3, -1, 124,
+ 199, -1, 77, 139, -1, 212, -1, 184, 154, -7, -3, -1, 169, 108, -1,
+ 198, 61, -1, 211, 210, -9, -5, -3, -1, 45, 13, 29, -1, 123, 183,
+ -5, -1, 209, -1, 92, 208, -1, 197, 138, -17, -7, -3, -1, 168, 76,
+ -1, 196, 107, -5, -1, 182, -1, 153, 12, -1, 60, 195, -9, -3, -1,
+ 122, 167, -1, 166, -1, 192, 11, -1, 194, -1, 44, 91, -55, -29, -15,
+ -7, -3, -1, 181, 28, -1, 137, 152, -3, -1, 193, 75, -1, 180, 106,
+ -5, -3, -1, 59, 121, 179, -3, -1, 151, 136, -1, 43, 90, -11, -5,
+ -1, 178, -1, 165, 27, -1, 177, -1, 176, 105, -7, -3, -1, 150, 74,
+ -1, 164, 120, -3, -1, 135, 58, 163, -17, -7, -3, -1, 89, 149, -1,
+ 42, 162, -3, -1, 26, 161, -3, -1, 10, 160, 104, -7, -3, -1, 134,
+ 73, -1, 148, 57, -5, -1, 147, -1, 119, 9, -1, 88, 133, -53, -29,
+ -13, -7, -3, -1, 41, 103, -1, 118, 146, -1, 145, -1, 25, 144, -7,
+ -3, -1, 72, 132, -1, 87, 117, -3, -1, 56, 131, -1, 102, 71, -7,
+ -3, -1, 40, 130, -1, 24, 129, -7, -3, -1, 116, 8, -1, 128, 86,
+ -3, -1, 101, 55, -1, 115, 70, -17, -7, -3, -1, 39, 114, -1, 100,
+ 23, -3, -1, 85, 113, -3, -1, 7, 112, 54, -7, -3, -1, 99, 69,
+ -1, 84, 38, -3, -1, 98, 22, -3, -1, 6, 96, 53, -33, -19, -9,
+ -5, -1, 97, -1, 83, 68, -1, 37, 82, -3, -1, 21, 81, -3, -1,
+ 5, 80, 52, -7, -3, -1, 67, 36, -1, 66, 51, -1, 65, -1, 20,
+ 4, -9, -3, -1, 35, 50, -3, -1, 64, 3, 19, -3, -1, 49, 48,
+ 34, -9, -7, -3, -1, 18, 33, -1, 2, 32, 17, -3, -1, 1, 16,
+ 0
+};
+
+static short tab16[] =
+{
+ -509, -503, -461, -323, -103, -37, -27, -15, -7, -3, -1, 239, 254, -1, 223,
+ 253, -3, -1, 207, 252, -1, 191, 251, -5, -1, 175, -1, 250, 159, -3,
+ -1, 249, 248, 143, -7, -3, -1, 127, 247, -1, 111, 246, 255, -9, -5,
+ -3, -1, 95, 245, 79, -1, 244, 243, -53, -1, 240, -1, 63, -29, -19,
+ -13, -7, -5, -1, 206, -1, 236, 221, 222, -1, 233, -1, 234, 217, -1,
+ 238, -1, 237, 235, -3, -1, 190, 205, -3, -1, 220, 219, 174, -11, -5,
+ -1, 204, -1, 173, 218, -3, -1, 126, 172, 202, -5, -3, -1, 201, 125,
+ 94, 189, 242, -93, -5, -3, -1, 47, 15, 31, -1, 241, -49, -25, -13,
+ -5, -1, 158, -1, 188, 203, -3, -1, 142, 232, -1, 157, 231, -7, -3,
+ -1, 187, 141, -1, 216, 110, -1, 230, 156, -13, -7, -3, -1, 171, 186,
+ -1, 229, 215, -1, 78, -1, 228, 140, -3, -1, 200, 62, -1, 109, -1,
+ 214, 155, -19, -11, -5, -3, -1, 185, 170, 225, -1, 212, -1, 184, 169,
+ -5, -1, 123, -1, 183, 208, 227, -7, -3, -1, 14, 224, -1, 93, 213,
+ -3, -1, 124, 199, -1, 77, 139, -75, -45, -27, -13, -7, -3, -1, 154,
+ 108, -1, 198, 61, -3, -1, 92, 197, 13, -7, -3, -1, 138, 168, -1,
+ 153, 76, -3, -1, 182, 122, 60, -11, -5, -3, -1, 91, 137, 28, -1,
+ 192, -1, 152, 121, -1, 226, -1, 46, 30, -15, -7, -3, -1, 211, 45,
+ -1, 210, 209, -5, -1, 59, -1, 151, 136, 29, -7, -3, -1, 196, 107,
+ -1, 195, 167, -1, 44, -1, 194, 181, -23, -13, -7, -3, -1, 193, 12,
+ -1, 75, 180, -3, -1, 106, 166, 179, -5, -3, -1, 90, 165, 43, -1,
+ 178, 27, -13, -5, -1, 177, -1, 11, 176, -3, -1, 105, 150, -1, 74,
+ 164, -5, -3, -1, 120, 135, 163, -3, -1, 58, 89, 42, -97, -57, -33,
+ -19, -11, -5, -3, -1, 149, 104, 161, -3, -1, 134, 119, 148, -5, -3,
+ -1, 73, 87, 103, 162, -5, -1, 26, -1, 10, 160, -3, -1, 57, 147,
+ -1, 88, 133, -9, -3, -1, 41, 146, -3, -1, 118, 9, 25, -5, -1,
+ 145, -1, 144, 72, -3, -1, 132, 117, -1, 56, 131, -21, -11, -5, -3,
+ -1, 102, 40, 130, -3, -1, 71, 116, 24, -3, -1, 129, 128, -3, -1,
+ 8, 86, 55, -9, -5, -1, 115, -1, 101, 70, -1, 39, 114, -5, -3,
+ -1, 100, 85, 7, 23, -23, -13, -5, -1, 113, -1, 112, 54, -3, -1,
+ 99, 69, -1, 84, 38, -3, -1, 98, 22, -1, 97, -1, 6, 96, -9,
+ -5, -1, 83, -1, 53, 68, -1, 37, 82, -1, 81, -1, 21, 5, -33,
+ -23, -13, -7, -3, -1, 52, 67, -1, 80, 36, -3, -1, 66, 51, 20,
+ -5, -1, 65, -1, 4, 64, -1, 35, 50, -3, -1, 19, 49, -3, -1,
+ 3, 48, 34, -3, -1, 18, 33, -1, 2, 32, -3, -1, 17, 1, 16,
+ 0
+};
+
+static short tab24[] =
+{
+ -451, -117, -43, -25, -15, -7, -3, -1, 239, 254, -1, 223, 253, -3, -1,
+ 207, 252, -1, 191, 251, -5, -1, 250, -1, 175, 159, -1, 249, 248, -9,
+ -5, -3, -1, 143, 127, 247, -1, 111, 246, -3, -1, 95, 245, -1, 79,
+ 244, -71, -7, -3, -1, 63, 243, -1, 47, 242, -5, -1, 241, -1, 31,
+ 240, -25, -9, -1, 15, -3, -1, 238, 222, -1, 237, 206, -7, -3, -1,
+ 236, 221, -1, 190, 235, -3, -1, 205, 220, -1, 174, 234, -15, -7, -3,
+ -1, 189, 219, -1, 204, 158, -3, -1, 233, 173, -1, 218, 188, -7, -3,
+ -1, 203, 142, -1, 232, 157, -3, -1, 217, 126, -1, 231, 172, 255, -235,
+ -143, -77, -45, -25, -15, -7, -3, -1, 202, 187, -1, 141, 216, -5, -3,
+ -1, 14, 224, 13, 230, -5, -3, -1, 110, 156, 201, -1, 94, 186, -9,
+ -5, -1, 229, -1, 171, 125, -1, 215, 228, -3, -1, 140, 200, -3, -1,
+ 78, 46, 62, -15, -7, -3, -1, 109, 214, -1, 227, 155, -3, -1, 185,
+ 170, -1, 226, 30, -7, -3, -1, 225, 93, -1, 213, 124, -3, -1, 199,
+ 77, -1, 139, 184, -31, -15, -7, -3, -1, 212, 154, -1, 169, 108, -3,
+ -1, 198, 61, -1, 211, 45, -7, -3, -1, 210, 29, -1, 123, 183, -3,
+ -1, 209, 92, -1, 197, 138, -17, -7, -3, -1, 168, 153, -1, 76, 196,
+ -3, -1, 107, 182, -3, -1, 208, 12, 60, -7, -3, -1, 195, 122, -1,
+ 167, 44, -3, -1, 194, 91, -1, 181, 28, -57, -35, -19, -7, -3, -1,
+ 137, 152, -1, 193, 75, -5, -3, -1, 192, 11, 59, -3, -1, 176, 10,
+ 26, -5, -1, 180, -1, 106, 166, -3, -1, 121, 151, -3, -1, 160, 9,
+ 144, -9, -3, -1, 179, 136, -3, -1, 43, 90, 178, -7, -3, -1, 165,
+ 27, -1, 177, 105, -1, 150, 164, -17, -9, -5, -3, -1, 74, 120, 135,
+ -1, 58, 163, -3, -1, 89, 149, -1, 42, 162, -7, -3, -1, 161, 104,
+ -1, 134, 119, -3, -1, 73, 148, -1, 57, 147, -63, -31, -15, -7, -3,
+ -1, 88, 133, -1, 41, 103, -3, -1, 118, 146, -1, 25, 145, -7, -3,
+ -1, 72, 132, -1, 87, 117, -3, -1, 56, 131, -1, 102, 40, -17, -7,
+ -3, -1, 130, 24, -1, 71, 116, -5, -1, 129, -1, 8, 128, -1, 86,
+ 101, -7, -5, -1, 23, -1, 7, 112, 115, -3, -1, 55, 39, 114, -15,
+ -7, -3, -1, 70, 100, -1, 85, 113, -3, -1, 54, 99, -1, 69, 84,
+ -7, -3, -1, 38, 98, -1, 22, 97, -5, -3, -1, 6, 96, 53, -1,
+ 83, 68, -51, -37, -23, -15, -9, -3, -1, 37, 82, -1, 21, -1, 5,
+ 80, -1, 81, -1, 52, 67, -3, -1, 36, 66, -1, 51, 20, -9, -5,
+ -1, 65, -1, 4, 64, -1, 35, 50, -1, 19, 49, -7, -5, -3, -1,
+ 3, 48, 34, 18, -1, 33, -1, 2, 32, -3, -1, 17, 1, -1, 16,
+ 0
+};
+
+static short tab_c0[] =
+{
+ -29, -21, -13, -7, -3, -1, 11, 15, -1, 13, 14, -3, -1, 7, 5,
+ 9, -3, -1, 6, 3, -1, 10, 12, -3, -1, 2, 1, -1, 4, 8,
+ 0
+};
+
+static short tab_c1[] =
+{
+ -15, -7, -3, -1, 15, 14, -1, 13, 12, -3, -1, 11, 10, -1, 9,
+ 8, -7, -3, -1, 7, 6, -1, 5, 4, -3, -1, 3, 2, -1, 1,
+ 0
+};
+
+static struct newhuff ht[] =
+{
+ { /* 0 */ 0, tab0},
+ { /* 2 */ 0, tab1},
+ { /* 3 */ 0, tab2},
+ { /* 3 */ 0, tab3},
+ { /* 0 */ 0, tab0},
+ { /* 4 */ 0, tab5},
+ { /* 4 */ 0, tab6},
+ { /* 6 */ 0, tab7},
+ { /* 6 */ 0, tab8},
+ { /* 6 */ 0, tab9},
+ { /* 8 */ 0, tab10},
+ { /* 8 */ 0, tab11},
+ { /* 8 */ 0, tab12},
+ { /* 16 */ 0, tab13},
+ { /* 0 */ 0, tab0},
+ { /* 16 */ 0, tab15},
+
+ { /* 16 */ 1, tab16},
+ { /* 16 */ 2, tab16},
+ { /* 16 */ 3, tab16},
+ { /* 16 */ 4, tab16},
+ { /* 16 */ 6, tab16},
+ { /* 16 */ 8, tab16},
+ { /* 16 */ 10, tab16},
+ { /* 16 */ 13, tab16},
+ { /* 16 */ 4, tab24},
+ { /* 16 */ 5, tab24},
+ { /* 16 */ 6, tab24},
+ { /* 16 */ 7, tab24},
+ { /* 16 */ 8, tab24},
+ { /* 16 */ 9, tab24},
+ { /* 16 */ 11, tab24},
+ { /* 16 */ 13, tab24}
+};
+
+static struct newhuff htc[] =
+{
+ { /* 1 , 1 , */ 0, tab_c0},
+ { /* 1 , 1 , */ 0, tab_c1}
+};
diff --git a/src/libmpg123/l2tables.h b/src/libmpg123/l2tables.h
new file mode 100755
index 0000000..51eb22f
--- /dev/null
+++ b/src/libmpg123/l2tables.h
@@ -0,0 +1,997 @@
+/*
+ * Layer 2 Alloc tables ..
+ * most other tables are calculated on program start (which is (of course)
+ * not ISO-conform) ..
+ * Layer-3 huffman table is in huffman.h
+ */
+
+struct al_table alloc_0[] =
+{
+ {4, 0},
+ {5, 3},
+ {3, -3},
+ {4, -7},
+ {5, -15},
+ {6, -31},
+ {7, -63},
+ {8, -127},
+ {9, -255},
+ {10, -511},
+ {11, -1023},
+ {12, -2047},
+ {13, -4095},
+ {14, -8191},
+ {15, -16383},
+ {16, -32767},
+ {4, 0},
+ {5, 3},
+ {3, -3},
+ {4, -7},
+ {5, -15},
+ {6, -31},
+ {7, -63},
+ {8, -127},
+ {9, -255},
+ {10, -511},
+ {11, -1023},
+ {12, -2047},
+ {13, -4095},
+ {14, -8191},
+ {15, -16383},
+ {16, -32767},
+ {4, 0},
+ {5, 3},
+ {3, -3},
+ {4, -7},
+ {5, -15},
+ {6, -31},
+ {7, -63},
+ {8, -127},
+ {9, -255},
+ {10, -511},
+ {11, -1023},
+ {12, -2047},
+ {13, -4095},
+ {14, -8191},
+ {15, -16383},
+ {16, -32767},
+ {4, 0},
+ {5, 3},
+ {7, 5},
+ {3, -3},
+ {10, 9},
+ {4, -7},
+ {5, -15},
+ {6, -31},
+ {7, -63},
+ {8, -127},
+ {9, -255},
+ {10, -511},
+ {11, -1023},
+ {12, -2047},
+ {13, -4095},
+ {16, -32767},
+ {4, 0},
+ {5, 3},
+ {7, 5},
+ {3, -3},
+ {10, 9},
+ {4, -7},
+ {5, -15},
+ {6, -31},
+ {7, -63},
+ {8, -127},
+ {9, -255},
+ {10, -511},
+ {11, -1023},
+ {12, -2047},
+ {13, -4095},
+ {16, -32767},
+ {4, 0},
+ {5, 3},
+ {7, 5},
+ {3, -3},
+ {10, 9},
+ {4, -7},
+ {5, -15},
+ {6, -31},
+ {7, -63},
+ {8, -127},
+ {9, -255},
+ {10, -511},
+ {11, -1023},
+ {12, -2047},
+ {13, -4095},
+ {16, -32767},
+ {4, 0},
+ {5, 3},
+ {7, 5},
+ {3, -3},
+ {10, 9},
+ {4, -7},
+ {5, -15},
+ {6, -31},
+ {7, -63},
+ {8, -127},
+ {9, -255},
+ {10, -511},
+ {11, -1023},
+ {12, -2047},
+ {13, -4095},
+ {16, -32767},
+ {4, 0},
+ {5, 3},
+ {7, 5},
+ {3, -3},
+ {10, 9},
+ {4, -7},
+ {5, -15},
+ {6, -31},
+ {7, -63},
+ {8, -127},
+ {9, -255},
+ {10, -511},
+ {11, -1023},
+ {12, -2047},
+ {13, -4095},
+ {16, -32767},
+ {4, 0},
+ {5, 3},
+ {7, 5},
+ {3, -3},
+ {10, 9},
+ {4, -7},
+ {5, -15},
+ {6, -31},
+ {7, -63},
+ {8, -127},
+ {9, -255},
+ {10, -511},
+ {11, -1023},
+ {12, -2047},
+ {13, -4095},
+ {16, -32767},
+ {4, 0},
+ {5, 3},
+ {7, 5},
+ {3, -3},
+ {10, 9},
+ {4, -7},
+ {5, -15},
+ {6, -31},
+ {7, -63},
+ {8, -127},
+ {9, -255},
+ {10, -511},
+ {11, -1023},
+ {12, -2047},
+ {13, -4095},
+ {16, -32767},
+ {4, 0},
+ {5, 3},
+ {7, 5},
+ {3, -3},
+ {10, 9},
+ {4, -7},
+ {5, -15},
+ {6, -31},
+ {7, -63},
+ {8, -127},
+ {9, -255},
+ {10, -511},
+ {11, -1023},
+ {12, -2047},
+ {13, -4095},
+ {16, -32767},
+ {3, 0},
+ {5, 3},
+ {7, 5},
+ {3, -3},
+ {10, 9},
+ {4, -7},
+ {5, -15},
+ {16, -32767},
+ {3, 0},
+ {5, 3},
+ {7, 5},
+ {3, -3},
+ {10, 9},
+ {4, -7},
+ {5, -15},
+ {16, -32767},
+ {3, 0},
+ {5, 3},
+ {7, 5},
+ {3, -3},
+ {10, 9},
+ {4, -7},
+ {5, -15},
+ {16, -32767},
+ {3, 0},
+ {5, 3},
+ {7, 5},
+ {3, -3},
+ {10, 9},
+ {4, -7},
+ {5, -15},
+ {16, -32767},
+ {3, 0},
+ {5, 3},
+ {7, 5},
+ {3, -3},
+ {10, 9},
+ {4, -7},
+ {5, -15},
+ {16, -32767},
+ {3, 0},
+ {5, 3},
+ {7, 5},
+ {3, -3},
+ {10, 9},
+ {4, -7},
+ {5, -15},
+ {16, -32767},
+ {3, 0},
+ {5, 3},
+ {7, 5},
+ {3, -3},
+ {10, 9},
+ {4, -7},
+ {5, -15},
+ {16, -32767},
+ {3, 0},
+ {5, 3},
+ {7, 5},
+ {3, -3},
+ {10, 9},
+ {4, -7},
+ {5, -15},
+ {16, -32767},
+ {3, 0},
+ {5, 3},
+ {7, 5},
+ {3, -3},
+ {10, 9},
+ {4, -7},
+ {5, -15},
+ {16, -32767},
+ {3, 0},
+ {5, 3},
+ {7, 5},
+ {3, -3},
+ {10, 9},
+ {4, -7},
+ {5, -15},
+ {16, -32767},
+ {3, 0},
+ {5, 3},
+ {7, 5},
+ {3, -3},
+ {10, 9},
+ {4, -7},
+ {5, -15},
+ {16, -32767},
+ {3, 0},
+ {5, 3},
+ {7, 5},
+ {3, -3},
+ {10, 9},
+ {4, -7},
+ {5, -15},
+ {16, -32767},
+ {2, 0},
+ {5, 3},
+ {7, 5},
+ {16, -32767},
+ {2, 0},
+ {5, 3},
+ {7, 5},
+ {16, -32767},
+ {2, 0},
+ {5, 3},
+ {7, 5},
+ {16, -32767},
+ {2, 0},
+ {5, 3},
+ {7, 5},
+ {16, -32767}};
+
+struct al_table alloc_1[] =
+{
+ {4, 0},
+ {5, 3},
+ {3, -3},
+ {4, -7},
+ {5, -15},
+ {6, -31},
+ {7, -63},
+ {8, -127},
+ {9, -255},
+ {10, -511},
+ {11, -1023},
+ {12, -2047},
+ {13, -4095},
+ {14, -8191},
+ {15, -16383},
+ {16, -32767},
+ {4, 0},
+ {5, 3},
+ {3, -3},
+ {4, -7},
+ {5, -15},
+ {6, -31},
+ {7, -63},
+ {8, -127},
+ {9, -255},
+ {10, -511},
+ {11, -1023},
+ {12, -2047},
+ {13, -4095},
+ {14, -8191},
+ {15, -16383},
+ {16, -32767},
+ {4, 0},
+ {5, 3},
+ {3, -3},
+ {4, -7},
+ {5, -15},
+ {6, -31},
+ {7, -63},
+ {8, -127},
+ {9, -255},
+ {10, -511},
+ {11, -1023},
+ {12, -2047},
+ {13, -4095},
+ {14, -8191},
+ {15, -16383},
+ {16, -32767},
+ {4, 0},
+ {5, 3},
+ {7, 5},
+ {3, -3},
+ {10, 9},
+ {4, -7},
+ {5, -15},
+ {6, -31},
+ {7, -63},
+ {8, -127},
+ {9, -255},
+ {10, -511},
+ {11, -1023},
+ {12, -2047},
+ {13, -4095},
+ {16, -32767},
+ {4, 0},
+ {5, 3},
+ {7, 5},
+ {3, -3},
+ {10, 9},
+ {4, -7},
+ {5, -15},
+ {6, -31},
+ {7, -63},
+ {8, -127},
+ {9, -255},
+ {10, -511},
+ {11, -1023},
+ {12, -2047},
+ {13, -4095},
+ {16, -32767},
+ {4, 0},
+ {5, 3},
+ {7, 5},
+ {3, -3},
+ {10, 9},
+ {4, -7},
+ {5, -15},
+ {6, -31},
+ {7, -63},
+ {8, -127},
+ {9, -255},
+ {10, -511},
+ {11, -1023},
+ {12, -2047},
+ {13, -4095},
+ {16, -32767},
+ {4, 0},
+ {5, 3},
+ {7, 5},
+ {3, -3},
+ {10, 9},
+ {4, -7},
+ {5, -15},
+ {6, -31},
+ {7, -63},
+ {8, -127},
+ {9, -255},
+ {10, -511},
+ {11, -1023},
+ {12, -2047},
+ {13, -4095},
+ {16, -32767},
+ {4, 0},
+ {5, 3},
+ {7, 5},
+ {3, -3},
+ {10, 9},
+ {4, -7},
+ {5, -15},
+ {6, -31},
+ {7, -63},
+ {8, -127},
+ {9, -255},
+ {10, -511},
+ {11, -1023},
+ {12, -2047},
+ {13, -4095},
+ {16, -32767},
+ {4, 0},
+ {5, 3},
+ {7, 5},
+ {3, -3},
+ {10, 9},
+ {4, -7},
+ {5, -15},
+ {6, -31},
+ {7, -63},
+ {8, -127},
+ {9, -255},
+ {10, -511},
+ {11, -1023},
+ {12, -2047},
+ {13, -4095},
+ {16, -32767},
+ {4, 0},
+ {5, 3},
+ {7, 5},
+ {3, -3},
+ {10, 9},
+ {4, -7},
+ {5, -15},
+ {6, -31},
+ {7, -63},
+ {8, -127},
+ {9, -255},
+ {10, -511},
+ {11, -1023},
+ {12, -2047},
+ {13, -4095},
+ {16, -32767},
+ {4, 0},
+ {5, 3},
+ {7, 5},
+ {3, -3},
+ {10, 9},
+ {4, -7},
+ {5, -15},
+ {6, -31},
+ {7, -63},
+ {8, -127},
+ {9, -255},
+ {10, -511},
+ {11, -1023},
+ {12, -2047},
+ {13, -4095},
+ {16, -32767},
+ {3, 0},
+ {5, 3},
+ {7, 5},
+ {3, -3},
+ {10, 9},
+ {4, -7},
+ {5, -15},
+ {16, -32767},
+ {3, 0},
+ {5, 3},
+ {7, 5},
+ {3, -3},
+ {10, 9},
+ {4, -7},
+ {5, -15},
+ {16, -32767},
+ {3, 0},
+ {5, 3},
+ {7, 5},
+ {3, -3},
+ {10, 9},
+ {4, -7},
+ {5, -15},
+ {16, -32767},
+ {3, 0},
+ {5, 3},
+ {7, 5},
+ {3, -3},
+ {10, 9},
+ {4, -7},
+ {5, -15},
+ {16, -32767},
+ {3, 0},
+ {5, 3},
+ {7, 5},
+ {3, -3},
+ {10, 9},
+ {4, -7},
+ {5, -15},
+ {16, -32767},
+ {3, 0},
+ {5, 3},
+ {7, 5},
+ {3, -3},
+ {10, 9},
+ {4, -7},
+ {5, -15},
+ {16, -32767},
+ {3, 0},
+ {5, 3},
+ {7, 5},
+ {3, -3},
+ {10, 9},
+ {4, -7},
+ {5, -15},
+ {16, -32767},
+ {3, 0},
+ {5, 3},
+ {7, 5},
+ {3, -3},
+ {10, 9},
+ {4, -7},
+ {5, -15},
+ {16, -32767},
+ {3, 0},
+ {5, 3},
+ {7, 5},
+ {3, -3},
+ {10, 9},
+ {4, -7},
+ {5, -15},
+ {16, -32767},
+ {3, 0},
+ {5, 3},
+ {7, 5},
+ {3, -3},
+ {10, 9},
+ {4, -7},
+ {5, -15},
+ {16, -32767},
+ {3, 0},
+ {5, 3},
+ {7, 5},
+ {3, -3},
+ {10, 9},
+ {4, -7},
+ {5, -15},
+ {16, -32767},
+ {3, 0},
+ {5, 3},
+ {7, 5},
+ {3, -3},
+ {10, 9},
+ {4, -7},
+ {5, -15},
+ {16, -32767},
+ {2, 0},
+ {5, 3},
+ {7, 5},
+ {16, -32767},
+ {2, 0},
+ {5, 3},
+ {7, 5},
+ {16, -32767},
+ {2, 0},
+ {5, 3},
+ {7, 5},
+ {16, -32767},
+ {2, 0},
+ {5, 3},
+ {7, 5},
+ {16, -32767},
+ {2, 0},
+ {5, 3},
+ {7, 5},
+ {16, -32767},
+ {2, 0},
+ {5, 3},
+ {7, 5},
+ {16, -32767},
+ {2, 0},
+ {5, 3},
+ {7, 5},
+ {16, -32767}};
+
+struct al_table alloc_2[] =
+{
+ {4, 0},
+ {5, 3},
+ {7, 5},
+ {10, 9},
+ {4, -7},
+ {5, -15},
+ {6, -31},
+ {7, -63},
+ {8, -127},
+ {9, -255},
+ {10, -511},
+ {11, -1023},
+ {12, -2047},
+ {13, -4095},
+ {14, -8191},
+ {15, -16383},
+ {4, 0},
+ {5, 3},
+ {7, 5},
+ {10, 9},
+ {4, -7},
+ {5, -15},
+ {6, -31},
+ {7, -63},
+ {8, -127},
+ {9, -255},
+ {10, -511},
+ {11, -1023},
+ {12, -2047},
+ {13, -4095},
+ {14, -8191},
+ {15, -16383},
+ {3, 0},
+ {5, 3},
+ {7, 5},
+ {10, 9},
+ {4, -7},
+ {5, -15},
+ {6, -31},
+ {7, -63},
+ {3, 0},
+ {5, 3},
+ {7, 5},
+ {10, 9},
+ {4, -7},
+ {5, -15},
+ {6, -31},
+ {7, -63},
+ {3, 0},
+ {5, 3},
+ {7, 5},
+ {10, 9},
+ {4, -7},
+ {5, -15},
+ {6, -31},
+ {7, -63},
+ {3, 0},
+ {5, 3},
+ {7, 5},
+ {10, 9},
+ {4, -7},
+ {5, -15},
+ {6, -31},
+ {7, -63},
+ {3, 0},
+ {5, 3},
+ {7, 5},
+ {10, 9},
+ {4, -7},
+ {5, -15},
+ {6, -31},
+ {7, -63},
+ {3, 0},
+ {5, 3},
+ {7, 5},
+ {10, 9},
+ {4, -7},
+ {5, -15},
+ {6, -31},
+ {7, -63}};
+
+struct al_table alloc_3[] =
+{
+ {4, 0},
+ {5, 3},
+ {7, 5},
+ {10, 9},
+ {4, -7},
+ {5, -15},
+ {6, -31},
+ {7, -63},
+ {8, -127},
+ {9, -255},
+ {10, -511},
+ {11, -1023},
+ {12, -2047},
+ {13, -4095},
+ {14, -8191},
+ {15, -16383},
+ {4, 0},
+ {5, 3},
+ {7, 5},
+ {10, 9},
+ {4, -7},
+ {5, -15},
+ {6, -31},
+ {7, -63},
+ {8, -127},
+ {9, -255},
+ {10, -511},
+ {11, -1023},
+ {12, -2047},
+ {13, -4095},
+ {14, -8191},
+ {15, -16383},
+ {3, 0},
+ {5, 3},
+ {7, 5},
+ {10, 9},
+ {4, -7},
+ {5, -15},
+ {6, -31},
+ {7, -63},
+ {3, 0},
+ {5, 3},
+ {7, 5},
+ {10, 9},
+ {4, -7},
+ {5, -15},
+ {6, -31},
+ {7, -63},
+ {3, 0},
+ {5, 3},
+ {7, 5},
+ {10, 9},
+ {4, -7},
+ {5, -15},
+ {6, -31},
+ {7, -63},
+ {3, 0},
+ {5, 3},
+ {7, 5},
+ {10, 9},
+ {4, -7},
+ {5, -15},
+ {6, -31},
+ {7, -63},
+ {3, 0},
+ {5, 3},
+ {7, 5},
+ {10, 9},
+ {4, -7},
+ {5, -15},
+ {6, -31},
+ {7, -63},
+ {3, 0},
+ {5, 3},
+ {7, 5},
+ {10, 9},
+ {4, -7},
+ {5, -15},
+ {6, -31},
+ {7, -63},
+ {3, 0},
+ {5, 3},
+ {7, 5},
+ {10, 9},
+ {4, -7},
+ {5, -15},
+ {6, -31},
+ {7, -63},
+ {3, 0},
+ {5, 3},
+ {7, 5},
+ {10, 9},
+ {4, -7},
+ {5, -15},
+ {6, -31},
+ {7, -63},
+ {3, 0},
+ {5, 3},
+ {7, 5},
+ {10, 9},
+ {4, -7},
+ {5, -15},
+ {6, -31},
+ {7, -63},
+ {3, 0},
+ {5, 3},
+ {7, 5},
+ {10, 9},
+ {4, -7},
+ {5, -15},
+ {6, -31},
+ {7, -63}};
+
+struct al_table alloc_4[] =
+{
+ {4, 0},
+ {5, 3},
+ {7, 5},
+ {3, -3},
+ {10, 9},
+ {4, -7},
+ {5, -15},
+ {6, -31},
+ {7, -63},
+ {8, -127},
+ {9, -255},
+ {10, -511},
+ {11, -1023},
+ {12, -2047},
+ {13, -4095},
+ {14, -8191},
+ {4, 0},
+ {5, 3},
+ {7, 5},
+ {3, -3},
+ {10, 9},
+ {4, -7},
+ {5, -15},
+ {6, -31},
+ {7, -63},
+ {8, -127},
+ {9, -255},
+ {10, -511},
+ {11, -1023},
+ {12, -2047},
+ {13, -4095},
+ {14, -8191},
+ {4, 0},
+ {5, 3},
+ {7, 5},
+ {3, -3},
+ {10, 9},
+ {4, -7},
+ {5, -15},
+ {6, -31},
+ {7, -63},
+ {8, -127},
+ {9, -255},
+ {10, -511},
+ {11, -1023},
+ {12, -2047},
+ {13, -4095},
+ {14, -8191},
+ {4, 0},
+ {5, 3},
+ {7, 5},
+ {3, -3},
+ {10, 9},
+ {4, -7},
+ {5, -15},
+ {6, -31},
+ {7, -63},
+ {8, -127},
+ {9, -255},
+ {10, -511},
+ {11, -1023},
+ {12, -2047},
+ {13, -4095},
+ {14, -8191},
+ {3, 0},
+ {5, 3},
+ {7, 5},
+ {10, 9},
+ {4, -7},
+ {5, -15},
+ {6, -31},
+ {7, -63},
+ {3, 0},
+ {5, 3},
+ {7, 5},
+ {10, 9},
+ {4, -7},
+ {5, -15},
+ {6, -31},
+ {7, -63},
+ {3, 0},
+ {5, 3},
+ {7, 5},
+ {10, 9},
+ {4, -7},
+ {5, -15},
+ {6, -31},
+ {7, -63},
+ {3, 0},
+ {5, 3},
+ {7, 5},
+ {10, 9},
+ {4, -7},
+ {5, -15},
+ {6, -31},
+ {7, -63},
+ {3, 0},
+ {5, 3},
+ {7, 5},
+ {10, 9},
+ {4, -7},
+ {5, -15},
+ {6, -31},
+ {7, -63},
+ {3, 0},
+ {5, 3},
+ {7, 5},
+ {10, 9},
+ {4, -7},
+ {5, -15},
+ {6, -31},
+ {7, -63},
+ {3, 0},
+ {5, 3},
+ {7, 5},
+ {10, 9},
+ {4, -7},
+ {5, -15},
+ {6, -31},
+ {7, -63},
+ {2, 0},
+ {5, 3},
+ {7, 5},
+ {10, 9},
+ {2, 0},
+ {5, 3},
+ {7, 5},
+ {10, 9},
+ {2, 0},
+ {5, 3},
+ {7, 5},
+ {10, 9},
+ {2, 0},
+ {5, 3},
+ {7, 5},
+ {10, 9},
+ {2, 0},
+ {5, 3},
+ {7, 5},
+ {10, 9},
+ {2, 0},
+ {5, 3},
+ {7, 5},
+ {10, 9},
+ {2, 0},
+ {5, 3},
+ {7, 5},
+ {10, 9},
+ {2, 0},
+ {5, 3},
+ {7, 5},
+ {10, 9},
+ {2, 0},
+ {5, 3},
+ {7, 5},
+ {10, 9},
+ {2, 0},
+ {5, 3},
+ {7, 5},
+ {10, 9},
+ {2, 0},
+ {5, 3},
+ {7, 5},
+ {10, 9},
+ {2, 0},
+ {5, 3},
+ {7, 5},
+ {10, 9},
+ {2, 0},
+ {5, 3},
+ {7, 5},
+ {10, 9},
+ {2, 0},
+ {5, 3},
+ {7, 5},
+ {10, 9},
+ {2, 0},
+ {5, 3},
+ {7, 5},
+ {10, 9},
+ {2, 0},
+ {5, 3},
+ {7, 5},
+ {10, 9},
+ {2, 0},
+ {5, 3},
+ {7, 5},
+ {10, 9},
+ {2, 0},
+ {5, 3},
+ {7, 5},
+ {10, 9},
+ {2, 0},
+ {5, 3},
+ {7, 5},
+ {10, 9}};
diff --git a/src/libmpg123/layer1.c b/src/libmpg123/layer1.c
new file mode 100755
index 0000000..057f08b
--- /dev/null
+++ b/src/libmpg123/layer1.c
@@ -0,0 +1,187 @@
+
+/*
+ * Mpeg Layer-1 audio decoder
+ * --------------------------
+ * copyright (c) 1995 by Michael Hipp, All rights reserved. See also 'README'
+ * near unoptimzed ...
+ *
+ * may have a few bugs after last optimization ...
+ *
+ */
+
+#include "mpg123.h"
+#include "getbits.h"
+
+/* Used by the getbits macros */
+static unsigned long rval;
+
+void I_step_one(unsigned int balloc[], unsigned int scale_index[2][SBLIMIT], struct frame *fr)
+{
+ unsigned int *ba = balloc;
+ unsigned int *sca = (unsigned int *) scale_index;
+
+ if (fr->stereo)
+ {
+ int i;
+ int jsbound = fr->jsbound;
+
+ for (i = 0; i < jsbound; i++)
+ {
+ *ba++ = mpg123_getbits(4);
+ *ba++ = mpg123_getbits(4);
+ }
+ for (i = jsbound; i < SBLIMIT; i++)
+ *ba++ = mpg123_getbits(4);
+
+ ba = balloc;
+
+ for (i = 0; i < jsbound; i++)
+ {
+ if ((*ba++))
+ *sca++ = mpg123_getbits(6);
+ if ((*ba++))
+ *sca++ = mpg123_getbits(6);
+ }
+ for (i = jsbound; i < SBLIMIT; i++)
+ if ((*ba++))
+ {
+ *sca++ = mpg123_getbits(6);
+ *sca++ = mpg123_getbits(6);
+ }
+ }
+ else
+ {
+ int i;
+
+ for (i = 0; i < SBLIMIT; i++)
+ *ba++ = mpg123_getbits(4);
+ ba = balloc;
+ for (i = 0; i < SBLIMIT; i++)
+ if ((*ba++))
+ *sca++ = mpg123_getbits(6);
+ }
+}
+
+void I_step_two(real fraction[2][SBLIMIT], unsigned int balloc[2 * SBLIMIT],
+ unsigned int scale_index[2][SBLIMIT], struct frame *fr)
+{
+ int i, n;
+ int smpb[2 * SBLIMIT]; /* values: 0-65535 */
+ int *sample;
+ register unsigned int *ba;
+ register unsigned int *sca = (unsigned int *) scale_index;
+
+ if (fr->stereo)
+ {
+ int jsbound = fr->jsbound;
+ register real *f0 = fraction[0];
+ register real *f1 = fraction[1];
+
+ ba = balloc;
+ for (sample = smpb, i = 0; i < jsbound; i++)
+ {
+ if ((n = *ba++))
+ *sample++ = mpg123_getbits(n + 1);
+ if ((n = *ba++))
+ *sample++ = mpg123_getbits(n + 1);
+ }
+ for (i = jsbound; i < SBLIMIT; i++)
+ if ((n = *ba++))
+ *sample++ = mpg123_getbits(n + 1);
+
+ ba = balloc;
+ for (sample = smpb, i = 0; i < jsbound; i++)
+ {
+ if ((n = *ba++))
+ *f0++ = (real) (((-1) << n) + (*sample++) + 1) * mpg123_muls[n + 1][*sca++];
+ else
+ *f0++ = 0.0;
+ if ((n = *ba++))
+ *f1++ = (real) (((-1) << n) + (*sample++) + 1) * mpg123_muls[n + 1][*sca++];
+ else
+ *f1++ = 0.0;
+ }
+ for (i = jsbound; i < SBLIMIT; i++)
+ {
+ if ((n = *ba++))
+ {
+ real samp = (((-1) << n) + (*sample++) + 1);
+
+ *f0++ = samp * mpg123_muls[n + 1][*sca++];
+ *f1++ = samp * mpg123_muls[n + 1][*sca++];
+ }
+ else
+ *f0++ = *f1++ = 0.0;
+ }
+ for (i = fr->down_sample_sblimit; i < 32; i++)
+ fraction[0][i] = fraction[1][i] = 0.0;
+ }
+ else
+ {
+ register real *f0 = fraction[0];
+
+ ba = balloc;
+ for (sample = smpb, i = 0; i < SBLIMIT; i++)
+ if ((n = *ba++))
+ *sample++ = mpg123_getbits(n + 1);
+ ba = balloc;
+ for (sample = smpb, i = 0; i < SBLIMIT; i++)
+ {
+ if ((n = *ba++))
+ *f0++ = (real) (((-1) << n) + (*sample++) + 1) * mpg123_muls[n + 1][*sca++];
+ else
+ *f0++ = 0.0;
+ }
+ for (i = fr->down_sample_sblimit; i < 32; i++)
+ fraction[0][i] = 0.0;
+ }
+}
+
+int mpg123_do_layer1(struct frame *fr)
+{
+ int i, stereo = fr->stereo;
+ unsigned int balloc[2 * SBLIMIT];
+ unsigned int scale_index[2][SBLIMIT];
+ real fraction[2][SBLIMIT];
+ int single = fr->single;
+
+ fr->jsbound = (fr->mode == MPG_MD_JOINT_STEREO) ? (fr->mode_ext << 2) + 4 : 32;
+
+ if (stereo == 1 || single == 3)
+ single = 0;
+
+ I_step_one(balloc, scale_index, fr);
+
+ for (i = 0; i < SCALE_BLOCK; i++)
+ {
+ I_step_two(fraction, balloc, scale_index, fr);
+
+ if (single >= 0)
+ {
+ (fr->synth_mono) ((real *) fraction[single], mpg123_pcm_sample, &mpg123_pcm_point);
+ }
+ else
+ {
+ int p1 = mpg123_pcm_point;
+
+ (fr->synth) ((real *) fraction[0], 0, mpg123_pcm_sample, &p1);
+ (fr->synth) ((real *) fraction[1], 1, mpg123_pcm_sample, &mpg123_pcm_point);
+ }
+
+/***
+ if (mpg123_info->output_audio)
+ {
+
+ mpg123_ip.add_vis_pcm(mpg123_ip.output->written_time(), mpg123_cfg.resolution == 16 ? FMT_S16_NE : FMT_U8,
+ mpg123_cfg.channels == 2 ? fr->stereo : 1, mpg123_pcm_point, mpg123_pcm_sample);
+ while (mpg123_ip.output->buffer_free() < mpg123_pcm_point && mpg123_info->going && mpg123_info->jump_to_time == -1)
+ xmms_usleep(10000);
+ if (mpg123_info->going && mpg123_info->jump_to_time == -1)
+ mpg123_ip.output->write_audio(mpg123_pcm_sample, mpg123_pcm_point);
+ }
+***/
+ mpg123_pcm_point = 0;
+ }
+
+ return 1;
+}
diff --git a/src/libmpg123/layer2.c b/src/libmpg123/layer2.c
new file mode 100755
index 0000000..07ce258
--- /dev/null
+++ b/src/libmpg123/layer2.c
@@ -0,0 +1,336 @@
+
+/*
+ * Mpeg Layer-2 audio decoder
+ * --------------------------
+ * copyright (c) 1995 by Michael Hipp, All rights reserved. See also 'README'
+ *
+ */
+
+#include "mpg123.h"
+#include "l2tables.h"
+#include "getbits.h"
+
+static int grp_3tab[32 * 3] =
+{0,}; /* used: 27 */
+static int grp_5tab[128 * 3] =
+{0,}; /* used: 125 */
+static int grp_9tab[1024 * 3] =
+{0,}; /* used: 729 */
+
+real mpg123_muls[27][64]; /* also used by layer 1 */
+
+/* Used by the getbits macros */
+static unsigned long rval;
+
+void mpg123_init_layer2(void)
+{
+ static double mulmul[27] = {
+ 0.0, -2.0 / 3.0, 2.0 / 3.0, 2.0 / 7.0, 2.0 / 15.0,
+ 2.0 / 31.0, 2.0 / 63.0, 2.0 / 127.0, 2.0 / 255.0,
+ 2.0 / 511.0, 2.0 / 1023.0, 2.0 / 2047.0, 2.0 / 4095.0,
+ 2.0 / 8191.0, 2.0 / 16383.0, 2.0 / 32767.0, 2.0 / 65535.0,
+ -4.0 / 5.0, -2.0 / 5.0, 2.0 / 5.0, 4.0 / 5.0, -8.0 / 9.0,
+ -4.0 / 9.0, -2.0 / 9.0, 2.0 / 9.0, 4.0 / 9.0, 8.0 / 9.0 };
+ static int base[3][9] = {
+ {1, 0, 2,},
+ {17, 18, 0, 19, 20,},
+ {21, 1, 22, 23, 0, 24, 25, 2, 26}};
+ int i, j, k, l, len;
+ real *table;
+ static int tablen[3] = {3, 5, 9};
+ static int *itable, *tables[3] =
+ {grp_3tab, grp_5tab, grp_9tab};
+
+ for (i = 0; i < 3; i++)
+ {
+ itable = tables[i];
+ len = tablen[i];
+ for (j = 0; j < len; j++)
+ for (k = 0; k < len; k++)
+ for (l = 0; l < len; l++)
+ {
+ *itable++ = base[i][l];
+ *itable++ = base[i][k];
+ *itable++ = base[i][j];
+ }
+ }
+
+ for (k = 0; k < 27; k++)
+ {
+ double m = mulmul[k];
+
+ table = mpg123_muls[k];
+ for (j = 3, i = 0; i < 63; i++, j--)
+ *table++ = m * pow(2.0, (double) j / 3.0);
+ *table++ = 0.0;
+ }
+}
+
+void II_step_one(unsigned int *bit_alloc, int *scale, struct frame *fr)
+{
+ int stereo = fr->stereo - 1;
+ int sblimit = fr->II_sblimit;
+ int jsbound = fr->jsbound;
+ int sblimit2 = fr->II_sblimit << stereo;
+ struct al_table *alloc1 = fr->alloc;
+ int i;
+ static unsigned int scfsi_buf[64];
+ unsigned int *scfsi, *bita;
+ int sc, step;
+
+ bita = bit_alloc;
+ if (stereo)
+ {
+ for (i = jsbound; i > 0; i--, alloc1 += (1 << step))
+ {
+ *bita++ = (char) mpg123_getbits(step = alloc1->bits);
+ *bita++ = (char) mpg123_getbits(step);
+ }
+ for (i = sblimit - jsbound; i > 0; i--, alloc1 += (1 << step))
+ {
+ bita[0] = (char) mpg123_getbits(step = alloc1->bits);
+ bita[1] = bita[0];
+ bita += 2;
+ }
+ bita = bit_alloc;
+ scfsi = scfsi_buf;
+ for (i = sblimit2; i; i--)
+ if (*bita++)
+ *scfsi++ = (char) mpg123_getbits_fast(2);
+ }
+ else
+ /* mono */
+ {
+ for (i = sblimit; i; i--, alloc1 += (1 << step))
+ *bita++ = (char) mpg123_getbits(step = alloc1->bits);
+ bita = bit_alloc;
+ scfsi = scfsi_buf;
+ for (i = sblimit; i; i--)
+ if (*bita++)
+ *scfsi++ = (char) mpg123_getbits_fast(2);
+ }
+
+ bita = bit_alloc;
+ scfsi = scfsi_buf;
+ for (i = sblimit2; i; i--)
+ if (*bita++)
+ switch (*scfsi++)
+ {
+ case 0:
+ *scale++ = mpg123_getbits_fast(6);
+ *scale++ = mpg123_getbits_fast(6);
+ *scale++ = mpg123_getbits_fast(6);
+ break;
+ case 1:
+ *scale++ = sc = mpg123_getbits_fast(6);
+ *scale++ = sc;
+ *scale++ = mpg123_getbits_fast(6);
+ break;
+ case 2:
+ *scale++ = sc = mpg123_getbits_fast(6);
+ *scale++ = sc;
+ *scale++ = sc;
+ break;
+ default: /* case 3 */
+ *scale++ = mpg123_getbits_fast(6);
+ *scale++ = sc = mpg123_getbits_fast(6);
+ *scale++ = sc;
+ break;
+ }
+
+}
+
+void II_step_two(unsigned int *bit_alloc, real fraction[2][4][SBLIMIT], int *scale, struct frame *fr, int x1)
+{
+ int i, j, k, ba;
+ int stereo = fr->stereo;
+ int sblimit = fr->II_sblimit;
+ int jsbound = fr->jsbound;
+ struct al_table *alloc2, *alloc1 = fr->alloc;
+ unsigned int *bita = bit_alloc;
+ int d1, step;
+
+ for (i = 0; i < jsbound; i++, alloc1 += (1 << step))
+ {
+ step = alloc1->bits;
+ for (j = 0; j < stereo; j++)
+ {
+ if ((ba = *bita++))
+ {
+ k = (alloc2 = alloc1 + ba)->bits;
+ if ((d1 = alloc2->d) < 0)
+ {
+ real cm = mpg123_muls[k][scale[x1]];
+
+ fraction[j][0][i] = ((real) ((int) mpg123_getbits(k) + d1)) * cm;
+ fraction[j][1][i] = ((real) ((int) mpg123_getbits(k) + d1)) * cm;
+ fraction[j][2][i] = ((real) ((int) mpg123_getbits(k) + d1)) * cm;
+ }
+ else
+ {
+ static int *table[] =
+ {0, 0, 0, grp_3tab, 0, grp_5tab, 0, 0, 0, grp_9tab};
+ unsigned int idx, *tab, m = scale[x1];
+
+ idx = (unsigned int) mpg123_getbits(k);
+ tab = (unsigned int *) (table[d1] + idx + idx + idx);
+ fraction[j][0][i] = mpg123_muls[*tab++][m];
+ fraction[j][1][i] = mpg123_muls[*tab++][m];
+ fraction[j][2][i] = mpg123_muls[*tab][m];
+ }
+ scale += 3;
+ }
+ else
+ fraction[j][0][i] = fraction[j][1][i] = fraction[j][2][i] = 0.0;
+ }
+ }
+
+ for (i = jsbound; i < sblimit; i++, alloc1 += (1 << step))
+ {
+ step = alloc1->bits;
+ bita++; /* channel 1 and channel 2 bitalloc are the same */
+ if ((ba = *bita++))
+ {
+ k = (alloc2 = alloc1 + ba)->bits;
+ if ((d1 = alloc2->d) < 0)
+ {
+ real cm;
+
+ cm = mpg123_muls[k][scale[x1 + 3]];
+ fraction[1][0][i] = (fraction[0][0][i] = (real) ((int) mpg123_getbits(k) + d1)) * cm;
+ fraction[1][1][i] = (fraction[0][1][i] = (real) ((int) mpg123_getbits(k) + d1)) * cm;
+ fraction[1][2][i] = (fraction[0][2][i] = (real) ((int) mpg123_getbits(k) + d1)) * cm;
+ cm = mpg123_muls[k][scale[x1]];
+ fraction[0][0][i] *= cm;
+ fraction[0][1][i] *= cm;
+ fraction[0][2][i] *= cm;
+ }
+ else
+ {
+ static int *table[] =
+ {0, 0, 0, grp_3tab, 0, grp_5tab, 0, 0, 0, grp_9tab};
+ unsigned int idx, *tab, m1, m2;
+
+ m1 = scale[x1];
+ m2 = scale[x1 + 3];
+ idx = (unsigned int) mpg123_getbits(k);
+ tab = (unsigned int *) (table[d1] + idx + idx + idx);
+ fraction[0][0][i] = mpg123_muls[*tab][m1];
+ fraction[1][0][i] = mpg123_muls[*tab++][m2];
+ fraction[0][1][i] = mpg123_muls[*tab][m1];
+ fraction[1][1][i] = mpg123_muls[*tab++][m2];
+ fraction[0][2][i] = mpg123_muls[*tab][m1];
+ fraction[1][2][i] = mpg123_muls[*tab][m2];
+ }
+ scale += 6;
+ }
+ else
+ {
+ fraction[0][0][i] = fraction[0][1][i] = fraction[0][2][i] =
+ fraction[1][0][i] = fraction[1][1][i] = fraction[1][2][i] = 0.0;
+ }
+/*
+ should we use individual scalefac for channel 2 or
+ is the current way the right one , where we just copy channel 1 to
+ channel 2 ??
+ The current 'strange' thing is, that we throw away the scalefac
+ values for the second channel ...!!
+ -> changed .. now we use the scalefac values of channel one !!
+ */
+ }
+
+ if (sblimit > (fr->down_sample_sblimit))
+ sblimit = fr->down_sample_sblimit;
+
+ for (i = sblimit; i < SBLIMIT; i++)
+ for (j = 0; j < stereo; j++)
+ fraction[j][0][i] = fraction[j][1][i] = fraction[j][2][i] = 0.0;
+
+}
+
+static void II_select_table(struct frame *fr)
+{
+ static int translate[3][2][16] = {
+ {{0, 2, 2, 2, 2, 2, 2, 0, 0, 0, 1, 1, 1, 1, 1, 0},
+ {0, 2, 2, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0}},
+ {{0, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ {0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
+ {{0, 3, 3, 3, 3, 3, 3, 0, 0, 0, 1, 1, 1, 1, 1, 0},
+ {0, 3, 3, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0}}
+ };
+
+ int table, sblim;
+ static struct al_table *tables[5] =
+ {alloc_0, alloc_1, alloc_2, alloc_3, alloc_4};
+ static int sblims[5] = { 27, 30, 8, 12, 30 };
+
+ if (fr->lsf)
+ table = 4;
+ else
+ table = translate[fr->sampling_frequency][2 - fr->stereo][fr->bitrate_index];
+ sblim = sblims[table];
+
+ fr->alloc = tables[table];
+ fr->II_sblimit = sblim;
+}
+
+
+int mpg123_do_layer2(struct frame *fr)
+{
+ int i, j;
+ int stereo = fr->stereo;
+ real fraction[2][4][SBLIMIT]; /* pick_table clears unused subbands */
+ unsigned int bit_alloc[64];
+ int scale[192];
+ int single = fr->single;
+
+ II_select_table(fr);
+ fr->jsbound = (fr->mode == MPG_MD_JOINT_STEREO) ?
+ (fr->mode_ext << 2) + 4 : fr->II_sblimit;
+ if (fr->jsbound > fr->II_sblimit)
+ fr->jsbound = fr->II_sblimit;
+
+ if (stereo == 1 || single == 3)
+ single = 0;
+
+ II_step_one(bit_alloc, scale, fr);
+
+ for (i = 0; i < SCALE_BLOCK; i++)
+ {
+ II_step_two(bit_alloc, fraction, scale, fr, i >> 2);
+ for (j = 0; j < 3; j++)
+ {
+ if (single >= 0)
+ {
+ (fr->synth_mono) (fraction[single][j], mpg123_pcm_sample, &mpg123_pcm_point);
+ }
+ else
+ {
+ int p1 = mpg123_pcm_point;
+
+ (fr->synth) (fraction[0][j], 0, mpg123_pcm_sample, &p1);
+ (fr->synth) (fraction[1][j], 1, mpg123_pcm_sample, &mpg123_pcm_point);
+ }
+
+ /* if(mpg123_pcm_point >= audiobufsize)
+ audio_flush(outmode,ai); */
+ }
+ }
+/***
+ if (mpg123_info->output_audio)
+ {
+
+ mpg123_ip.add_vis_pcm(mpg123_ip.output->written_time(), mpg123_cfg.resolution == 16 ? FMT_S16_NE : FMT_U8,
+ mpg123_cfg.channels == 2 ? fr->stereo : 1, mpg123_pcm_point, mpg123_pcm_sample);
+
+ while (mpg123_ip.output->buffer_free() < mpg123_pcm_point && mpg123_info->going && mpg123_info->jump_to_time == -1)
+ xmms_usleep(10000);
+ if (mpg123_info->going && mpg123_info->jump_to_time == -1)
+ mpg123_ip.output->write_audio(mpg123_pcm_sample, mpg123_pcm_point);
+
+ }
+***/
+ mpg123_pcm_point = 0;
+
+ return 1;
+}
diff --git a/src/libmpg123/layer3.c b/src/libmpg123/layer3.c
new file mode 100755
index 0000000..c7cb8cd
--- /dev/null
+++ b/src/libmpg123/layer3.c
@@ -0,0 +1,2100 @@
+
+/*
+ * Mpeg Layer-3 audio decoder
+ * --------------------------
+ * copyright (c) 1995-1999 by Michael Hipp.
+ * All rights reserved. See also 'README'
+ *
+ * Optimize-TODO: put short bands into the band-field without the stride of 3 reals
+ * Length-optimze: unify long and short band code where it is possible
+ */
+
+#include <stdlib.h>
+#include "mpg123.h"
+#include "huffman.h"
+
+#include "getbits.h"
+
+static real ispow[8207];
+static real aa_ca[8], aa_cs[8];
+static real COS1[12][6];
+static real win[4][36];
+static real win1[4][36];
+static real gainpow2[256 + 118 + 4];
+real COS9[9];
+static real COS6_1, COS6_2;
+real tfcos36[9];
+static real tfcos12[3];
+#define NEW_DCT9
+#ifdef NEW_DCT9
+static real cos9[3], cos18[3];
+#endif
+
+struct bandInfoStruct
+{
+ int longIdx[23];
+ int longDiff[22];
+ int shortIdx[14];
+ int shortDiff[13];
+};
+
+int longLimit[9][23];
+int shortLimit[9][14];
+
+/* Used by the getbits macros */
+static unsigned long rval;
+static unsigned char rval_uc;
+
+struct bandInfoStruct bandInfo[9] =
+{
+/* MPEG 1.0 */
+ { {0,4,8,12,16,20,24,30,36,44,52,62,74, 90,110,134,162,196,238,288,342,418,576},
+ {4,4,4,4,4,4,6,6,8, 8,10,12,16,20,24,28,34,42,50,54, 76,158},
+ {0,4*3,8*3,12*3,16*3,22*3,30*3,40*3,52*3,66*3, 84*3,106*3,136*3,192*3},
+ {4,4,4,4,6,8,10,12,14,18,22,30,56} } ,
+
+ { {0,4,8,12,16,20,24,30,36,42,50,60,72, 88,106,128,156,190,230,276,330,384,576},
+ {4,4,4,4,4,4,6,6,6, 8,10,12,16,18,22,28,34,40,46,54, 54,192},
+ {0,4*3,8*3,12*3,16*3,22*3,28*3,38*3,50*3,64*3, 80*3,100*3,126*3,192*3},
+ {4,4,4,4,6,6,10,12,14,16,20,26,66} } ,
+
+ { {0,4,8,12,16,20,24,30,36,44,54,66,82,102,126,156,194,240,296,364,448,550,576} ,
+ {4,4,4,4,4,4,6,6,8,10,12,16,20,24,30,38,46,56,68,84,102, 26} ,
+ {0,4*3,8*3,12*3,16*3,22*3,30*3,42*3,58*3,78*3,104*3,138*3,180*3,192*3} ,
+ {4,4,4,4,6,8,12,16,20,26,34,42,12} } ,
+
+/* MPEG 2.0 */
+ { {0,6,12,18,24,30,36,44,54,66,80,96,116,140,168,200,238,284,336,396,464,522,576},
+ {6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54 } ,
+ {0,4*3,8*3,12*3,18*3,24*3,32*3,42*3,56*3,74*3,100*3,132*3,174*3,192*3} ,
+ {4,4,4,6,6,8,10,14,18,26,32,42,18 } } ,
+/*
+ { {0,6,12,18,24,30,36,44,54,66,80,96,114,136,162,194,232,278,330,394,464,540,576},
+ {6,6,6,6,6,6,8,10,12,14,16,18,22,26,32,38,46,52,64,70,76,36 } ,
+*/
+/* changed 19th value fropm 330 to 332 */
+ { {0,6,12,18,24,30,36,44,54,66,80,96,114,136,162,194,232,278,332,394,464,540,576},
+ {6,6,6,6,6,6,8,10,12,14,16,18,22,26,32,38,46,54,62,70,76,36 } ,
+ {0,4*3,8*3,12*3,18*3,26*3,36*3,48*3,62*3,80*3,104*3,136*3,180*3,192*3} ,
+ {4,4,4,6,8,10,12,14,18,24,32,44,12 } } ,
+
+ { {0,6,12,18,24,30,36,44,54,66,80,96,116,140,168,200,238,284,336,396,464,522,576},
+ {6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54 },
+ {0,4*3,8*3,12*3,18*3,26*3,36*3,48*3,62*3,80*3,104*3,134*3,174*3,192*3},
+ {4,4,4,6,8,10,12,14,18,24,30,40,18 } } ,
+/* MPEG 2.5 */
+ { {0,6,12,18,24,30,36,44,54,66,80,96,116,140,168,200,238,284,336,396,464,522,576} ,
+ {6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54},
+ {0,12,24,36,54,78,108,144,186,240,312,402,522,576},
+ {4,4,4,6,8,10,12,14,18,24,30,40,18} },
+ { {0,6,12,18,24,30,36,44,54,66,80,96,116,140,168,200,238,284,336,396,464,522,576} ,
+ {6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54},
+ {0,12,24,36,54,78,108,144,186,240,312,402,522,576},
+ {4,4,4,6,8,10,12,14,18,24,30,40,18} },
+ { {0,12,24,36,48,60,72,88,108,132,160,192,232,280,336,400,476,566,568,570,572,574,576},
+ {12,12,12,12,12,12,16,20,24,28,32,40,48,56,64,76,90,2,2,2,2,2},
+ {0, 24, 48, 72,108,156,216,288,372,480,486,492,498,576},
+ {8,8,8,12,16,20,24,28,36,2,2,2,26} } ,
+};
+
+static int mapbuf0[9][152];
+static int mapbuf1[9][156];
+static int mapbuf2[9][44];
+static int *map[9][3];
+static int *mapend[9][3];
+
+static unsigned int n_slen2[512]; /* MPEG 2.0 slen for 'normal' mode */
+static unsigned int i_slen2[256]; /* MPEG 2.0 slen for intensity stereo */
+
+static real tan1_1[16], tan2_1[16], tan1_2[16], tan2_2[16];
+static real pow1_1[2][16], pow2_1[2][16], pow1_2[2][16], pow2_2[2][16];
+
+/*
+ * init tables for layer-3
+ */
+void mpg123_init_layer3(int down_sample_sblimit)
+{
+ int i, j, k, l;
+
+ for (i = -256; i < 118 + 4; i++)
+ gainpow2[i + 256] = pow((double) 2.0, -0.25 * (double) (i + 210));
+
+ for (i = 0; i < 8207; i++)
+ ispow[i] = pow((double) i, (double) 4.0 / 3.0);
+
+ for (i = 0; i < 8; i++)
+ {
+ static double Ci[8] =
+ {-0.6, -0.535, -0.33, -0.185, -0.095, -0.041, -0.0142, -0.0037};
+ double sq = sqrt(1.0 + Ci[i] * Ci[i]);
+
+ aa_cs[i] = 1.0 / sq;
+ aa_ca[i] = Ci[i] / sq;
+ }
+
+ for (i = 0; i < 18; i++)
+ {
+ win[0][i] = win[1][i] = 0.5 * sin(M_PI / 72.0 * (double) (2 * (i + 0) + 1)) / cos(M_PI * (double) (2 * (i + 0) + 19) / 72.0);
+ win[0][i + 18] = win[3][i + 18] = 0.5 * sin(M_PI / 72.0 * (double) (2 * (i + 18) + 1)) / cos(M_PI * (double) (2 * (i + 18) + 19) / 72.0);
+ }
+ for (i = 0; i < 6; i++)
+ {
+ win[1][i + 18] = 0.5 / cos(M_PI * (double) (2 * (i + 18) + 19) / 72.0);
+ win[3][i + 12] = 0.5 / cos(M_PI * (double) (2 * (i + 12) + 19) / 72.0);
+ win[1][i + 24] = 0.5 * sin(M_PI / 24.0 * (double) (2 * i + 13)) / cos(M_PI * (double) (2 * (i + 24) + 19) / 72.0);
+ win[1][i + 30] = win[3][i] = 0.0;
+ win[3][i + 6] = 0.5 * sin(M_PI / 24.0 * (double) (2 * i + 1)) / cos(M_PI * (double) (2 * (i + 6) + 19) / 72.0);
+ }
+
+ for (i = 0; i < 9; i++)
+ COS9[i] = cos(M_PI / 18.0 * (double) i);
+
+ for (i = 0; i < 9; i++)
+ tfcos36[i] = 0.5 / cos(M_PI * (double) (i * 2 + 1) / 36.0);
+ for (i = 0; i < 3; i++)
+ tfcos12[i] = 0.5 / cos(M_PI * (double) (i * 2 + 1) / 12.0);
+
+ COS6_1 = cos(M_PI / 6.0 * (double) 1);
+ COS6_2 = cos(M_PI / 6.0 * (double) 2);
+
+#ifdef NEW_DCT9
+ cos9[0] = cos(1.0 * M_PI / 9.0);
+ cos9[1] = cos(5.0 * M_PI / 9.0);
+ cos9[2] = cos(7.0 * M_PI / 9.0);
+ cos18[0] = cos(1.0 * M_PI / 18.0);
+ cos18[1] = cos(11.0 * M_PI / 18.0);
+ cos18[2] = cos(13.0 * M_PI / 18.0);
+#endif
+
+ for (i = 0; i < 12; i++)
+ {
+ win[2][i] = 0.5 * sin(M_PI / 24.0 * (double) (2 * i + 1)) / cos(M_PI * (double) (2 * i + 7) / 24.0);
+ for (j = 0; j < 6; j++)
+ COS1[i][j] = cos(M_PI / 24.0 * (double) ((2 * i + 7) * (2 * j + 1)));
+ }
+
+ for (j = 0; j < 4; j++)
+ {
+ static int len[4] = { 36, 36, 12, 36 };
+
+ for (i = 0; i < len[j]; i += 2)
+ win1[j][i] = +win[j][i];
+ for (i = 1; i < len[j]; i += 2)
+ win1[j][i] = -win[j][i];
+ }
+
+ for (i = 0; i < 16; i++)
+ {
+ double t = tan((double) i * M_PI / 12.0);
+
+ tan1_1[i] = t / (1.0 + t);
+ tan2_1[i] = 1.0 / (1.0 + t);
+ tan1_2[i] = M_SQRT2 * t / (1.0 + t);
+ tan2_2[i] = M_SQRT2 / (1.0 + t);
+
+ for (j = 0; j < 2; j++)
+ {
+ double base = pow(2.0, -0.25 * (j + 1.0));
+ double p1 = 1.0, p2 = 1.0;
+
+ if (i > 0)
+ {
+ if (i & 1)
+ p1 = pow(base, (i + 1.0) * 0.5);
+ else
+ p2 = pow(base, i * 0.5);
+ }
+ pow1_1[j][i] = p1;
+ pow2_1[j][i] = p2;
+ pow1_2[j][i] = M_SQRT2 * p1;
+ pow2_2[j][i] = M_SQRT2 * p2;
+ }
+ }
+
+ for (j = 0; j < 9; j++)
+ {
+ struct bandInfoStruct *bi = &bandInfo[j];
+ int *mp;
+ int cb, lwin;
+ int *bdf;
+
+ mp = map[j][0] = mapbuf0[j];
+ bdf = bi->longDiff;
+ for (i = 0, cb = 0; cb < 8; cb++, i += *bdf++)
+ {
+ *mp++ = (*bdf) >> 1;
+ *mp++ = i;
+ *mp++ = 3;
+ *mp++ = cb;
+ }
+ bdf = bi->shortDiff + 3;
+ for (cb = 3; cb < 13; cb++)
+ {
+ int l = (*bdf++) >> 1;
+
+ for (lwin = 0; lwin < 3; lwin++)
+ {
+ *mp++ = l;
+ *mp++ = i + lwin;
+ *mp++ = lwin;
+ *mp++ = cb;
+ }
+ i += 6 * l;
+ }
+ mapend[j][0] = mp;
+
+ mp = map[j][1] = mapbuf1[j];
+ bdf = bi->shortDiff + 0;
+ for (i = 0, cb = 0; cb < 13; cb++)
+ {
+ int l = (*bdf++) >> 1;
+
+ for (lwin = 0; lwin < 3; lwin++)
+ {
+ *mp++ = l;
+ *mp++ = i + lwin;
+ *mp++ = lwin;
+ *mp++ = cb;
+ }
+ i += 6 * l;
+ }
+ mapend[j][1] = mp;
+
+ mp = map[j][2] = mapbuf2[j];
+ bdf = bi->longDiff;
+ for (cb = 0; cb < 22; cb++)
+ {
+ *mp++ = (*bdf++) >> 1;
+ *mp++ = cb;
+ }
+ mapend[j][2] = mp;
+
+ }
+
+ for (j = 0; j < 9; j++)
+ {
+ for (i = 0; i < 23; i++)
+ {
+ longLimit[j][i] = (bandInfo[j].longIdx[i] - 1 + 8) / 18 + 1;
+ if (longLimit[j][i] > (down_sample_sblimit))
+ longLimit[j][i] = down_sample_sblimit;
+ }
+ for (i = 0; i < 14; i++)
+ {
+ shortLimit[j][i] = (bandInfo[j].shortIdx[i] - 1) / 18 + 1;
+ if (shortLimit[j][i] > (down_sample_sblimit))
+ shortLimit[j][i] = down_sample_sblimit;
+ }
+ }
+
+ for (i = 0; i < 5; i++)
+ {
+ for (j = 0; j < 6; j++)
+ {
+ for (k = 0; k < 6; k++)
+ {
+ int n = k + j * 6 + i * 36;
+
+ i_slen2[n] = i | (j << 3) | (k << 6) | (3 << 12);
+ }
+ }
+ }
+ for (i = 0; i < 4; i++)
+ {
+ for (j = 0; j < 4; j++)
+ {
+ for (k = 0; k < 4; k++)
+ {
+ int n = k + j * 4 + i * 16;
+
+ i_slen2[n + 180] = i | (j << 3) | (k << 6) | (4 << 12);
+ }
+ }
+ }
+ for (i = 0; i < 4; i++)
+ {
+ for (j = 0; j < 3; j++)
+ {
+ int n = j + i * 3;
+
+ i_slen2[n + 244] = i | (j << 3) | (5 << 12);
+ n_slen2[n + 500] = i | (j << 3) | (2 << 12) | (1 << 15);
+ }
+ }
+
+ for (i = 0; i < 5; i++)
+ {
+ for (j = 0; j < 5; j++)
+ {
+ for (k = 0; k < 4; k++)
+ {
+ for (l = 0; l < 4; l++)
+ {
+ int n = l + k * 4 + j * 16 + i * 80;
+
+ n_slen2[n] = i | (j << 3) | (k << 6) | (l << 9) | (0 << 12);
+ }
+ }
+ }
+ }
+ for (i = 0; i < 5; i++)
+ {
+ for (j = 0; j < 5; j++)
+ {
+ for (k = 0; k < 4; k++)
+ {
+ int n = k + j * 4 + i * 20;
+
+ n_slen2[n + 400] = i | (j << 3) | (k << 6) | (1 << 12);
+ }
+ }
+ }
+}
+
+/*
+ * read additional side information (for MPEG 1 and MPEG 2)
+ */
+static int III_get_side_info(struct III_sideinfo *si, int stereo,
+ int ms_stereo, long sfreq, int single, int lsf)
+{
+ int ch, gr;
+ int powdiff = (single == 3) ? 4 : 0;
+
+ static const int tabs[2][5] = { {2, 9, 5, 3, 4}, {1, 8, 1, 2, 9} };
+ const int *tab = tabs[lsf];
+
+ si->main_data_begin = mpg123_getbits(tab[1]);
+ if (stereo == 1)
+ si->private_bits = mpg123_getbits_fast(tab[2]);
+ else
+ si->private_bits = mpg123_getbits_fast(tab[3]);
+
+ if (!lsf)
+ {
+ for (ch = 0; ch < stereo; ch++)
+ {
+ si->ch[ch].gr[0].scfsi = -1;
+ si->ch[ch].gr[1].scfsi = mpg123_getbits_fast(4);
+ }
+ }
+
+ for (gr = 0; gr < tab[0]; gr++)
+ {
+ for (ch = 0; ch < stereo; ch++)
+ {
+ register struct gr_info_s *gr_info = &(si->ch[ch].gr[gr]);
+
+ gr_info->part2_3_length = mpg123_getbits(12);
+ gr_info->big_values = mpg123_getbits(9);
+ if (gr_info->big_values > 288)
+ {
+ /* fprintf(stderr, "big_values too large!\n"); */
+ /* gr_info->big_values = 288; */
+ return 0;
+ }
+ gr_info->pow2gain = gainpow2 + 256 - mpg123_getbits_fast(8) + powdiff;
+ if (ms_stereo)
+ gr_info->pow2gain += 2;
+ gr_info->scalefac_compress = mpg123_getbits(tab[4]);
+
+ if (mpg123_get1bit())
+ { /* window switch flag */
+ int i;
+
+ gr_info->block_type = mpg123_getbits_fast(2);
+ gr_info->mixed_block_flag = mpg123_get1bit();
+ gr_info->table_select[0] = mpg123_getbits_fast(5);
+ gr_info->table_select[1] = mpg123_getbits_fast(5);
+ /*
+ * table_select[2] not needed, because
+ * there is no region2, but to satisfy
+ * some verifications tools we set it
+ * either.
+ */
+ gr_info->table_select[2] = 0;
+ for (i = 0; i < 3; i++)
+ gr_info->full_gain[i] = gr_info->pow2gain + (mpg123_getbits_fast(3) << 3);
+
+ if (gr_info->block_type == 0)
+ {
+ /* fprintf(stderr, "Blocktype == 0 and window-switching == 1 not allowed.\n"); */
+ /* exit(1); */
+ return 0;
+ }
+
+ /* region_count/start parameters are implicit in this case. */
+ if (!lsf || gr_info->block_type == 2)
+ gr_info->region1start = 36 >> 1;
+ else
+ {
+ /* check this again for 2.5 and sfreq=8 */
+ if (sfreq == 8)
+ gr_info->region1start = 108 >> 1;
+ else
+ gr_info->region1start = 54 >> 1;
+ }
+ gr_info->region2start = 576 >> 1;
+ }
+ else
+ {
+ int i, r0c, r1c;
+
+ for (i = 0; i < 3; i++)
+ gr_info->table_select[i] = mpg123_getbits_fast(5);
+ r0c = mpg123_getbits_fast(4);
+ r1c = mpg123_getbits_fast(3);
+ gr_info->region1start = bandInfo[sfreq].longIdx[r0c + 1] >> 1;
+ if (r0c + r1c + 2 > 22)
+ gr_info->region2start = 576 >> 1;
+ else
+ gr_info->region2start = bandInfo[sfreq].longIdx[r0c + 1 + r1c + 1] >> 1;
+ gr_info->block_type = 0;
+ gr_info->mixed_block_flag = 0;
+ }
+ if (!lsf)
+ gr_info->preflag = mpg123_get1bit();
+ gr_info->scalefac_scale = mpg123_get1bit();
+ gr_info->count1table_select = mpg123_get1bit();
+ }
+ }
+ return 1;
+}
+
+
+/*
+ * read scalefactors
+ */
+static int III_get_scale_factors_1(int *scf, struct gr_info_s *gr_info)
+{
+ static const unsigned char slen[2][16] = {
+ {0, 0, 0, 0, 3, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4},
+ {0, 1, 2, 3, 0, 1, 2, 3, 1, 2, 3, 1, 2, 3, 2, 3}
+ };
+ int numbits;
+ int num0 = slen[0][gr_info->scalefac_compress];
+ int num1 = slen[1][gr_info->scalefac_compress];
+
+ if (gr_info->block_type == 2)
+ {
+ int i = 18;
+
+ numbits = (num0 + num1) * 18;
+
+ if (gr_info->mixed_block_flag)
+ {
+ for (i = 8; i; i--)
+ *scf++ = mpg123_getbits_fast(num0);
+ i = 9;
+ numbits -= num0; /* num0 * 17 + num1 * 18 */
+ }
+
+ for (; i; i--)
+ *scf++ = mpg123_getbits_fast(num0);
+ for (i = 18; i; i--)
+ *scf++ = mpg123_getbits_fast(num1);
+ *scf++ = 0;
+ *scf++ = 0;
+ *scf++ = 0; /* short[13][0..2] = 0 */
+ }
+ else
+ {
+ int i;
+ int scfsi = gr_info->scfsi;
+
+ if (scfsi < 0)
+ { /* scfsi < 0 => granule == 0 */
+ for (i = 11; i; i--)
+ *scf++ = mpg123_getbits_fast(num0);
+ for (i = 10; i; i--)
+ *scf++ = mpg123_getbits_fast(num1);
+ numbits = (num0 + num1) * 10 + num0;
+ *scf++ = 0;
+ }
+ else
+ {
+ numbits = 0;
+ if (!(scfsi & 0x8))
+ {
+ for (i = 0; i < 6; i++)
+ *scf++ = mpg123_getbits_fast(num0);
+ numbits += num0 * 6;
+ }
+ else
+ {
+ scf += 6;
+ }
+
+ if (!(scfsi & 0x4))
+ {
+ for (i = 0; i < 5; i++)
+ *scf++ = mpg123_getbits_fast(num0);
+ numbits += num0 * 5;
+ }
+ else
+ {
+ scf += 5;
+ }
+
+ if (!(scfsi & 0x2))
+ {
+ for (i = 0; i < 5; i++)
+ *scf++ = mpg123_getbits_fast(num1);
+ numbits += num1 * 5;
+ }
+ else
+ {
+ scf += 5;
+ }
+
+ if (!(scfsi & 0x1))
+ {
+ for (i = 0; i < 5; i++)
+ *scf++ = mpg123_getbits_fast(num1);
+ numbits += num1 * 5;
+ }
+ else
+ {
+ scf += 5;
+ }
+ *scf++ = 0; /* no l[21] in original sources */
+ }
+ }
+ return numbits;
+}
+
+static int III_get_scale_factors_2(int *scf, struct gr_info_s *gr_info, int i_stereo)
+{
+ unsigned char *pnt;
+ int i, j, n = 0, numbits = 0;
+ unsigned int slen;
+
+ static unsigned char stab[3][6][4] =
+ {
+ {{6, 5, 5, 5}, {6, 5, 7, 3}, {11, 10, 0, 0},
+ {7, 7, 7, 0}, {6, 6, 6, 3}, {8, 8, 5, 0}},
+ {{9, 9, 9, 9}, {9, 9, 12, 6}, {18, 18, 0, 0},
+ {12, 12, 12, 0}, {12, 9, 9, 6}, {15, 12, 9, 0}},
+ {{6, 9, 9, 9}, {6, 9, 12, 6}, {15, 18, 0, 0},
+ {6, 15, 12, 0}, {6, 12, 9, 6}, {6, 18, 9, 0}}
+ };
+
+ if (i_stereo) /* i_stereo AND second channel -> mpg123_do_layer3() checks this */
+ slen = i_slen2[gr_info->scalefac_compress >> 1];
+ else
+ slen = n_slen2[gr_info->scalefac_compress];
+
+ gr_info->preflag = (slen >> 15) & 0x1;
+
+ n = 0;
+ if (gr_info->block_type == 2)
+ {
+ n++;
+ if (gr_info->mixed_block_flag)
+ n++;
+ }
+
+ pnt = stab[n][(slen >> 12) & 0x7];
+
+ for (i = 0; i < 4; i++)
+ {
+ int num = slen & 0x7;
+
+ slen >>= 3;
+ if (num)
+ {
+ for (j = 0; j < (int) (pnt[i]); j++)
+ *scf++ = mpg123_getbits_fast(num);
+ numbits += pnt[i] * num;
+ }
+ else
+ {
+ for (j = 0; j < (int) (pnt[i]); j++)
+ *scf++ = 0;
+ }
+ }
+
+ n = (n << 1) + 1;
+ for (i = 0; i < n; i++)
+ *scf++ = 0;
+
+ return numbits;
+}
+
+static int pretab1[22] =
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 3, 3, 3, 2, 0};
+static int pretab2[22] =
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+/*
+ * Dequantize samples (includes huffman decoding)
+ */
+/* 24 is enough because tab13 has max. a 19 bit huffvector */
+#define BITSHIFT (int)((sizeof (long) - 1) * 8)
+
+#define REFRESH_MASK() \
+while(num < BITSHIFT) { \
+ mask |= ((unsigned long)mpg123_getbyte()) << (BITSHIFT - num); \
+ num += 8; \
+ part2remain -= 8; \
+}
+
+static int III_dequantize_sample(real xr[SBLIMIT][SSLIMIT], int *scf,
+ struct gr_info_s *gr_info, int sfreq, int part2bits)
+{
+ int shift = 1 + gr_info->scalefac_scale;
+ real *xrpnt = (real *) xr;
+ int l[3], l3;
+ int part2remain = gr_info->part2_3_length - part2bits;
+ int *me;
+
+ int num = mpg123_getbitoffset();
+ long mask;
+ /* we must split this, because for num==0 the shift is undefined if you do it in one step */
+ mask = ((unsigned long) mpg123_getbits(num)) << BITSHIFT;
+ mask <<= 8 - num;
+ part2remain -= num;
+
+ {
+ int bv = gr_info->big_values;
+ int region1 = gr_info->region1start;
+ int region2 = gr_info->region2start;
+
+ l3 = ((576 >> 1) - bv) >> 1;
+/*
+ * we may lose the 'odd' bit here !!
+ * check this later again
+ */
+ if (bv <= region1)
+ {
+ l[0] = bv;
+ l[1] = 0;
+ l[2] = 0;
+ }
+ else
+ {
+ l[0] = region1;
+ if (bv <= region2)
+ {
+ l[1] = bv - l[0];
+ l[2] = 0;
+ }
+ else
+ {
+ l[1] = region2 - l[0];
+ l[2] = bv - region2;
+ }
+ }
+ }
+
+ if (gr_info->block_type == 2)
+ {
+ /*
+ * decoding with short or mixed mode BandIndex table
+ */
+ int i, max[4];
+ int step = 0, lwin = 3, cb = 0;
+ register real v = 0.0;
+ register int *m, mc;
+
+ if (gr_info->mixed_block_flag)
+ {
+ max[3] = -1;
+ max[0] = max[1] = max[2] = 2;
+ m = map[sfreq][0];
+ me = mapend[sfreq][0];
+ }
+ else
+ {
+ max[0] = max[1] = max[2] = max[3] = -1;
+ /* max[3] not really needed in this case */
+ m = map[sfreq][1];
+ me = mapend[sfreq][1];
+ }
+
+ mc = 0;
+ for (i = 0; i < 2; i++)
+ {
+ int lp = l[i];
+ struct newhuff *h = ht + gr_info->table_select[i];
+
+ for (; lp; lp--, mc--)
+ {
+ register int x, y;
+ if ((!mc))
+ {
+ mc = *m++;
+ xrpnt = ((real *) xr) + (*m++);
+ lwin = *m++;
+ cb = *m++;
+ if (lwin == 3)
+ {
+ v = gr_info->pow2gain[(*scf++) << shift];
+ step = 1;
+ }
+ else
+ {
+ v = gr_info->full_gain[lwin][(*scf++) << shift];
+ step = 3;
+ }
+ }
+ {
+ register short *val = h->table;
+
+ REFRESH_MASK();
+ while ((y = *val++) < 0)
+ {
+ if (mask < 0)
+ val -= y;
+ num--;
+ mask <<= 1;
+ }
+ x = y >> 4;
+ y &= 0xf;
+ }
+ if (x == 15 && h->linbits)
+ {
+ max[lwin] = cb;
+ REFRESH_MASK();
+ x += ((unsigned long) mask) >> (BITSHIFT + 8 - h->linbits);
+ num -= h->linbits + 1;
+ mask <<= h->linbits;
+ if (mask < 0)
+ *xrpnt = -ispow[x] * v;
+ else
+ *xrpnt = ispow[x] * v;
+ mask <<= 1;
+ }
+ else if (x)
+ {
+ max[lwin] = cb;
+ if (mask < 0)
+ *xrpnt = -ispow[x] * v;
+ else
+ *xrpnt = ispow[x] * v;
+ num--;
+ mask <<= 1;
+ }
+ else
+ *xrpnt = 0.0;
+ xrpnt += step;
+ if (y == 15 && h->linbits)
+ {
+ max[lwin] = cb;
+ REFRESH_MASK();
+ y += ((unsigned long) mask) >> (BITSHIFT + 8 - h->linbits);
+ num -= h->linbits + 1;
+ mask <<= h->linbits;
+ if (mask < 0)
+ *xrpnt = -ispow[y] * v;
+ else
+ *xrpnt = ispow[y] * v;
+ mask <<= 1;
+ }
+ else if (y)
+ {
+ max[lwin] = cb;
+ if (mask < 0)
+ *xrpnt = -ispow[y] * v;
+ else
+ *xrpnt = ispow[y] * v;
+ num--;
+ mask <<= 1;
+ }
+ else
+ *xrpnt = 0.0;
+ xrpnt += step;
+ }
+ }
+
+ for (; l3 && (part2remain + num > 0); l3--)
+ {
+ struct newhuff *h = htc + gr_info->count1table_select;
+ register short *val = h->table, a;
+
+ REFRESH_MASK();
+ while ((a = *val++) < 0)
+ {
+ if (mask < 0)
+ val -= a;
+ num--;
+ mask <<= 1;
+ }
+ if (part2remain + num <= 0)
+ {
+ num -= part2remain + num;
+ break;
+ }
+
+ for (i = 0; i < 4; i++)
+ {
+ if (!(i & 1))
+ {
+ if (!mc)
+ {
+ mc = *m++;
+ xrpnt = ((real *) xr) + (*m++);
+ lwin = *m++;
+ cb = *m++;
+ if (lwin == 3)
+ {
+ v = gr_info->pow2gain[(*scf++) << shift];
+ step = 1;
+ }
+ else
+ {
+ v = gr_info->full_gain[lwin][(*scf++) << shift];
+ step = 3;
+ }
+ }
+ mc--;
+ }
+ if ((a & (0x8 >> i)))
+ {
+ max[lwin] = cb;
+ if (part2remain + num <= 0)
+ {
+ break;
+ }
+ if (mask < 0)
+ *xrpnt = -v;
+ else
+ *xrpnt = v;
+ num--;
+ mask <<= 1;
+ }
+ else
+ *xrpnt = 0.0;
+ xrpnt += step;
+ }
+ }
+
+ if (lwin < 3)
+ { /* short band? */
+ while (1)
+ {
+ /* HACK Prevent overflowing the xr buffer */
+ if (mc * 6 > &xr[SBLIMIT][SSLIMIT] - xrpnt)
+ return 1;
+
+ for (; mc > 0; mc--)
+ {
+ *xrpnt = 0.0;
+ xrpnt += 3; /* short band -> step=3 */
+ *xrpnt = 0.0;
+ xrpnt += 3;
+ }
+ if (m >= me)
+ break;
+ mc = *m++;
+ xrpnt = ((real *) xr) + *m++;
+ if (*m++ == 0)
+ break; /* optimize: field will be set to zero at the end of the function */
+ m++; /* cb */
+ }
+ }
+
+ gr_info->maxband[0] = max[0] + 1;
+ gr_info->maxband[1] = max[1] + 1;
+ gr_info->maxband[2] = max[2] + 1;
+ gr_info->maxbandl = max[3] + 1;
+
+ {
+ int rmax = max[0] > max[1] ? max[0] : max[1];
+
+ rmax = (rmax > max[2] ? rmax : max[2]) + 1;
+ gr_info->maxb = rmax ? shortLimit[sfreq][rmax] : longLimit[sfreq][max[3] + 1];
+ }
+
+ }
+ else
+ {
+ /*
+ * decoding with 'long' BandIndex table (block_type != 2)
+ */
+ int *pretab = gr_info->preflag ? pretab1 : pretab2;
+ int i, max = -1;
+ int cb = 0;
+ int *m = map[sfreq][2];
+ register real v = 0.0;
+ int mc = 0;
+
+ /*
+ * long hash table values
+ */
+ for (i = 0; i < 3; i++)
+ {
+ int lp = l[i];
+ struct newhuff *h = ht + gr_info->table_select[i];
+
+ for (; lp; lp--, mc--)
+ {
+ int x, y;
+
+ if (!mc)
+ {
+ mc = *m++;
+ cb = *m++;
+/* if (cb == 21) */
+/* v = 0.0; */
+/* else */
+ v = gr_info->pow2gain[((*scf++) + (*pretab++)) << shift];
+
+ }
+ {
+ register short *val = h->table;
+ REFRESH_MASK();
+ while ((y = *val++) < 0)
+ {
+ if (mask < 0)
+ val -= y;
+ num--;
+ mask <<= 1;
+ }
+ x = y >> 4;
+ y &= 0xf;
+ }
+
+ if (x == 15 && h->linbits)
+ {
+ max = cb;
+ REFRESH_MASK();
+ x += ((unsigned long) mask) >> (BITSHIFT + 8 - h->linbits);
+ num -= h->linbits + 1;
+ mask <<= h->linbits;
+ if (mask < 0)
+ *xrpnt++ = -ispow[x] * v;
+ else
+ *xrpnt++ = ispow[x] * v;
+ mask <<= 1;
+ }
+ else if (x)
+ {
+ max = cb;
+ if (mask < 0)
+ *xrpnt++ = -ispow[x] * v;
+ else
+ *xrpnt++ = ispow[x] * v;
+ num--;
+ mask <<= 1;
+ }
+ else
+ *xrpnt++ = 0.0;
+
+ if (y == 15 && h->linbits)
+ {
+ max = cb;
+ REFRESH_MASK();
+ y += ((unsigned long) mask) >> (BITSHIFT + 8 - h->linbits);
+ num -= h->linbits + 1;
+ mask <<= h->linbits;
+ if (mask < 0)
+ *xrpnt++ = -ispow[y] * v;
+ else
+ *xrpnt++ = ispow[y] * v;
+ mask <<= 1;
+ }
+ else if (y)
+ {
+ max = cb;
+ if (mask < 0)
+ *xrpnt++ = -ispow[y] * v;
+ else
+ *xrpnt++ = ispow[y] * v;
+ num--;
+ mask <<= 1;
+ }
+ else
+ *xrpnt++ = 0.0;
+ }
+ }
+
+ /*
+ * short (count1table) values
+ */
+ for (; l3 && (part2remain + num > 0); l3--)
+ {
+ struct newhuff *h = htc + gr_info->count1table_select;
+ register short *val = h->table, a;
+
+ REFRESH_MASK();
+ while ((a = *val++) < 0)
+ {
+ if (mask < 0)
+ val -= a;
+ num--;
+ mask <<= 1;
+ }
+ if (part2remain + num <= 0)
+ {
+ num -= part2remain + num;
+ break;
+ }
+
+ for (i = 0; i < 4; i++)
+ {
+ if (!(i & 1))
+ {
+ if (!mc)
+ {
+ mc = *m++;
+ cb = *m++;
+/* if (cb == 21) */
+/* v = 0.0; */
+/* else */
+ v = gr_info->pow2gain[((*scf++) + (*pretab++)) << shift];
+ }
+ mc--;
+ }
+ if ((a & (0x8 >> i)))
+ {
+ max = cb;
+ if (part2remain + num <= 0)
+ {
+ break;
+ }
+ if (mask < 0)
+ *xrpnt++ = -v;
+ else
+ *xrpnt++ = v;
+ num--;
+ mask <<= 1;
+ }
+ else
+ *xrpnt++ = 0.0;
+ }
+ }
+
+ gr_info->maxbandl = max + 1;
+ gr_info->maxb = longLimit[sfreq][gr_info->maxbandl];
+ }
+
+ part2remain += num;
+ mpg123_backbits(num);
+ num = 0;
+
+ while (xrpnt < &xr[SBLIMIT][0])
+ *xrpnt++ = 0.0;
+
+ while (part2remain > 16)
+ {
+ mpg123_getbits(16); /* Dismiss stuffing Bits */
+ part2remain -= 16;
+ }
+ if (part2remain > 0)
+ mpg123_getbits(part2remain);
+ else if (part2remain < 0)
+ {
+/* fprintf(stderr, "mpg123: Can't rewind stream by %d bits!\n", */
+/* -part2remain); */
+ return 1; /* -> error */
+ }
+ return 0;
+}
+
+/*
+ * III_stereo: calculate real channel values for Joint-I-Stereo-mode
+ */
+static void III_i_stereo(real xr_buf[2][SBLIMIT][SSLIMIT], int *scalefac, struct gr_info_s *gr_info, int sfreq, int ms_stereo, int lsf)
+{
+ real(*xr)[SBLIMIT * SSLIMIT] = (real(*)[SBLIMIT * SSLIMIT]) xr_buf;
+ struct bandInfoStruct *bi = &bandInfo[sfreq];
+
+ const real *tab1, *tab2;
+
+#if 1
+ int tab;
+ static const real *tabs[3][2][2] = {
+ {{tan1_1, tan2_1}, {tan1_2, tan2_2}},
+ {{pow1_1[0], pow2_1[0]}, {pow1_2[0], pow2_2[0]}},
+ {{pow1_1[1], pow2_1[1]}, {pow1_2[1], pow2_2[1]}}
+ };
+
+ tab = lsf + (gr_info->scalefac_compress & lsf);
+ tab1 = tabs[tab][ms_stereo][0];
+ tab2 = tabs[tab][ms_stereo][1];
+#else
+ if (lsf)
+ {
+ int p = gr_info->scalefac_compress & 0x1;
+
+ if (ms_stereo)
+ {
+ tab1 = pow1_2[p];
+ tab2 = pow2_2[p];
+ }
+ else
+ {
+ tab1 = pow1_1[p];
+ tab2 = pow2_1[p];
+ }
+ }
+ else
+ {
+ if (ms_stereo)
+ {
+ tab1 = tan1_2;
+ tab2 = tan2_2;
+ }
+ else
+ {
+ tab1 = tan1_1;
+ tab2 = tan2_1;
+ }
+ }
+#endif
+
+ if (gr_info->block_type == 2)
+ {
+ int lwin, do_l = 0;
+
+ if (gr_info->mixed_block_flag)
+ do_l = 1;
+
+ for (lwin = 0; lwin < 3; lwin++)
+ { /* process each window */
+ /* get first band with zero values */
+ int is_p, sb, idx, sfb = gr_info->maxband[lwin]; /* sfb is minimal 3 for mixed mode */
+
+ if (sfb > 3)
+ do_l = 0;
+
+ for (; sfb < 12; sfb++)
+ {
+ is_p = scalefac[sfb * 3 + lwin - gr_info->mixed_block_flag]; /* scale: 0-15 */
+ if (is_p != 7)
+ {
+ real t1, t2;
+
+ sb = bi->shortDiff[sfb];
+ idx = bi->shortIdx[sfb] + lwin;
+ t1 = tab1[is_p];
+ t2 = tab2[is_p];
+ for (; sb > 0; sb--, idx += 3)
+ {
+ real v = xr[0][idx];
+
+ xr[0][idx] = v * t1;
+ xr[1][idx] = v * t2;
+ }
+ }
+ }
+
+#if 1
+/* in the original: copy 10 to 11 , here: copy 11 to 12
+ maybe still wrong??? (copy 12 to 13?) */
+ is_p = scalefac[11 * 3 + lwin - gr_info->mixed_block_flag]; /* scale: 0-15 */
+ sb = bi->shortDiff[12];
+ idx = bi->shortIdx[12] + lwin;
+#else
+ is_p = scalefac[10 * 3 + lwin - gr_info->mixed_block_flag]; /* scale: 0-15 */
+ sb = bi->shortDiff[11];
+ idx = bi->shortIdx[11] + lwin;
+#endif
+ if (is_p != 7)
+ {
+ real t1, t2;
+ t1 = tab1[is_p];
+ t2 = tab2[is_p];
+ for (; sb > 0; sb--, idx += 3)
+ {
+ real v = xr[0][idx];
+ xr[0][idx] = v * t1;
+ xr[1][idx] = v * t2;
+ }
+ }
+ } /* end for(lwin; .. ; . ) */
+
+/* also check l-part, if ALL bands in the three windows are 'empty'
+ * and mode = mixed_mode
+ */
+ if (do_l)
+ {
+ int sfb = gr_info->maxbandl;
+ int idx = bi->longIdx[sfb];
+
+ for (; sfb < 8; sfb++)
+ {
+ int sb = bi->longDiff[sfb];
+ int is_p = scalefac[sfb]; /* scale: 0-15 */
+
+ if (is_p != 7)
+ {
+ real t1, t2;
+
+ t1 = tab1[is_p];
+ t2 = tab2[is_p];
+ for (; sb > 0; sb--, idx++)
+ {
+ real v = xr[0][idx];
+
+ xr[0][idx] = v * t1;
+ xr[1][idx] = v * t2;
+ }
+ }
+ else
+ idx += sb;
+ }
+ }
+ }
+ else
+ { /* ((gr_info->block_type != 2)) */
+ int sfb = gr_info->maxbandl;
+ int is_p, idx = bi->longIdx[sfb];
+
+/* hmm ... maybe the maxbandl stuff for i-stereo is buggy? */
+ if (sfb <= 21)
+ {
+ for (; sfb < 21; sfb++)
+ {
+ int sb = bi->longDiff[sfb];
+
+ is_p = scalefac[sfb]; /* scale: 0-15 */
+ if (is_p != 7)
+ {
+ real t1, t2;
+ t1 = tab1[is_p];
+ t2 = tab2[is_p];
+ for (; sb > 0; sb--, idx++)
+ {
+ real v = xr[0][idx];
+ xr[0][idx] = v * t1;
+ xr[1][idx] = v * t2;
+ }
+ }
+ else
+ idx += sb;
+ }
+
+ is_p = scalefac[20];
+ if (is_p != 7)
+ { /* copy l-band 20 to l-band 21 */
+ int sb;
+ real t1 = tab1[is_p], t2 = tab2[is_p];
+
+ for (sb = bi->longDiff[21]; sb > 0; sb--, idx++)
+ {
+ real v = xr[0][idx];
+
+ xr[0][idx] = v * t1;
+ xr[1][idx] = v * t2;
+ }
+ }
+ }
+ } /* ... */
+}
+
+static void III_antialias(real xr[SBLIMIT][SSLIMIT], struct gr_info_s *gr_info)
+{
+ int sblim;
+
+ if (gr_info->block_type == 2)
+ {
+ if (!gr_info->mixed_block_flag)
+ return;
+ sblim = 1;
+ }
+ else
+ {
+ sblim = gr_info->maxb - 1;
+ }
+
+ /* 31 alias-reduction operations between each pair of sub-bands */
+ /* with 8 butterflies between each pair */
+
+ {
+ int sb;
+ real *xr1 = (real *) xr[1];
+
+ if (sblim < 1 || sblim > SBLIMIT)
+ return;
+
+ for (sb = sblim; sb; sb--, xr1 += 10)
+ {
+ int ss;
+ real *cs = aa_cs, *ca = aa_ca;
+ real *xr2 = xr1;
+
+ for (ss = 7; ss >= 0; ss--)
+ { /* upper and lower butterfly inputs */
+ register real bu = *--xr2, bd = *xr1;
+
+ *xr2 = (bu * (*cs)) - (bd * (*ca));
+ *xr1++ = (bd * (*cs++)) + (bu * (*ca++));
+ }
+ }
+ }
+}
+
+/*
+ This is an optimized DCT from Jeff Tsay's maplay 1.2+ package.
+ Saved one multiplication by doing the 'twiddle factor' stuff
+ together with the window mul. (MH)
+
+ This uses Byeong Gi Lee's Fast Cosine Transform algorithm, but the
+ 9 point IDCT needs to be reduced further. Unfortunately, I don't
+ know how to do that, because 9 is not an even number. - Jeff.
+
+ ****************************************************************
+
+ 9 Point Inverse Discrete Cosine Transform
+
+ This piece of code is Copyright 1997 Mikko Tommila and is freely usable
+ by anybody. The algorithm itself is of course in the public domain.
+
+ Again derived heuristically from the 9-point WFTA.
+
+ The algorithm is optimized (?) for speed, not for small rounding errors or
+ good readability.
+
+ 36 additions, 11 multiplications
+
+ Again this is very likely sub-optimal.
+
+ The code is optimized to use a minimum number of temporary variables,
+ so it should compile quite well even on 8-register Intel x86 processors.
+ This makes the code quite obfuscated and very difficult to understand.
+
+ References:
+ [1] S. Winograd: "On Computing the Discrete Fourier Transform",
+ Mathematics of Computation, Volume 32, Number 141, January 1978,
+ Pages 175-199
+*/
+
+/*------------------------------------------------------------------*/
+/* */
+/* Function: Calculation of the inverse MDCT */
+/* */
+/*------------------------------------------------------------------*/
+
+#ifdef USE_3DNOW
+void dct36(real *inbuf,real *o1,real *o2,real *wintab,real *tsbuf)
+#else
+static void dct36(real * inbuf, real * o1, real * o2, real * wintab, real * tsbuf)
+#endif
+{
+#ifdef NEW_DCT9
+ real tmp[18];
+#endif
+
+ {
+ register real *in = inbuf;
+
+ in[17] += in[16];
+ in[16] += in[15];
+ in[15] += in[14];
+ in[14] += in[13];
+ in[13] += in[12];
+ in[12] += in[11];
+ in[11] += in[10];
+ in[10] += in[9];
+ in[9] += in[8];
+ in[8] += in[7];
+ in[7] += in[6];
+ in[6] += in[5];
+ in[5] += in[4];
+ in[4] += in[3];
+ in[3] += in[2];
+ in[2] += in[1];
+ in[1] += in[0];
+
+ in[17] += in[15];
+ in[15] += in[13];
+ in[13] += in[11];
+ in[11] += in[9];
+ in[9] += in[7];
+ in[7] += in[5];
+ in[5] += in[3];
+ in[3] += in[1];
+
+
+#ifdef NEW_DCT9
+#if 1
+ {
+ real t3;
+ {
+ real t0, t1, t2;
+
+ t0 = COS6_2 * (in[8] + in[16] - in[4]);
+ t1 = COS6_2 * in[12];
+
+ t3 = in[0];
+ t2 = t3 - t1 - t1;
+ tmp[1] = tmp[7] = t2 - t0;
+ tmp[4] = t2 + t0 + t0;
+ t3 += t1;
+
+ t2 = COS6_1 * (in[10] + in[14] - in[2]);
+ tmp[1] -= t2;
+ tmp[7] += t2;
+ }
+ {
+ real t0, t1, t2;
+
+ t0 = cos9[0] * (in[4] + in[8]);
+ t1 = cos9[1] * (in[8] - in[16]);
+ t2 = cos9[2] * (in[4] + in[16]);
+
+ tmp[2] = tmp[6] = t3 - t0 - t2;
+ tmp[0] = tmp[8] = t3 + t0 + t1;
+ tmp[3] = tmp[5] = t3 - t1 + t2;
+ }
+ }
+ {
+ real t1, t2, t3;
+
+ t1 = cos18[0] * (in[2] + in[10]);
+ t2 = cos18[1] * (in[10] - in[14]);
+ t3 = COS6_1 * in[6];
+
+ {
+ real t0 = t1 + t2 + t3;
+ tmp[0] += t0;
+ tmp[8] -= t0;
+ }
+
+ t2 -= t3;
+ t1 -= t3;
+
+ t3 = cos18[2] * (in[2] + in[14]);
+
+ t1 += t3;
+ tmp[3] += t1;
+ tmp[5] -= t1;
+
+ t2 -= t3;
+ tmp[2] += t2;
+ tmp[6] -= t2;
+ }
+
+#else
+ {
+ real t0, t1, t2, t3, t4, t5, t6, t7;
+
+ t1 = COS6_2 * in[12];
+ t2 = COS6_2 * (in[8] + in[16] - in[4]);
+
+ t3 = in[0] + t1;
+ t4 = in[0] - t1 - t1;
+ t5 = t4 - t2;
+ tmp[4] = t4 + t2 + t2;
+
+ t0 = cos9[0] * (in[4] + in[8]);
+ t1 = cos9[1] * (in[8] - in[16]);
+
+ t2 = cos9[2] * (in[4] + in[16]);
+
+ t6 = t3 - t0 - t2;
+ t0 += t3 + t1;
+ t3 += t2 - t1;
+
+ t2 = cos18[0] * (in[2] + in[10]);
+ t4 = cos18[1] * (in[10] - in[14]);
+ t7 = COS6_1 * in[6];
+
+ t1 = t2 + t4 + t7;
+ tmp[0] = t0 + t1;
+ tmp[8] = t0 - t1;
+ t1 = cos18[2] * (in[2] + in[14]);
+ t2 += t1 - t7;
+
+ tmp[3] = t3 + t2;
+ t0 = COS6_1 * (in[10] + in[14] - in[2]);
+ tmp[5] = t3 - t2;
+
+ t4 -= t1 + t7;
+
+ tmp[1] = t5 - t0;
+ tmp[7] = t5 + t0;
+ tmp[2] = t6 + t4;
+ tmp[6] = t6 - t4;
+ }
+#endif
+
+ {
+ real t0, t1, t2, t3, t4, t5, t6, t7;
+
+ t1 = COS6_2 * in[13];
+ t2 = COS6_2 * (in[9] + in[17] - in[5]);
+
+ t3 = in[1] + t1;
+ t4 = in[1] - t1 - t1;
+ t5 = t4 - t2;
+
+ t0 = cos9[0] * (in[5] + in[9]);
+ t1 = cos9[1] * (in[9] - in[17]);
+
+ tmp[13] = (t4 + t2 + t2) * tfcos36[17 - 13];
+ t2 = cos9[2] * (in[5] + in[17]);
+
+ t6 = t3 - t0 - t2;
+ t0 += t3 + t1;
+ t3 += t2 - t1;
+
+ t2 = cos18[0] * (in[3] + in[11]);
+ t4 = cos18[1] * (in[11] - in[15]);
+ t7 = COS6_1 * in[7];
+
+ t1 = t2 + t4 + t7;
+ tmp[17] = (t0 + t1) * tfcos36[17 - 17];
+ tmp[9] = (t0 - t1) * tfcos36[17 - 9];
+ t1 = cos18[2] * (in[3] + in[15]);
+ t2 += t1 - t7;
+
+ tmp[14] = (t3 + t2) * tfcos36[17 - 14];
+ t0 = COS6_1 * (in[11] + in[15] - in[3]);
+ tmp[12] = (t3 - t2) * tfcos36[17 - 12];
+
+ t4 -= t1 + t7;
+
+ tmp[16] = (t5 - t0) * tfcos36[17 - 16];
+ tmp[10] = (t5 + t0) * tfcos36[17 - 10];
+ tmp[15] = (t6 + t4) * tfcos36[17 - 15];
+ tmp[11] = (t6 - t4) * tfcos36[17 - 11];
+ }
+
+#define MACRO(v) \
+do { \
+ real tmpval; \
+ \
+ tmpval = tmp[(v)] + tmp[17-(v)]; \
+ out2[9+(v)] = tmpval * w[27+(v)]; \
+ out2[8-(v)] = tmpval * w[26-(v)]; \
+ tmpval = tmp[(v)] - tmp[17-(v)]; \
+ ts[SBLIMIT*(8-(v))] = out1[8-(v)] + tmpval * w[8-(v)]; \
+ ts[SBLIMIT*(9+(v))] = out1[9+(v)] + tmpval * w[9+(v)]; \
+} while (0)
+
+ {
+ register real *out2 = o2;
+ register real *w = wintab;
+ register real *out1 = o1;
+ register real *ts = tsbuf;
+
+ MACRO(0);
+ MACRO(1);
+ MACRO(2);
+ MACRO(3);
+ MACRO(4);
+ MACRO(5);
+ MACRO(6);
+ MACRO(7);
+ MACRO(8);
+ }
+
+#else
+
+ {
+
+#define MACRO0(v) \
+do { \
+ real tmp; \
+ out2[9+(v)] = (tmp = sum0 + sum1) * w[27+(v)]; \
+ out2[8-(v)] = tmp * w[26-(v)]; \
+ sum0 -= sum1; \
+ ts[SBLIMIT*(8-(v))] = out1[8-(v)] + sum0 * w[8-(v)]; \
+ ts[SBLIMIT*(9+(v))] = out1[9+(v)] + sum0 * w[9+(v)]; \
+} while (0)
+
+#define MACRO1(v) \
+do { \
+ real sum0,sum1; \
+ sum0 = tmp1a + tmp2a; \
+ sum1 = (tmp1b + tmp2b) * tfcos36[(v)]; \
+ MACRO0(v); \
+} while (0)
+
+#define MACRO2(v) \
+do { \
+ real sum0, sum1; \
+ sum0 = tmp2a - tmp1a; \
+ sum1 = (tmp2b - tmp1b) * tfcos36[(v)]; \
+ MACRO0(v); \
+} while (0)
+
+ register const real *c = COS9;
+ register real *out2 = o2;
+ register real *w = wintab;
+ register real *out1 = o1;
+ register real *ts = tsbuf;
+
+ real ta33, ta66, tb33, tb66;
+
+ ta33 = in[2 * 3 + 0] * c[3];
+ ta66 = in[2 * 6 + 0] * c[6] + in[2 * 0 + 0];
+ tb33 = in[2 * 3 + 1] * c[3];
+ tb66 = in[2 * 6 + 1] * c[6] + in[2 * 0 + 1];
+
+ {
+ real tmp1a, tmp2a, tmp1b, tmp2b;
+ tmp1a = in[2 * 1 + 0] * c[1] + ta33 + in[2 * 5 + 0] * c[5] + in[2 * 7 + 0] * c[7];
+ tmp1b = in[2 * 1 + 1] * c[1] + tb33 + in[2 * 5 + 1] * c[5] + in[2 * 7 + 1] * c[7];
+ tmp2a = in[2 * 2 + 0] * c[2] + in[2 * 4 + 0] * c[4] + ta66 + in[2 * 8 + 0] * c[8];
+ tmp2b = in[2 * 2 + 1] * c[2] + in[2 * 4 + 1] * c[4] + tb66 + in[2 * 8 + 1] * c[8];
+
+ MACRO1(0);
+ MACRO2(8);
+ }
+
+ {
+ real tmp1a, tmp2a, tmp1b, tmp2b;
+ tmp1a = (in[2 * 1 + 0] - in[2 * 5 + 0] - in[2 * 7 + 0]) * c[3];
+ tmp1b = (in[2 * 1 + 1] - in[2 * 5 + 1] - in[2 * 7 + 1]) * c[3];
+ tmp2a = (in[2 * 2 + 0] - in[2 * 4 + 0] - in[2 * 8 + 0]) * c[6] - in[2 * 6 + 0] + in[2 * 0 + 0];
+ tmp2b = (in[2 * 2 + 1] - in[2 * 4 + 1] - in[2 * 8 + 1]) * c[6] - in[2 * 6 + 1] + in[2 * 0 + 1];
+
+ MACRO1(1);
+ MACRO2(7);
+ }
+
+ {
+ real tmp1a, tmp2a, tmp1b, tmp2b;
+ tmp1a = in[2 * 1 + 0] * c[5] - ta33 - in[2 * 5 + 0] * c[7] + in[2 * 7 + 0] * c[1];
+ tmp1b = in[2 * 1 + 1] * c[5] - tb33 - in[2 * 5 + 1] * c[7] + in[2 * 7 + 1] * c[1];
+ tmp2a = -in[2 * 2 + 0] * c[8] - in[2 * 4 + 0] * c[2] + ta66 + in[2 * 8 + 0] * c[4];
+ tmp2b = -in[2 * 2 + 1] * c[8] - in[2 * 4 + 1] * c[2] + tb66 + in[2 * 8 + 1] * c[4];
+
+ MACRO1(2);
+ MACRO2(6);
+ }
+
+ {
+ real tmp1a, tmp2a, tmp1b, tmp2b;
+ tmp1a = in[2 * 1 + 0] * c[7] - ta33 + in[2 * 5 + 0] * c[1] - in[2 * 7 + 0] * c[5];
+ tmp1b = in[2 * 1 + 1] * c[7] - tb33 + in[2 * 5 + 1] * c[1] - in[2 * 7 + 1] * c[5];
+ tmp2a = -in[2 * 2 + 0] * c[4] + in[2 * 4 + 0] * c[8] + ta66 - in[2 * 8 + 0] * c[2];
+ tmp2b = -in[2 * 2 + 1] * c[4] + in[2 * 4 + 1] * c[8] + tb66 - in[2 * 8 + 1] * c[2];
+
+ MACRO1(3);
+ MACRO2(5);
+ }
+
+ {
+ real sum0, sum1;
+
+ sum0 = in[2 * 0 + 0] - in[2 * 2 + 0] + in[2 * 4 + 0] - in[2 * 6 + 0] + in[2 * 8 + 0];
+ sum1 = (in[2 * 0 + 1] - in[2 * 2 + 1] + in[2 * 4 + 1] - in[2 * 6 + 1] + in[2 * 8 + 1]) * tfcos36[4];
+ MACRO0(4);
+ }
+ }
+#endif
+
+ }
+}
+
+/*
+ * new DCT12
+ */
+static void dct12(real * in, real * rawout1, real * rawout2, register real * wi, register real * ts)
+{
+
+#define DCT12_PART1() \
+do { \
+ in5 = in[5*3]; \
+ in5 += (in4 = in[4*3]); \
+ in4 += (in3 = in[3*3]); \
+ in3 += (in2 = in[2*3]); \
+ in2 += (in1 = in[1*3]); \
+ in1 += (in0 = in[0*3]); \
+ \
+ in5 += in3; in3 += in1; \
+ \
+ in2 *= COS6_1; \
+ in3 *= COS6_1; \
+} while (0)
+
+#define DCT12_PART2() \
+do { \
+ in0 += in4 * COS6_2; \
+ \
+ in4 = in0 + in2; \
+ in0 -= in2; \
+ \
+ in1 += in5 * COS6_2; \
+ \
+ in5 = (in1 + in3) * tfcos12[0]; \
+ in1 = (in1 - in3) * tfcos12[2]; \
+ \
+ in3 = in4 + in5; \
+ in4 -= in5; \
+ \
+ in2 = in0 + in1; \
+ in0 -= in1; \
+} while (0)
+
+
+ {
+ real in0, in1, in2, in3, in4, in5;
+ register real *out1 = rawout1;
+
+ ts[SBLIMIT * 0] = out1[0];
+ ts[SBLIMIT * 1] = out1[1];
+ ts[SBLIMIT * 2] = out1[2];
+ ts[SBLIMIT * 3] = out1[3];
+ ts[SBLIMIT * 4] = out1[4];
+ ts[SBLIMIT * 5] = out1[5];
+
+ DCT12_PART1();
+
+ {
+ real tmp0, tmp1 = (in0 - in4);
+
+ {
+ real tmp2 = (in1 - in5) * tfcos12[1];
+
+ tmp0 = tmp1 + tmp2;
+ tmp1 -= tmp2;
+ }
+ ts[(17 - 1) * SBLIMIT] = out1[17 - 1] + tmp0 * wi[11 - 1];
+ ts[(12 + 1) * SBLIMIT] = out1[12 + 1] + tmp0 * wi[6 + 1];
+ ts[(6 + 1) * SBLIMIT] = out1[6 + 1] + tmp1 * wi[1];
+ ts[(11 - 1) * SBLIMIT] = out1[11 - 1] + tmp1 * wi[5 - 1];
+ }
+
+ DCT12_PART2();
+
+ ts[(17 - 0) * SBLIMIT] = out1[17 - 0] + in2 * wi[11 - 0];
+ ts[(12 + 0) * SBLIMIT] = out1[12 + 0] + in2 * wi[6 + 0];
+ ts[(12 + 2) * SBLIMIT] = out1[12 + 2] + in3 * wi[6 + 2];
+ ts[(17 - 2) * SBLIMIT] = out1[17 - 2] + in3 * wi[11 - 2];
+
+ ts[(6 + 0) * SBLIMIT] = out1[6 + 0] + in0 * wi[0];
+ ts[(11 - 0) * SBLIMIT] = out1[11 - 0] + in0 * wi[5 - 0];
+ ts[(6 + 2) * SBLIMIT] = out1[6 + 2] + in4 * wi[2];
+ ts[(11 - 2) * SBLIMIT] = out1[11 - 2] + in4 * wi[5 - 2];
+ }
+
+ in++;
+
+ {
+ real in0, in1, in2, in3, in4, in5;
+ register real *out2 = rawout2;
+
+ DCT12_PART1();
+
+ {
+ real tmp0, tmp1 = (in0 - in4);
+
+ {
+ real tmp2 = (in1 - in5) * tfcos12[1];
+
+ tmp0 = tmp1 + tmp2;
+ tmp1 -= tmp2;
+ }
+ out2[5 - 1] = tmp0 * wi[11 - 1];
+ out2[0 + 1] = tmp0 * wi[6 + 1];
+ ts[(12 + 1) * SBLIMIT] += tmp1 * wi[1];
+ ts[(17 - 1) * SBLIMIT] += tmp1 * wi[5 - 1];
+ }
+
+ DCT12_PART2();
+
+ out2[5 - 0] = in2 * wi[11 - 0];
+ out2[0 + 0] = in2 * wi[6 + 0];
+ out2[0 + 2] = in3 * wi[6 + 2];
+ out2[5 - 2] = in3 * wi[11 - 2];
+
+ ts[(12 + 0) * SBLIMIT] += in0 * wi[0];
+ ts[(17 - 0) * SBLIMIT] += in0 * wi[5 - 0];
+ ts[(12 + 2) * SBLIMIT] += in4 * wi[2];
+ ts[(17 - 2) * SBLIMIT] += in4 * wi[5 - 2];
+ }
+
+ in++;
+
+ {
+ real in0, in1, in2, in3, in4, in5;
+ register real *out2 = rawout2;
+
+ out2[12] = out2[13] = out2[14] = out2[15] = out2[16] = out2[17] = 0.0;
+
+ DCT12_PART1();
+
+ {
+ real tmp0, tmp1 = (in0 - in4);
+
+ {
+ real tmp2 = (in1 - in5) * tfcos12[1];
+
+ tmp0 = tmp1 + tmp2;
+ tmp1 -= tmp2;
+ }
+ out2[11 - 1] = tmp0 * wi[11 - 1];
+ out2[6 + 1] = tmp0 * wi[6 + 1];
+ out2[0 + 1] += tmp1 * wi[1];
+ out2[5 - 1] += tmp1 * wi[5 - 1];
+ }
+
+ DCT12_PART2();
+
+ out2[11 - 0] = in2 * wi[11 - 0];
+ out2[6 + 0] = in2 * wi[6 + 0];
+ out2[6 + 2] = in3 * wi[6 + 2];
+ out2[11 - 2] = in3 * wi[11 - 2];
+
+ out2[0 + 0] += in0 * wi[0];
+ out2[5 - 0] += in0 * wi[5 - 0];
+ out2[0 + 2] += in4 * wi[2];
+ out2[5 - 2] += in4 * wi[5 - 2];
+ }
+}
+
+/*
+ * III_hybrid
+ */
+static void III_hybrid(real fsIn[SBLIMIT][SSLIMIT],
+ real tsOut[SSLIMIT][SBLIMIT], int ch,
+ struct gr_info_s *gr_info, struct frame *fr)
+{
+ static real block[2][2][SBLIMIT * SSLIMIT] = { {{0,}} };
+ static int blc[2] = { 0, 0 };
+
+ real *tspnt = (real *) tsOut;
+ real *rawout1, *rawout2;
+ int bt;
+ unsigned sb = 0;
+
+ {
+ int b = blc[ch];
+ rawout1 = block[b][ch];
+ b = -b + 1;
+ rawout2 = block[b][ch];
+ blc[ch] = b;
+ }
+
+ if (gr_info->mixed_block_flag)
+ {
+ sb = 2;
+#ifdef USE_3DNOW
+ (fr->dct36)(fsIn[0],rawout1,rawout2,win[0],tspnt);
+ (fr->dct36)(fsIn[1],rawout1+18,rawout2+18,win1[0],tspnt+1);
+#else
+ dct36(fsIn[0], rawout1, rawout2, win[0], tspnt);
+ dct36(fsIn[1], rawout1 + 18, rawout2 + 18, win1[0], tspnt + 1);
+#endif
+ rawout1 += 36;
+ rawout2 += 36;
+ tspnt += 2;
+ }
+
+ bt = gr_info->block_type;
+ if (bt == 2)
+ {
+ for (; sb < gr_info->maxb; sb += 2, tspnt += 2, rawout1 += 36, rawout2 += 36)
+ {
+ dct12(fsIn[sb], rawout1, rawout2, win[2], tspnt);
+ dct12(fsIn[sb + 1], rawout1 + 18, rawout2 + 18, win1[2], tspnt + 1);
+ }
+ }
+ else
+ {
+ for (; sb < gr_info->maxb; sb += 2, tspnt += 2, rawout1 += 36, rawout2 += 36)
+ {
+#ifdef USE_3DNOW
+ (fr->dct36)(fsIn[sb],rawout1,rawout2,win[bt],tspnt);
+ (fr->dct36)(fsIn[sb+1],rawout1+18,rawout2+18,win1[bt],tspnt+1);
+#else
+ dct36(fsIn[sb], rawout1, rawout2, win[bt], tspnt);
+ dct36(fsIn[sb + 1], rawout1 + 18, rawout2 + 18, win1[bt], tspnt + 1);
+#endif
+ }
+ }
+
+ for (; sb < SBLIMIT; sb++, tspnt++)
+ {
+ int i;
+ for (i = 0; i < SSLIMIT; i++)
+ {
+ tspnt[i * SBLIMIT] = *rawout1++;
+ *rawout2++ = 0.0;
+ }
+ }
+}
+
+/*
+ * main layer3 handler
+ */
+int mpg123_do_layer3(struct frame *fr)
+{
+ int gr, ch, ss;
+ int scalefacs[2][39]; /* max 39 for short[13][3] mode, mixed: 38, long: 22 */
+ struct III_sideinfo sideinfo;
+ int stereo = fr->stereo;
+ int single = fr->single;
+ int ms_stereo, i_stereo;
+ int sfreq = fr->sampling_frequency;
+ int stereo1, granules;
+
+ if (stereo == 1)
+ { /* stream is mono */
+ stereo1 = 1;
+ single = 0;
+ }
+ else if (single >= 0) /* stream is stereo, but force to mono */
+ stereo1 = 1;
+ else
+ stereo1 = 2;
+
+ if (fr->mode == MPG_MD_JOINT_STEREO)
+ {
+ ms_stereo = (fr->mode_ext & 0x2) >> 1;
+ i_stereo = fr->mode_ext & 0x1;
+ }
+ else
+ ms_stereo = i_stereo = 0;
+
+ granules = fr->lsf ? 1 : 2;
+ if (!III_get_side_info(&sideinfo, stereo, ms_stereo, sfreq, single, fr->lsf))
+ return 0;
+
+ mpg123_set_pointer(sideinfo.main_data_begin);
+
+ for (gr = 0; gr < granules; gr++)
+ {
+ real hybridIn[2][SBLIMIT][SSLIMIT];
+ real hybridOut[2][SSLIMIT][SBLIMIT];
+
+ {
+ struct gr_info_s *gr_info = &(sideinfo.ch[0].gr[gr]);
+ long part2bits;
+
+ if (fr->lsf)
+ part2bits = III_get_scale_factors_2(scalefacs[0], gr_info, 0);
+ else
+ part2bits = III_get_scale_factors_1(scalefacs[0], gr_info);
+
+ if (III_dequantize_sample(hybridIn[0], scalefacs[0], gr_info, sfreq, part2bits))
+ return 0;
+ }
+
+ if (stereo == 2)
+ {
+ struct gr_info_s *gr_info = &(sideinfo.ch[1].gr[gr]);
+ long part2bits;
+
+ if (fr->lsf)
+ part2bits = III_get_scale_factors_2(scalefacs[1], gr_info, i_stereo);
+ else
+ part2bits = III_get_scale_factors_1(scalefacs[1], gr_info);
+
+ if (III_dequantize_sample(hybridIn[1], scalefacs[1], gr_info, sfreq, part2bits))
+ return 0;
+
+ if (ms_stereo)
+ {
+ int i;
+ unsigned maxb = sideinfo.ch[0].gr[gr].maxb;
+
+ if (sideinfo.ch[1].gr[gr].maxb > maxb)
+ maxb = sideinfo.ch[1].gr[gr].maxb;
+ for (i = 0; i < SSLIMIT * maxb; i++)
+ {
+ real tmp0 = ((real *) hybridIn[0])[i];
+ real tmp1 = ((real *) hybridIn[1])[i];
+ ((real *) hybridIn[0])[i] = tmp0 + tmp1;
+ ((real *) hybridIn[1])[i] = tmp0 - tmp1;
+ }
+ }
+
+ if (i_stereo)
+ III_i_stereo(hybridIn, scalefacs[1], gr_info, sfreq, ms_stereo, fr->lsf);
+
+ if (ms_stereo || i_stereo || (single == 3))
+ {
+ if (gr_info->maxb > sideinfo.ch[0].gr[gr].maxb)
+ sideinfo.ch[0].gr[gr].maxb = gr_info->maxb;
+ else
+ gr_info->maxb = sideinfo.ch[0].gr[gr].maxb;
+ }
+
+ switch (single)
+ {
+ case 3:
+ {
+ register unsigned i;
+ register real *in0 = (real *) hybridIn[0],
+ *in1 = (real *) hybridIn[1];
+ for (i = 0; i < SSLIMIT * gr_info->maxb; i++, in0++)
+ *in0 = (*in0 + *in1++); /* *0.5 done by pow-scale */
+ }
+ break;
+ case 1:
+ {
+ register unsigned i;
+ register real *in0 = (real *) hybridIn[0],
+ *in1 = (real *) hybridIn[1];
+ for (i = 0; i < SSLIMIT * gr_info->maxb; i++)
+ *in0++ = *in1++;
+ }
+ break;
+ }
+ }
+/***
+ if (mpg123_info->eq_active)
+ {
+ int i, sb;
+
+ if (single < 0)
+ {
+ for (sb = 0, i = 0; sb < SBLIMIT; sb++)
+ {
+ for (ss = 0; ss < SSLIMIT; ss++)
+ {
+ hybridIn[0][sb][ss] *= mpg123_info->eq_mul[i];
+ hybridIn[1][sb][ss] *= mpg123_info->eq_mul[i++];
+ }
+ }
+ }
+ else
+ {
+ for (sb = 0, i = 0; sb < SBLIMIT; sb++)
+ {
+ for (ss = 0; ss < SSLIMIT; ss++)
+ hybridIn[0][sb][ss] *= mpg123_info->eq_mul[i++];
+ }
+ }
+ }
+***/
+
+ for (ch = 0; ch < stereo1; ch++)
+ {
+ struct gr_info_s *gr_info = &(sideinfo.ch[ch].gr[gr]);
+
+ III_antialias(hybridIn[ch], gr_info);
+ if (gr_info->maxb < 1 || gr_info->maxb > SBLIMIT)
+ return 0;
+ III_hybrid(hybridIn[ch], hybridOut[ch], ch, gr_info, fr);
+ }
+
+ for (ss = 0; ss < SSLIMIT; ss++)
+ {
+ if (single >= 0)
+ {
+ (fr->synth_mono) (hybridOut[0][ss], mpg123_pcm_sample, &mpg123_pcm_point);
+ }
+ else
+ {
+ int p1 = mpg123_pcm_point;
+
+ (fr->synth) (hybridOut[0][ss], 0, mpg123_pcm_sample, &p1);
+ (fr->synth) (hybridOut[1][ss], 1, mpg123_pcm_sample, &mpg123_pcm_point);
+ }
+ }
+
+/***
+ if (mpg123_info->output_audio)
+ {
+ mpg123_ip.add_vis_pcm(mpg123_ip.output->written_time(),
+ mpg123_cfg.resolution == 16 ? FMT_S16_NE : FMT_U8,
+ mpg123_cfg.channels == 2 ? fr->stereo : 1,
+ mpg123_pcm_point, mpg123_pcm_sample);
+ while (mpg123_ip.output->buffer_free() < mpg123_pcm_point &&
+ mpg123_info->going && mpg123_info->jump_to_time == -1)
+ xmms_usleep(10000);
+ if (mpg123_info->going && mpg123_info->jump_to_time == -1)
+ mpg123_ip.output->write_audio(mpg123_pcm_sample, mpg123_pcm_point);
+ }
+***/
+ mpg123_pcm_point = 0;
+ }
+ return 1;
+}
diff --git a/src/libmpg123/mpg123.c b/src/libmpg123/mpg123.c
new file mode 100755
index 0000000..1def17e
--- /dev/null
+++ b/src/libmpg123/mpg123.c
@@ -0,0 +1,172 @@
+/* XMMS - Cross-platform multimedia player
+ * Copyright (C) 1998-2000 Peter Alm, Mikael Alm, Olle Hallnas, Thomas Nilsson and 4Front Technologies
+ * Copyright (C) 1999,2000 Håvard Kvålen
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * Note : removed code not used in EasyTAG
+ */
+
+#include "mpg123.h"
+
+
+double mpg123_compute_tpf(struct frame *fr)
+{
+ const int bs[4] = {0, 384, 1152, 1152};
+ double tpf;
+
+ tpf = bs[fr->lay];
+ tpf /= mpg123_freqs[fr->sampling_frequency] << (fr->lsf);
+ return tpf;
+}
+
+
+static guint32 convert_to_header(guint8 * buf)
+{
+
+ return (buf[0] << 24) + (buf[1] << 16) + (buf[2] << 8) + buf[3];
+}
+
+
+#define DET_BUF_SIZE 1024
+
+#if 0 /* Not used at the present time */
+static gboolean mpg123_detect_by_content(gchar *filename)
+{
+ FILE *file;
+ guchar tmp[4];
+ guint32 head;
+ struct frame fr;
+ guchar buf[DET_BUF_SIZE];
+ gint in_buf, i;
+
+ if((file = fopen(filename, "rb")) == NULL)
+ return FALSE;
+ if (fread(tmp, 1, 4, file) != 4)
+ goto done;
+ head = convert_to_header(tmp);
+ while(!mpg123_head_check(head))
+ {
+ /*
+ * The mpeg-stream can start anywhere in the file,
+ * so we check the entire file
+ */
+ /* Optimize this */
+ in_buf = fread(buf, 1, DET_BUF_SIZE, file);
+ if(in_buf == 0)
+ goto done;
+
+ for (i = 0; i < in_buf; i++)
+ {
+ head <<= 8;
+ head |= buf[i];
+ if(mpg123_head_check(head))
+ {
+ fseek(file, i+1-in_buf, SEEK_CUR);
+ break;
+ }
+ }
+ }
+ if (mpg123_decode_header(&fr, head))
+ {
+ /*
+ * We found something which looks like a MPEG-header.
+ * We check the next frame too, to be sure
+ */
+
+ if (fseek(file, fr.framesize, SEEK_CUR) != 0)
+ goto done;
+ if (fread(tmp, 1, 4, file) != 4)
+ goto done;
+ head = convert_to_header(tmp);
+ if (mpg123_head_check(head) && mpg123_decode_header(&fr, head))
+ {
+ fclose(file);
+ return TRUE;
+ }
+ }
+
+ done:
+ fclose(file);
+ return FALSE;
+}
+#endif
+
+//static guint get_song_time(FILE * file)
+guint mpg123_get_song_time(FILE * file)
+{
+ guint32 head;
+ guchar tmp[4], *buf;
+ struct frame frm;
+ XHEADDATA xing_header;
+ double tpf, bpf;
+ guint32 len;
+ long id3v2size = 0;
+
+ if (!file)
+ return -1;
+
+ fseek(file, 0, SEEK_SET);
+ if (fread(tmp, 1, 4, file) != 4)
+ return 0;
+
+ // Skip data of the ID3v2.x tag (patch from Artur Polaczynski)
+ if (tmp[0] == 'I' && tmp[1] == 'D' && tmp[2] == '3' && tmp[3] < 0xFF)
+ {
+ // id3v2 tag skipeer $49 44 33 yy yy xx zz zz zz zz [zz size]
+ fseek(file, 2, SEEK_CUR); // Size is 6-9 position
+ if (fread(tmp, 1, 4, file) != 4)
+ return 0;
+ id3v2size = 10 + ( (long)(tmp[3]) | ((long)(tmp[2]) << 7) | ((long)(tmp[1]) << 14) | ((long)(tmp[0]) << 21) );
+ fseek(file, id3v2size, SEEK_SET);
+ if (fread(tmp, 1, 4, file) != 4) // Read mpeg header
+ return 0;
+ }
+
+ head = convert_to_header(tmp);
+ while (!mpg123_head_check(head))
+ {
+ head <<= 8;
+ if (fread(tmp, 1, 1, file) != 1)
+ return 0;
+ head |= tmp[0];
+ }
+ if (mpg123_decode_header(&frm, head))
+ {
+ buf = g_malloc(frm.framesize + 4);
+ fseek(file, -4, SEEK_CUR);
+ fread(buf, 1, frm.framesize + 4, file);
+ xing_header.toc = NULL;
+ tpf = mpg123_compute_tpf(&frm);
+ if (mpg123_get_xing_header(&xing_header, buf))
+ {
+ g_free(buf);
+ return ((guint) (tpf * xing_header.frames * 1000));
+ }
+ g_free(buf);
+ bpf = mpg123_compute_bpf(&frm);
+ fseek(file, 0, SEEK_END);
+ len = ftell(file) - id3v2size;
+ fseek(file, -128, SEEK_END);
+ fread(tmp, 1, 3, file);
+ if (!strncmp(tmp, "TAG", 3))
+ len -= 128;
+ return ((guint) ((guint)(len / bpf) * tpf * 1000));
+ }
+ return 0;
+}
+
diff --git a/src/libmpg123/mpg123.h b/src/libmpg123/mpg123.h
new file mode 100755
index 0000000..a3a3726
--- /dev/null
+++ b/src/libmpg123/mpg123.h
@@ -0,0 +1,144 @@
+/*
+ * mpg123 defines
+ * used source: musicout.h from mpegaudio package
+ */
+
+#ifndef __MPG123_H__
+#define __MPG123_H__
+
+
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+#include <gtk/gtk.h>
+
+#include "dxhead.h"
+
+#define real float
+
+/* #define MAX_NAME_SIZE 81 */
+#define SBLIMIT 32
+#define SCALE_BLOCK 12
+#define SSLIMIT 18
+
+#define MPG_MD_STEREO 0
+#define MPG_MD_JOINT_STEREO 1
+#define MPG_MD_DUAL_CHANNEL 2
+#define MPG_MD_MONO 3
+
+
+struct al_table
+{
+ short bits;
+ short d;
+};
+
+struct frame
+{
+ struct al_table *alloc;
+ int (*synth) (real *, int, unsigned char *, int *);
+ int (*synth_mono) (real *, unsigned char *, int *);
+ int stereo;
+ int jsbound;
+ int single;
+ int II_sblimit;
+ int down_sample_sblimit;
+ int lsf;
+ int mpeg25;
+ int down_sample;
+ int header_change;
+ int lay;
+ int (*do_layer) (struct frame * fr);
+ int error_protection;
+ int bitrate_index;
+ int sampling_frequency;
+ int padding;
+ int extension;
+ int mode;
+ int mode_ext;
+ int copyright;
+ int original;
+ int emphasis;
+ int framesize; /* computed framesize */
+};
+
+
+struct bitstream_info
+{
+ int bitindex;
+ unsigned char *wordpointer;
+};
+
+extern struct bitstream_info bsi;
+
+/* ------ Declarations from "common.c" ------ */
+extern unsigned int mpg123_get1bit(void);
+extern unsigned int mpg123_getbits(int);
+extern unsigned int mpg123_getbits_fast(int);
+
+extern int mpg123_head_check(unsigned long);
+
+extern void mpg123_set_pointer(long);
+
+extern unsigned char *mpg123_pcm_sample;
+extern int mpg123_pcm_point;
+
+struct gr_info_s
+{
+ int scfsi;
+ unsigned part2_3_length;
+ unsigned big_values;
+ unsigned scalefac_compress;
+ unsigned block_type;
+ unsigned mixed_block_flag;
+ unsigned table_select[3];
+ unsigned subblock_gain[3];
+ unsigned maxband[3];
+ unsigned maxbandl;
+ unsigned maxb;
+ unsigned region1start;
+ unsigned region2start;
+ unsigned preflag;
+ unsigned scalefac_scale;
+ unsigned count1table_select;
+ real *full_gain[3];
+ real *pow2gain;
+};
+
+struct III_sideinfo
+{
+ unsigned main_data_begin;
+ unsigned private_bits;
+ struct
+ {
+ struct gr_info_s gr[2];
+ }
+ ch[2];
+};
+
+int mpg123_stream_check_for_xing_header(struct frame *fr, XHEADDATA * xhead);
+
+extern int mpg123_do_layer3(struct frame *fr);
+extern int mpg123_do_layer2(struct frame *fr);
+extern int mpg123_do_layer1(struct frame *fr);
+
+
+extern void mpg123_init_layer3(int);
+extern void mpg123_init_layer2(void);
+
+int mpg123_decode_header(struct frame *fr, unsigned long newhead);
+double mpg123_compute_bpf(struct frame *fr);
+double mpg123_compute_tpf(struct frame *fr);
+
+
+extern unsigned char *mpg123_conv16to8;
+extern long mpg123_freqs[9];
+extern real mpg123_muls[27][64];
+extern real mpg123_decwin[512 + 32];
+extern real *mpg123_pnts[5];
+
+extern int tabsel_123[2][3][16];
+
+guint mpg123_get_song_time(FILE * file);
+
+#endif
diff --git a/src/log.c b/src/log.c
new file mode 100755
index 0000000..f066fc7
--- /dev/null
+++ b/src/log.c
@@ -0,0 +1,307 @@
+/* log.c - 2007/03/25 */
+/*
+ * EasyTAG - Tag editor for MP3 and Ogg Vorbis files
+ * Copyright (C) 2000-2007 Jerome Couderc <easytag@gmail.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+
+#include <glib/gi18n-lib.h>
+#include <string.h>
+#include <stdio.h>
+#include <time.h>
+
+#include "log.h"
+#include "easytag.h"
+#include "bar.h"
+
+#ifdef WIN32
+# include "win32/win32dep.h"
+#endif
+
+
+/****************
+ * Declarations *
+ ****************/
+
+GtkWidget *LogList = NULL;
+GtkListStore *logListModel;
+GList *LogPrintTmpList = NULL; // Temporary list to store messages for the LogList when this control wasn't yet created
+
+enum
+{
+ LOG_TIME_TEXT,
+ LOG_TEXT,
+ LOG_ROW_BACKGROUND,
+ LOG_ROW_FOREGROUND,
+ LOG_COLUMN_COUNT
+};
+
+// File for log
+gchar *LOG_FILE = ".easytag/easytag.log";
+
+// Structure used to store information for the temporary list
+typedef struct _Log_Data Log_Data;
+struct _Log_Data
+{
+ gchar *time; /* The time of this line of log */
+ gchar *string; /* The string of the line of log to display */
+};
+
+
+/**************
+ * Prototypes *
+ **************/
+gboolean Log_Popup_Menu_Handler (GtkMenu *menu, GdkEventButton *event);
+void Log_Print_Tmp_List (void);
+gchar *Log_Format_Date (void);
+
+
+
+/*************
+ * Functions *
+ *************/
+
+GtkWidget *Create_Log_Area (void)
+{
+ GtkWidget *Frame;
+ GtkWidget *ScrollWindowLogList;
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+ GtkWidget *PopupMenu;
+
+
+ Frame = gtk_frame_new(_("Log"));
+ gtk_container_set_border_width(GTK_CONTAINER(Frame), 2);
+
+ /*
+ * The ScrollWindow and the List
+ */
+ ScrollWindowLogList = gtk_scrolled_window_new(NULL,NULL);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ScrollWindowLogList),
+ GTK_POLICY_AUTOMATIC,GTK_POLICY_AUTOMATIC);
+ gtk_container_add(GTK_CONTAINER(Frame),ScrollWindowLogList);
+
+ /* The file list */
+ logListModel = gtk_list_store_new(LOG_COLUMN_COUNT,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ GDK_TYPE_COLOR,
+ GDK_TYPE_COLOR);
+
+ LogList = gtk_tree_view_new_with_model(GTK_TREE_MODEL(logListModel));
+ gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(LogList), FALSE);
+ gtk_container_add(GTK_CONTAINER(ScrollWindowLogList), LogList);
+ gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(LogList), FALSE);
+ gtk_widget_set_size_request(LogList, 0, 0);
+ gtk_tree_view_set_reorderable(GTK_TREE_VIEW(LogList), FALSE);
+ gtk_tree_selection_set_mode(gtk_tree_view_get_selection(GTK_TREE_VIEW(LogList)),GTK_SELECTION_MULTIPLE);
+
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new();
+ gtk_tree_view_column_pack_start(column, renderer, FALSE);
+ gtk_tree_view_column_set_attributes(column, renderer,
+ "text", LOG_TIME_TEXT,
+ "background-gdk", LOG_ROW_BACKGROUND,
+ "foreground-gdk", LOG_ROW_FOREGROUND,
+ NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(LogList), column);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new();
+ gtk_tree_view_column_pack_start(column, renderer, FALSE);
+ gtk_tree_view_column_set_attributes(column, renderer,
+ "text", LOG_TEXT,
+ "background-gdk", LOG_ROW_BACKGROUND,
+ "foreground-gdk", LOG_ROW_FOREGROUND,
+ NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(LogList), column);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+
+ // Create Popup Menu on browser album list
+ PopupMenu = gtk_ui_manager_get_widget(UIManager, "/LogPopup");
+ g_signal_connect_swapped(G_OBJECT(LogList),"button_press_event",
+ G_CALLBACK(Log_Popup_Menu_Handler), G_OBJECT(PopupMenu));
+
+ // Load pending messages in the Log list
+ Log_Print_Tmp_List();
+
+ //gtk_widget_show_all(ScrollWindowLogList);
+ gtk_widget_show_all(Frame);
+
+ //return ScrollWindowLogList;
+ return Frame;
+}
+
+
+/*
+ * Log_Popup_Menu_Handler : displays the corresponding menu
+ */
+gboolean Log_Popup_Menu_Handler (GtkMenu *menu, GdkEventButton *event)
+{
+ if (event && (event->type==GDK_BUTTON_PRESS) && (event->button == 3))
+ {
+ gtk_menu_popup(menu,NULL,NULL,NULL,NULL,event->button,event->time);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/*
+ * Remove all lines in the log list
+ */
+void Log_Clean_Log_List (void)
+{
+ if (logListModel)
+ gtk_list_store_clear(logListModel);
+}
+
+
+/*
+ * Return time in allocated data
+ */
+gchar *Log_Format_Date (void)
+{
+ struct tm *tms;
+ time_t nowtime;
+ gchar *current_date = g_malloc0(21);
+
+ // Get current time and date
+ nowtime = time(NULL);
+ tms = localtime(&nowtime);
+ strftime(current_date,20,"%X",tms); // Time without date in current locale
+ //strftime(current_date,20,"%x",ptr); // Date without time in current locale
+
+ return current_date;
+}
+
+
+/*
+ * Function to use anywhere in the application to send a message to the LogList
+ */
+void Log_Print (gchar const *format, ...)
+{
+ va_list args;
+ gchar *string;
+
+ GtkTreeIter iter;
+ static gboolean first_time = TRUE;
+ static gchar *file_path = NULL;
+ FILE *file = NULL;
+
+
+ va_start (args, format);
+ string = g_strdup_vprintf (format, args);
+ va_end (args);
+
+ // If the log window is displayed then messages are displayed, else
+ // the messages are stored in a temporary list.
+ if (LogList && logListModel)
+ {
+ gchar *time = Log_Format_Date();
+ gtk_list_store_append(logListModel, &iter);
+ gtk_list_store_set(logListModel, &iter,
+ LOG_TIME_TEXT, time,
+ LOG_TEXT, string,
+ LOG_ROW_BACKGROUND, NULL,
+ LOG_ROW_FOREGROUND, NULL,
+ -1);
+ g_free(time);
+ }else
+ {
+ Log_Data *LogData = g_malloc0(sizeof(Log_Data));
+ LogData->time = Log_Format_Date();
+ LogData->string = string;
+
+ LogPrintTmpList = g_list_append(LogPrintTmpList,LogData);
+ //g_print("%s",string);
+ }
+
+
+ // Store also the messages in the log file.
+ if (!file_path)
+ file_path = g_strconcat(HOME_VARIABLE,
+ HOME_VARIABLE[strlen(HOME_VARIABLE)-1]!=G_DIR_SEPARATOR ? G_DIR_SEPARATOR_S : "",
+ LOG_FILE,NULL);
+
+ // The first time, the whole file is delete. Else, text is appended
+ if (first_time)
+ file = fopen(file_path,"w+");
+ else
+ file = fopen(file_path,"a+");
+ //g_free(file_path);
+
+ if (file)
+ {
+ gchar *time = Log_Format_Date();
+ gchar *data = g_strdup_printf("%s %s\n",time,string);
+ fwrite(data,strlen(data),1,file);
+ g_free(data);
+ g_free(time);
+
+ first_time = FALSE;
+ fclose(file);
+ }
+
+}
+
+/*
+ * Display pending messages in the LogList
+ */
+void Log_Print_Tmp_List (void)
+{
+ GtkTreeIter iter;
+
+ LogPrintTmpList = g_list_first(LogPrintTmpList);
+ while (LogPrintTmpList)
+ {
+
+ if (LogList && logListModel)
+ {
+ gtk_list_store_append(logListModel, &iter);
+ gtk_list_store_set(logListModel, &iter,
+ LOG_TIME_TEXT, ((Log_Data *)LogPrintTmpList->data)->time,
+ LOG_TEXT, ((Log_Data *)LogPrintTmpList->data)->string,
+ LOG_ROW_BACKGROUND, NULL,
+ LOG_ROW_FOREGROUND, NULL,
+ -1);
+ }
+
+ if (!LogPrintTmpList->next) break;
+ LogPrintTmpList = LogPrintTmpList->next;
+ }
+
+ // Free the list...
+ if (LogPrintTmpList)
+ {
+ LogPrintTmpList = g_list_first(LogPrintTmpList);
+ while (LogPrintTmpList)
+ {
+ g_free(((Log_Data *)LogPrintTmpList->data)->time);
+ g_free(((Log_Data *)LogPrintTmpList->data));
+
+ if (!LogPrintTmpList->next) break;
+ LogPrintTmpList = LogPrintTmpList->next;
+ }
+
+ g_list_free(LogPrintTmpList);
+ LogPrintTmpList = NULL;
+ }
+
+}
+
diff --git a/src/log.h b/src/log.h
new file mode 100644
index 0000000..2ec4408
--- /dev/null
+++ b/src/log.h
@@ -0,0 +1,42 @@
+/* log.h - 2007/03/25 */
+/*
+ * EasyTAG - Tag editor for MP3 and Ogg Vorbis files
+ * Copyright (C) 2000-2007 Jerome Couderc <easytag@gmail.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef __LOG_H__
+#define __LOG_H__
+
+#include <gtk/gtk.h>
+
+//#include "et_core.h"
+
+
+
+/**************
+ * Prototypes *
+ **************/
+
+GtkWidget *Create_Log_Area (void);
+
+void Log_Clean_Log_List (void);
+
+void Log_Print (gchar const *format, ...);
+
+
+#endif /* __LOG_H__ */
diff --git a/src/misc.c b/src/misc.c
new file mode 100755
index 0000000..41f12ec
--- /dev/null
+++ b/src/misc.c
@@ -0,0 +1,3392 @@
+/* misc.c - 2000/06/28 */
+/*
+ * EasyTAG - Tag editor for MP3 and Ogg Vorbis files
+ * Copyright (C) 2000-2003 Jerome Couderc <easytag@gmail.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+#include <glib/gi18n-lib.h>
+#include <ctype.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "misc.h"
+#include "easytag.h"
+#include "msgbox.h"
+#include "id3_tag.h"
+#include "browser.h"
+#include "setting.h"
+#include "bar.h"
+#include "prefs.h"
+#include "scan.h"
+#include "genres.h"
+#include "log.h"
+#include "charset.h"
+
+#ifdef WIN32
+# include <windows.h>
+#endif
+
+
+/***************
+ * Declaration *
+ ***************/
+// Playlist window defined in misc.h
+
+// Search file window
+GtkWidget *SearchFileWindow = NULL;
+GtkWidget *SearchStringCombo;
+GtkListStore *SearchStringModel = NULL;
+GtkWidget *SearchInFilename;
+GtkWidget *SearchInTag;
+GtkWidget *SearchCaseSensitive;
+GtkWidget *SearchResultList;
+GtkListStore *SearchResultListModel;
+GtkWidget *SearchStatusBar;
+guint SearchStatusBarContext;
+
+// Load filename window
+GtkWidget *LoadFilenameWindow = NULL;
+GtkWidget *FileToLoadCombo;
+GtkListStore *FileToLoadModel = NULL;
+GtkWidget *LoadFileContentList;
+GtkListStore* LoadFileContentListModel;
+GtkWidget *LoadFileNameList;
+GtkListStore* LoadFileNameListModel;
+
+
+enum
+{
+ // Columns for titles
+ SEARCH_RESULT_FILENAME = 0,
+ SEARCH_RESULT_TITLE,
+ SEARCH_RESULT_ARTIST,
+ SEARCH_RESULT_ALBUM,
+ SEARCH_RESULT_DISC_NUMBER,
+ SEARCH_RESULT_YEAR,
+ SEARCH_RESULT_TRACK,
+ SEARCH_RESULT_GENRE,
+ SEARCH_RESULT_COMMENT,
+ SEARCH_RESULT_COMPOSER,
+ SEARCH_RESULT_ORIG_ARTIST,
+ SEARCH_RESULT_COPYRIGHT,
+ SEARCH_RESULT_URL,
+ SEARCH_RESULT_ENCODED_BY,
+
+ // Columns for pango style (normal/bold)
+ SEARCH_RESULT_FILENAME_WEIGHT,
+ SEARCH_RESULT_TITLE_WEIGHT,
+ SEARCH_RESULT_ARTIST_WEIGHT,
+ SEARCH_RESULT_ALBUM_WEIGHT,
+ SEARCH_RESULT_DISC_NUMBER_WEIGHT,
+ SEARCH_RESULT_YEAR_WEIGHT,
+ SEARCH_RESULT_TRACK_WEIGHT,
+ SEARCH_RESULT_GENRE_WEIGHT,
+ SEARCH_RESULT_COMMENT_WEIGHT,
+ SEARCH_RESULT_COMPOSER_WEIGHT,
+ SEARCH_RESULT_ORIG_ARTIST_WEIGHT,
+ SEARCH_RESULT_COPYRIGHT_WEIGHT,
+ SEARCH_RESULT_URL_WEIGHT,
+ SEARCH_RESULT_ENCODED_BY_WEIGHT,
+
+ // Columns for color (normal/red)
+ SEARCH_RESULT_FILENAME_FOREGROUND,
+ SEARCH_RESULT_TITLE_FOREGROUND,
+ SEARCH_RESULT_ARTIST_FOREGROUND,
+ SEARCH_RESULT_ALBUM_FOREGROUND,
+ SEARCH_RESULT_DISC_NUMBER_FOREGROUND,
+ SEARCH_RESULT_YEAR_FOREGROUND,
+ SEARCH_RESULT_TRACK_FOREGROUND,
+ SEARCH_RESULT_GENRE_FOREGROUND,
+ SEARCH_RESULT_COMMENT_FOREGROUND,
+ SEARCH_RESULT_COMPOSER_FOREGROUND,
+ SEARCH_RESULT_ORIG_ARTIST_FOREGROUND,
+ SEARCH_RESULT_COPYRIGHT_FOREGROUND,
+ SEARCH_RESULT_URL_FOREGROUND,
+ SEARCH_RESULT_ENCODED_BY_FOREGROUND,
+
+ SEARCH_RESULT_POINTER,
+ SEARCH_COLUMN_COUNT
+};
+
+enum
+{
+ LOAD_FILE_CONTENT_TEXT,
+ LOAD_FILE_CONTENT_COUNT
+};
+
+enum
+{
+ LOAD_FILE_NAME_TEXT,
+ LOAD_FILE_NAME_POINTER,
+ LOAD_FILE_NAME_COUNT
+};
+
+/**************
+ * Prototypes *
+ **************/
+void Open_Write_Playlist_Window (void);
+gboolean Write_Playlist_Window_Key_Press (GtkWidget *window, GdkEvent *event);
+void Destroy_Write_Playlist_Window (void);
+void Playlist_Write_Button_Pressed (void);
+gboolean Write_Playlist (gchar *play_list_name);
+gboolean Playlist_Check_Content_Mask (GtkObject *widget_to_show_hide, GtkEntry *widget_source);
+void Playlist_Convert_Forwardslash_Into_Backslash (gchar *string);
+
+void Open_Search_File_Window (void);
+void Destroy_Search_File_Window (void);
+gboolean Search_File_Window_Key_Press (GtkWidget *window, GdkEvent *event);
+void Search_File (GtkWidget *search_button);
+void Add_Row_To_Search_Result_List (ET_File *ETFile,const gchar *string_to_search);
+void Search_Result_List_Row_Selected (GtkTreeSelection* selection, gpointer data);
+
+void Open_Load_Filename_Window (void);
+void Destroy_Load_Filename_Window (void);
+gboolean Load_Filename_Window_Key_Press (GtkWidget *window, GdkEvent *event);
+void Load_Filename_List_Key_Press (GtkWidget *clist, GdkEvent *event);
+void Load_File_Content (GtkWidget *file_entry);
+void Load_File_List (void);
+void Load_Filename_Select_Row_In_Other_List (GtkWidget *target, gpointer selection_emit);
+void Load_Filename_Set_Filenames (void);
+void Button_Load_Set_Sensivity (GtkWidget *button, GtkWidget *entry);
+GtkWidget *Create_Load_Filename_Popup_Menu (GtkWidget *list);
+void Load_Filename_List_Insert_Blank_Line (GtkWidget *list);
+void Load_Filename_List_Delete_Line (GtkWidget *list);
+void Load_Filename_List_Delete_All_Blank_Lines (GtkWidget *list);
+void Load_Filename_List_Reload (GtkWidget *list);
+void Load_Filename_Update_Text_Line (GtkWidget *entry, GtkWidget *list);
+void Load_Filename_Edit_Text_Line (GtkTreeSelection *selection, gpointer data);
+
+void Create_Xpm_Icon_Factory (const char **xpmdata, const char *name);
+
+/* Browser */
+static void Open_File_Selection_Window (GtkWidget *entry, gchar *title, GtkFileChooserAction action);
+void File_Selection_Window_For_File (GtkWidget *entry);
+void File_Selection_Window_For_Directory (GtkWidget *entry);
+
+
+/*************
+ * Functions *
+ *************/
+
+/******************************
+ * Functions managing pixmaps *
+ ******************************/
+/*
+ * Buttons creation with pixmap
+ */
+
+GtkWidget *Create_Button_With_Pixmap(guint button_type)
+{
+ GtkWidget *Button;
+ GtkWidget *HBox;
+ GtkWidget *Label;
+ GtkWidget *Pixmap;
+
+ gtk_widget_realize(MainWindow);
+ switch (button_type)
+ {
+ case BUTTON_OK:
+ Label = gtk_label_new(_(" OK "));
+ Pixmap = gtk_image_new_from_stock(GTK_STOCK_OK, GTK_ICON_SIZE_BUTTON);
+ break;
+
+ case BUTTON_YES:
+ Label = gtk_label_new(_(" Yes "));
+ Pixmap = gtk_image_new_from_stock(GTK_STOCK_YES, GTK_ICON_SIZE_BUTTON);
+ break;
+
+ case BUTTON_NO:
+ Label = gtk_label_new(_(" No "));
+ Pixmap = gtk_image_new_from_stock(GTK_STOCK_NO, GTK_ICON_SIZE_BUTTON);
+ break;
+
+ case BUTTON_APPLY:
+ Label = gtk_label_new(_(" Apply "));
+ Pixmap = gtk_image_new_from_stock(GTK_STOCK_APPLY, GTK_ICON_SIZE_BUTTON);
+ break;
+
+ case BUTTON_SAVE:
+ Label = gtk_label_new(_(" Save "));
+ Pixmap = gtk_image_new_from_stock(GTK_STOCK_SAVE, GTK_ICON_SIZE_BUTTON);
+ break;
+
+ case BUTTON_CANCEL:
+ Label = gtk_label_new(_(" Cancel "));
+ Pixmap = gtk_image_new_from_stock(GTK_STOCK_CANCEL, GTK_ICON_SIZE_BUTTON);
+ break;
+
+ case BUTTON_CLOSE:
+ Label = gtk_label_new(_(" Close "));
+ Pixmap = gtk_image_new_from_stock(GTK_STOCK_CLOSE, GTK_ICON_SIZE_BUTTON);
+ break;
+
+ case BUTTON_WRITE:
+ Label = gtk_label_new(_(" Write "));
+ Pixmap = gtk_image_new_from_stock(GTK_STOCK_SAVE_AS, GTK_ICON_SIZE_BUTTON);
+ break;
+
+ case BUTTON_EXECUTE:
+ Label = gtk_label_new(_(" Execute "));
+ Pixmap = gtk_image_new_from_stock(GTK_STOCK_EXECUTE, GTK_ICON_SIZE_BUTTON);
+ break;
+
+ case BUTTON_SEARCH:
+ Label = gtk_label_new(_(" Search "));
+ Pixmap = gtk_image_new_from_stock(GTK_STOCK_FIND, GTK_ICON_SIZE_BUTTON);
+ break;
+
+ case BUTTON_BROWSE:
+ Label = gtk_label_new(_(" Browse... "));
+ Pixmap = gtk_image_new_from_stock(GTK_STOCK_OPEN, GTK_ICON_SIZE_BUTTON);
+ break;
+
+ default:
+ Button = gtk_button_new_with_label("Unknown button");
+ return Button;
+ break;
+ }
+
+ Button = gtk_button_new();
+ HBox = gtk_hbox_new(FALSE,0);
+ gtk_container_add(GTK_CONTAINER(Button),HBox);
+ /* Add items in button */
+ gtk_container_add(GTK_CONTAINER(HBox),Pixmap);
+ gtk_container_add(GTK_CONTAINER(HBox),Label);
+ /* Alignment of items */
+ gtk_misc_set_alignment(GTK_MISC(Pixmap),1,0.5);
+ gtk_misc_set_alignment(GTK_MISC(Label),0,0.5);
+
+ return Button;
+}
+
+/*
+ * Create an icon into an event box to allow some events (as to display tooltips).
+ */
+GtkWidget *Create_Pixmap_Icon_With_Event_Box (const gchar *pixmap_name)
+{
+ GtkWidget *icon;
+ GtkWidget *EventBox;
+
+ EventBox = gtk_event_box_new();
+ if (pixmap_name)
+ {
+ icon = gtk_image_new_from_stock(pixmap_name, GTK_ICON_SIZE_BUTTON);
+ gtk_container_add(GTK_CONTAINER(EventBox),icon);
+ }
+
+ return EventBox;
+}
+
+/*
+ * Return a button with an icon and a label
+ */
+GtkWidget *Create_Button_With_Icon_And_Label (const gchar *pixmap_name, gchar *label)
+{
+ GtkWidget *Button;
+ GtkWidget *HBox;
+ GtkWidget *Label;
+ GtkWidget *Pixmap;
+
+ Button = gtk_button_new();
+ HBox = gtk_hbox_new(FALSE,0);
+ gtk_container_add(GTK_CONTAINER(Button),HBox);
+
+ /* Add a pixmap if not null */
+ if (pixmap_name != NULL)
+ {
+ gtk_widget_realize(MainWindow);
+ Pixmap = gtk_image_new_from_stock(pixmap_name, GTK_ICON_SIZE_BUTTON);
+ gtk_container_add(GTK_CONTAINER(HBox),Pixmap);
+ }
+
+ /* Add a label if not null */
+ if (label != NULL)
+ {
+ Label = gtk_label_new(label);
+ gtk_container_add(GTK_CONTAINER(HBox),Label);
+ }
+
+ /* Display a warning message if the both parameters are NULL */
+ if (pixmap_name==NULL && label==NULL)
+ g_warning("Empty button created 'adr=%p' (no icon and no label)!",Button);
+
+ return Button;
+}
+
+/*
+ * Add the 'string' passed in parameter to the list store
+ * If this string already exists in the list store, it doesn't add it.
+ * Returns TRUE if string was added.
+ */
+gboolean Add_String_To_Combo_List (GtkListStore *liststore, gchar *str)
+{
+ GtkTreeIter iter;
+ gchar *text;
+ guint HISTORY_MAX_LENGTH = 15;
+ //gboolean found = FALSE;
+ gchar *string = g_strdup(str);
+
+ if (!string || g_utf8_strlen(string, -1) <= 0)
+ return FALSE;
+
+#if 0
+ // We add the string to the beginning of the list store
+ // So we will start to parse from the second line below
+ gtk_list_store_prepend(liststore, &iter);
+ gtk_list_store_set(liststore, &iter, MISC_COMBO_TEXT, string, -1);
+
+ // Search in the list store if string already exists and remove other same strings in the list
+ found = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(liststore), &iter);
+ //gtk_tree_model_get(GTK_TREE_MODEL(liststore), &iter, MISC_COMBO_TEXT, &text, -1);
+ while (found && gtk_tree_model_iter_next(GTK_TREE_MODEL(liststore), &iter))
+ {
+ gtk_tree_model_get(GTK_TREE_MODEL(liststore), &iter, MISC_COMBO_TEXT, &text, -1);
+ //g_print(">0>%s\n>1>%s\n",string,text);
+ if (g_utf8_collate(text, string) == 0)
+ {
+ g_free(text);
+ // FIX ME : it seems that after it selects the next item for the
+ // combo (changes 'string')????
+ // So should select the first item?
+ gtk_list_store_remove(liststore, &iter);
+ // Must be rewinded?
+ found = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(liststore), &iter);
+ //gtk_tree_model_get(GTK_TREE_MODEL(liststore), &iter, MISC_COMBO_TEXT, &text, -1);
+ continue;
+ }
+ g_free(text);
+ }
+
+ // Limit list size to HISTORY_MAX_LENGTH
+ while (gtk_tree_model_iter_n_children(GTK_TREE_MODEL(liststore),NULL) > HISTORY_MAX_LENGTH)
+ {
+ if ( gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(liststore),
+ &iter,NULL,HISTORY_MAX_LENGTH) )
+ {
+ gtk_list_store_remove(liststore, &iter);
+ }
+ }
+
+ g_free(string);
+ // Place again to the beginning of the list, to select the right value?
+ //gtk_tree_model_get_iter_first(GTK_TREE_MODEL(liststore), &iter);
+
+ return TRUE;
+
+#else
+
+ // Search in the list store if string already exists.
+ // FIXME : insert string at the beginning of the list (if already exists),
+ // and remove other same strings in the list
+ if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(liststore), &iter))
+ {
+ do
+ {
+ gtk_tree_model_get(GTK_TREE_MODEL(liststore), &iter, MISC_COMBO_TEXT, &text, -1);
+ if (g_utf8_collate(text, string) == 0)
+ {
+ g_free(text);
+ return FALSE;
+ }
+
+ g_free(text);
+ } while(gtk_tree_model_iter_next(GTK_TREE_MODEL(liststore), &iter));
+ }
+
+ // We add the string to the beginning of the list store
+ gtk_list_store_prepend(liststore, &iter);
+ gtk_list_store_set(liststore, &iter, MISC_COMBO_TEXT, string, -1);
+
+ // Limit list size to HISTORY_MAX_LENGTH
+ while (gtk_tree_model_iter_n_children(GTK_TREE_MODEL(liststore),NULL) > HISTORY_MAX_LENGTH)
+ {
+ if ( gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(liststore),
+ &iter,NULL,HISTORY_MAX_LENGTH) )
+ {
+ gtk_list_store_remove(liststore, &iter);
+ }
+ }
+
+ g_free(string);
+ return TRUE;
+#endif
+}
+
+/*
+ * Returns the text of the selected item in a combo box
+ * Remember to free the returned value...
+ */
+gchar *Get_Active_Combo_Box_Item (GtkComboBox *combo)
+{
+ gchar *text;
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+
+ if (!combo)
+ return NULL;
+
+ model = gtk_combo_box_get_model(combo);
+ if (!gtk_combo_box_get_active_iter(combo, &iter))
+ return NULL;
+
+ gtk_tree_model_get(model, &iter, MISC_COMBO_TEXT, &text, -1);
+ return text;
+}
+
+/*
+ * Event attached to an entry to disable an other widget (for example: a button)
+ * when the entry is empty
+ */
+void Entry_Changed_Disable_Object(GtkObject *widget_to_disable, GtkEditable *source_widget)
+{
+ gchar *text = NULL;
+
+ if (!widget_to_disable || !source_widget) return;
+
+ text = gtk_editable_get_chars(GTK_EDITABLE(source_widget),0,-1);
+ if (!text || strlen(text)<1)
+ gtk_widget_set_sensitive(GTK_WIDGET(widget_to_disable),FALSE);
+ else
+ gtk_widget_set_sensitive(GTK_WIDGET(widget_to_disable),TRUE);
+
+ g_free(text);
+}
+
+/*
+ * To insert only digits in an entry. If the text contains only digits: returns it,
+ * else only first digits.
+ */
+void Insert_Only_Digit (GtkEditable *editable, const gchar *inserted_text, gint length,
+ gint *position, gpointer data)
+{
+ int i = 1; // Ignore first character
+ int j = 1;
+ gchar *result;
+
+ if (length<=0 || !inserted_text)
+ return;
+
+ if (!isdigit((guchar)inserted_text[0]) && inserted_text[0] != '-')
+ {
+ g_signal_stop_emission_by_name(G_OBJECT(editable),"insert_text");
+ return;
+ } else if (length == 1)
+ {
+ // We already checked the first digit...
+ return;
+ }
+
+ g_signal_stop_emission_by_name(G_OBJECT(editable),"insert_text");
+ result = g_malloc0(length);
+ result[0] = inserted_text[0];
+
+ // Check the rest, if any...
+ for (i = 1; i < length; i++)
+ {
+ if (isdigit((guchar)inserted_text[i]))
+ {
+ result[j++] = inserted_text[i];
+ }
+ }
+
+ if (result[0] == (gchar)NULL)
+ {
+ g_free(result);
+ return;
+ }
+
+ g_signal_handlers_block_by_func(G_OBJECT(editable),G_CALLBACK(Insert_Only_Digit),data);
+ gtk_editable_insert_text(editable, result, j, position);
+ g_signal_handlers_unblock_by_func(G_OBJECT(editable),G_CALLBACK(Insert_Only_Digit),data);
+ g_free(result);
+}
+
+/*
+ * Parse and auto complete date entry if you don't type the 4 digits.
+ */
+#include <time.h>
+#include <stdlib.h>
+gboolean Parse_Date (void)
+{
+ const gchar *year;
+ gchar *tmp, *tmp1;
+ gchar current_year[5];
+ time_t t;
+ struct tm t0;
+
+ if (!DATE_AUTO_COMPLETION) return FALSE;
+
+ /* Get the info entered by user */
+ year = gtk_entry_get_text(GTK_ENTRY(YearEntry));
+
+ if ( strcmp(year,"")!=0 && strlen(year)<4 )
+ {
+ t = time(NULL);
+ /* Get the current date */
+ memcpy(&t0, localtime(&t), sizeof(struct tm));
+ /* Put the current year in 'current_year' tab */
+ sprintf(current_year,"%d",1900+t0.tm_year);
+
+ tmp = &current_year[4-strlen(year)];
+ if ( atoi(year) <= atoi(tmp) )
+ {
+ sprintf(current_year,"%d",atoi(current_year)-atoi(tmp));
+ tmp1 = g_strdup_printf("%d",atoi(current_year)+atoi(year));
+ gtk_entry_set_text(GTK_ENTRY(YearEntry),tmp1);
+ g_free(tmp1);
+
+ }else
+ {
+ sprintf(current_year,"%d",atoi(current_year)-atoi(tmp)
+ -(strlen(year)<=0 ? 1 : strlen(year)<=1 ? 10 : // pow(10,strlen(year)) returns 99 instead of 100 under Win32...
+ strlen(year)<=2 ? 100 : strlen(year)<=3 ? 1000 : 0));
+ tmp1 = g_strdup_printf("%d",atoi(current_year)+atoi(year));
+ gtk_entry_set_text(GTK_ENTRY(YearEntry),tmp1);
+ g_free(tmp1);
+ }
+ }
+ return FALSE;
+}
+
+/*
+ * Load the genres list to the combo, and sorts it
+ */
+int Compare_Two_Genres (gchar *genre1,gchar *genre2)
+{
+ return strcmp(genre1,genre2);
+}
+
+void Load_Genres_List_To_UI (void)
+{
+ guint i;
+ GtkTreeIter iter;
+
+ if (!GenreComboModel) return;
+
+ gtk_list_store_append(GTK_LIST_STORE(GenreComboModel), &iter);
+ gtk_list_store_set(GTK_LIST_STORE(GenreComboModel), &iter, MISC_COMBO_TEXT, "", -1);
+
+ gtk_list_store_append(GTK_LIST_STORE(GenreComboModel), &iter);
+ gtk_list_store_set(GTK_LIST_STORE(GenreComboModel), &iter, MISC_COMBO_TEXT, "Unknown", -1);
+
+ for (i=0; i<=GENRE_MAX; i++)
+ {
+ gtk_list_store_append(GTK_LIST_STORE(GenreComboModel), &iter);
+ gtk_list_store_set(GTK_LIST_STORE(GenreComboModel), &iter, MISC_COMBO_TEXT, id3_genres[i], -1);
+ }
+}
+
+/*
+ * Load the track numbers into the track combo list
+ * We limit the preloaded values to 30 to avoid lost of time with lot of files...
+ */
+void Load_Track_List_To_UI (void)
+{
+ guint len;
+ guint i;
+ GtkTreeIter iter;
+ gchar *text;
+
+ if (!ETCore->ETFileDisplayedList || !TrackEntryComboModel) return;
+
+ // Number mini of items
+ //if ((len=ETCore->ETFileDisplayedList_Length) < 30)
+ // Length limited to 30 (instead to the number of files)!
+ len = 30;
+
+ // Create list of tracks
+ for (i=1; i<=len; i++)
+ {
+
+ if (NUMBER_TRACK_FORMATED)
+ {
+ text = g_strdup_printf("%.*d",NUMBER_TRACK_FORMATED_SPIN_BUTTON,i);
+ } else
+ {
+ text = g_strdup_printf("%.2d",i);
+ }
+
+ gtk_list_store_append(GTK_LIST_STORE(TrackEntryComboModel), &iter);
+ gtk_list_store_set(GTK_LIST_STORE(TrackEntryComboModel), &iter, MISC_COMBO_TEXT, text, -1);
+ g_free(text);
+ }
+
+}
+
+/*
+ * Change mouse cursor
+ */
+void Init_Mouse_Cursor (void)
+{
+ MouseCursor = NULL;
+}
+
+void Destroy_Mouse_Cursor (void)
+{
+ if (MouseCursor)
+ {
+ gdk_cursor_unref(MouseCursor);
+ MouseCursor = NULL;
+ }
+}
+
+void Set_Busy_Cursor (void)
+{
+ /* If still built, destroy it to avoid memory leak */
+ Destroy_Mouse_Cursor();
+ /* Create the new cursor */
+ MouseCursor = gdk_cursor_new(GDK_WATCH);
+ gdk_window_set_cursor(MainWindow->window,MouseCursor);
+}
+
+void Set_Unbusy_Cursor (void)
+{
+ /* Back to standard cursor */
+ gdk_window_set_cursor(MainWindow->window,NULL);
+ Destroy_Mouse_Cursor();
+}
+
+
+
+/*
+ * Add easytag specific icons to GTK stock set
+ */
+#include "../pixmaps/scan.xpm"
+#include "../pixmaps/select_all.xpm"
+#include "../pixmaps/invert_selection.xpm"
+#include "../pixmaps/add.xpm"
+#include "../pixmaps/unselect_all.xpm"
+#include "../pixmaps/grab.xpm"
+#include "../pixmaps/mask.xpm"
+//#include "../pixmaps/blackwhite.xpm"
+#include "../pixmaps/forbidden.xpm"
+#include "../pixmaps/read_only.xpm"
+//#include "../pixmaps/sequence_track.xpm"
+#include "../pixmaps/red_lines.xpm"
+#include "../pixmaps/artist_album.xpm"
+#include "../pixmaps/add_folder.xpm"
+#include "../pixmaps/parent_folder.xpm"
+#include "../pixmaps/sound.xpm"
+#include "../pixmaps/all_uppercase.xpm"
+#include "../pixmaps/all_downcase.xpm"
+#include "../pixmaps/first_letter_uppercase.xpm"
+#include "../pixmaps/first_letter_uppercase_word.xpm"
+void Init_Custom_Icons (void)
+{
+ Create_Xpm_Icon_Factory((const char**)select_all_xpm, "easytag-select-all");
+ Create_Xpm_Icon_Factory((const char**)scan_xpm, "easytag-scan");
+ Create_Xpm_Icon_Factory((const char**)invert_selection_xpm, "easytag-invert-selection");
+ Create_Xpm_Icon_Factory((const char**)add_xpm, "easytag-add");
+ Create_Xpm_Icon_Factory((const char**)unselect_all_xpm, "easytag-unselect-all");
+ Create_Xpm_Icon_Factory((const char**)grab_xpm, "easytag-grab");
+ Create_Xpm_Icon_Factory((const char**)mask_xpm, "easytag-mask");
+ //Create_Xpm_Icon_Factory((const char**)blackwhite_xpm, "easytag-blackwhite");
+ Create_Xpm_Icon_Factory((const char**)forbidden_xpm, "easytag-forbidden");
+ Create_Xpm_Icon_Factory((const char**)read_only_xpm, "easytag-read-only");
+ //Create_Xpm_Icon_Factory((const char**)sequence_track_xpm, "easytag-sequence-track");
+ Create_Xpm_Icon_Factory((const char**)red_lines_xpm, "easytag-red-lines");
+ Create_Xpm_Icon_Factory((const char**)artist_album_xpm, "easytag-artist-album");
+ Create_Xpm_Icon_Factory((const char**)parent_folder_xpm, "easytag-parent-folder");
+ Create_Xpm_Icon_Factory((const char**)add_folder_xpm, "easytag-add-folder");
+ Create_Xpm_Icon_Factory((const char**)sound_xpm, "easytag-sound");
+ Create_Xpm_Icon_Factory((const char**)all_uppercase_xpm, "easytag-all-uppercase");
+ Create_Xpm_Icon_Factory((const char**)all_downcase_xpm, "easytag-all-downcase");
+ Create_Xpm_Icon_Factory((const char**)first_letter_uppercase_xpm, "easytag-first-letter-uppercase");
+ Create_Xpm_Icon_Factory((const char**)first_letter_uppercase_word_xpm, "easytag-first-letter-uppercase-word");
+}
+
+
+/*
+ * Create an icon factory from the specified pixmap
+ * Also add it to the GTK stock images
+ */
+void Create_Xpm_Icon_Factory (const char **xpmdata, const char *xpmname)
+{
+ GtkIconSet *set;
+ GtkIconFactory *factory;
+ GdkPixbuf *pixbuf;
+
+ if (!*xpmdata || !xpmname)
+ return;
+
+ pixbuf = gdk_pixbuf_new_from_xpm_data(xpmdata);
+
+ set = gtk_icon_set_new_from_pixbuf(pixbuf);
+ factory = gtk_icon_factory_new();
+ gtk_icon_factory_add(factory, xpmname, set);
+ gtk_icon_set_unref(set);
+ gtk_icon_factory_add_default(factory);
+}
+
+/*
+ * Return a widget with a pixmap
+ * Note: for pixmap 'pixmap.xpm', pixmap_name is 'pixmap_xpm'
+ */
+GtkWidget *Create_Xpm_Image (const char **xpm_name)
+{
+ GdkPixbuf *pixbuf;
+ GtkWidget *image;
+
+ if (!*xpm_name)
+ return NULL;
+
+ pixbuf = gdk_pixbuf_new_from_xpm_data(xpm_name);
+ image = gtk_image_new_from_pixbuf(GDK_PIXBUF(pixbuf));
+
+ return image;
+}
+
+
+
+/*
+ * Iter compare func: Sort alphabetically
+ */
+gint Combo_Alphabetic_Sort (GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer data)
+{
+ gchar *text1, *text1_folded;
+ gchar *text2, *text2_folded;
+ gint ret;
+
+ gtk_tree_model_get(model, a, MISC_COMBO_TEXT, &text1, -1);
+ gtk_tree_model_get(model, b, MISC_COMBO_TEXT, &text2, -1);
+
+ if (text1 == text2)
+ {
+ g_free(text1);
+ g_free(text2);
+ return 0;
+ }
+
+ if (text1 == NULL)
+ {
+ g_free(text2);
+ return -1;
+ }
+
+ if (text2 == NULL)
+ {
+ g_free(text1);
+ return 1;
+ }
+
+ text1_folded = g_utf8_casefold(text1, -1);
+ text2_folded = g_utf8_casefold(text2, -1);
+ ret = g_utf8_collate(text1_folded, text2_folded);
+
+ g_free(text1);
+ g_free(text2);
+ g_free(text1_folded);
+ g_free(text2_folded);
+ return ret;
+}
+
+
+/*************************
+ * File selection window *
+ *************************/
+void File_Selection_Window_For_File (GtkWidget *entry)
+{
+ Open_File_Selection_Window(entry, _("Select directory..."), GTK_FILE_CHOOSER_ACTION_OPEN);
+}
+
+void File_Selection_Window_For_Directory (GtkWidget *entry)
+{
+ Open_File_Selection_Window(entry, _("Select file..."), GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER);
+}
+
+/*
+ * Open the file selection window and saves the selected file path into entry
+ */
+static void Open_File_Selection_Window (GtkWidget *entry, gchar *title, GtkFileChooserAction action)
+{
+ gchar *tmp;
+ gchar *filename, *filename_utf8;
+ GtkWidget *FileSelectionWindow;
+ GtkWindow *parent_window = NULL;
+
+ parent_window = (GtkWindow*) gtk_widget_get_toplevel(entry);
+ if (!GTK_WIDGET_TOPLEVEL(parent_window))
+ {
+ g_warning("Could not get parent window\n");
+ return;
+ }
+
+ FileSelectionWindow = gtk_file_chooser_dialog_new(title, parent_window, action,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
+ NULL);
+ // Set initial directory
+ tmp = (gchar*) gtk_entry_get_text(GTK_ENTRY(entry));
+ if (tmp && *tmp)
+ {
+ if (!gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(FileSelectionWindow),tmp))
+ gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(FileSelectionWindow),tmp);
+ }
+
+
+ if (gtk_dialog_run(GTK_DIALOG(FileSelectionWindow)) == GTK_RESPONSE_ACCEPT)
+ {
+ filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(FileSelectionWindow));
+ filename_utf8 = filename_to_display(filename);
+ gtk_entry_set_text(GTK_ENTRY(entry),filename_utf8);
+ g_free(filename);
+ g_free(filename_utf8);
+ }
+ gtk_widget_destroy(FileSelectionWindow);
+}
+
+
+
+/*
+ * Run the audio player and load files of the current dir
+ */
+void Run_Audio_Player_Using_File_List (GList *etfilelist_init)
+{
+ gchar **argv;
+ gint argv_index = 0;
+ GList *etfilelist;
+ ET_File *etfile;
+ gchar *filename;
+ gchar *program_path;
+#ifdef WIN32
+ gchar *argv_join;
+ STARTUPINFO siStartupInfo;
+ PROCESS_INFORMATION piProcessInfo;
+#else
+ pid_t pid;
+ gchar **argv_user;
+ gint argv_user_number;
+#endif
+
+ // Exit if no program selected...
+ if (!AUDIO_FILE_PLAYER || strlen(g_strstrip(AUDIO_FILE_PLAYER))<1)
+ {
+ GtkWidget *msgbox = msg_box_new(_("Warning..."),_("No audio player defined!"),GTK_STOCK_DIALOG_WARNING,BUTTON_OK,0);
+ msg_box_hide_check_button(MSG_BOX(msgbox));
+ //gtk_window_set_transient_for(GTK_WINDOW(msgbox),GTK_WINDOW(OptionsWindow));
+ msg_box_run(MSG_BOX(msgbox));
+ gtk_widget_destroy(msgbox);
+
+ return;
+ }
+
+ if ( !(program_path = Check_If_Executable_Exists(AUDIO_FILE_PLAYER)) )
+ {
+ gchar *msg = g_strdup_printf(_("The program '%s' can't be found!"),AUDIO_FILE_PLAYER);
+ Log_Print(msg);
+ g_free(msg);
+ return;
+ }
+ g_free(program_path);
+
+ // The list of files to play
+ etfilelist = etfilelist_init;
+
+#ifdef WIN32
+
+ // See documentation : http://c.developpez.com/faq/vc/?page=ProcessThread and http://www.answers.com/topic/createprocess
+ ZeroMemory(&siStartupInfo, sizeof(siStartupInfo));
+ siStartupInfo.cb = sizeof(siStartupInfo);
+ ZeroMemory(&piProcessInfo, sizeof(piProcessInfo));
+
+ argv = g_new0(gchar *,g_list_length(etfilelist) + 2); // "+2" for 1rst arg 'foo' and last arg 'NULL'
+ argv[argv_index++] = "foo";
+
+ // Load files as arguments
+ while (etfilelist)
+ {
+ etfile = (ET_File *)etfilelist->data;
+ filename = ((File_Name *)etfile->FileNameCur->data)->value;
+ //filename_utf8 = ((File_Name *)etfile->FileNameCur->data)->value_utf8;
+ // We must enclose filename between quotes, because of possible (probable!) spaces in filenames"
+ argv[argv_index++] = g_strconcat("\"", filename, "\"", NULL);
+ etfilelist = etfilelist->next;
+ }
+ argv[argv_index] = NULL; // Ends the list of arguments
+
+ // Make a command line with all arguments (joins strings together to form one long string separated by a space)
+ argv_join = g_strjoinv(" ", argv);
+
+ if (CreateProcess(AUDIO_FILE_PLAYER,
+ argv_join,
+ NULL,
+ NULL,
+ FALSE,
+ CREATE_DEFAULT_ERROR_MODE,
+ NULL,
+ NULL,
+ &siStartupInfo,
+ &piProcessInfo) == FALSE)
+ {
+ Log_Print(_("Can't execute %s (error %d)!\n"), AUDIO_FILE_PLAYER, GetLastError());
+ }
+
+ // Free allocated parameters (for each filename)
+ for (argv_index = 1; argv[argv_index]; argv_index++)
+ g_free(argv[argv_index]);
+
+ g_free(argv_join);
+
+#else
+
+ argv_user = g_strsplit(AUDIO_FILE_PLAYER," ",0); // the string may contains arguments, space is the delimiter
+ // Number of arguments into 'argv_user'
+ for (argv_user_number=0;argv_user[argv_user_number];argv_user_number++);
+
+ argv = g_new0(gchar *,argv_user_number + g_list_length(etfilelist) + 1); // +1 for NULL
+
+ // Load 'user' arguments (program name and more...)
+ while (argv_user[argv_index])
+ {
+ argv[argv_index] = argv_user[argv_index];
+ argv_index++;
+ }
+
+ // Load files as arguments
+ while (etfilelist)
+ {
+ etfile = (ET_File *)etfilelist->data;
+ filename = ((File_Name *)etfile->FileNameCur->data)->value;
+ //filename_utf8 = ((File_Name *)etfile->FileNameCur->data)->value_utf8;
+ argv[argv_index++] = filename;
+ etfilelist = etfilelist->next;
+ }
+ argv[argv_index] = NULL; // Ends the list of arguments
+
+ pid = fork();
+ switch (pid)
+ {
+ case -1:
+ Log_Print(_("Can't fork another process!\n"));
+ break;
+ case 0:
+ {
+ if (execvp(argv[0],argv) == -1)
+ {
+ Log_Print(_("Can't execute %s (%s)!\n"),argv[0],g_strerror(errno));
+ }
+ g_strfreev(argv_user);
+ _exit(1);
+ break;
+ }
+ default:
+ break;
+ }
+
+#endif
+
+ g_free(argv);
+}
+
+void Run_Audio_Player_Using_Directory (void)
+{
+ GList *etfilelist = g_list_first(ETCore->ETFileList);
+
+ Run_Audio_Player_Using_File_List(etfilelist);
+}
+
+void Run_Audio_Player_Using_Selection (void)
+{
+ GList *etfilelist = NULL;
+ GList *selfilelist = NULL;
+ ET_File *etfile;
+ GtkTreeSelection *selection;
+
+ if (!BrowserList) return;
+
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(BrowserList));
+ selfilelist = gtk_tree_selection_get_selected_rows(selection, NULL);
+ while (selfilelist)
+ {
+ etfile = Browser_List_Get_ETFile_From_Path(selfilelist->data);
+ etfilelist = g_list_append(etfilelist, etfile);
+
+ if (!selfilelist->next) break;
+ selfilelist = selfilelist->next;
+ }
+
+ g_list_foreach(selfilelist, (GFunc) gtk_tree_path_free, NULL);
+ g_list_free(selfilelist);
+
+ Run_Audio_Player_Using_File_List(etfilelist);
+
+ g_list_free(etfilelist);
+}
+
+void Run_Audio_Player_Using_Browser_Artist_List (void)
+{
+ GtkTreeIter iter;
+ GtkTreeSelection *selection;
+ GtkTreeModel *artistListModel;
+ GList *AlbumList, *etfilelist;
+ GList *concatenated_list = NULL;
+
+ if (!BrowserArtistList) return;
+
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(BrowserArtistList));
+ if (!gtk_tree_selection_get_selected(selection, &artistListModel, &iter))
+ return;
+
+ gtk_tree_model_get(artistListModel, &iter,
+ ARTIST_ALBUM_LIST_POINTER, &AlbumList,
+ -1);
+
+ while (AlbumList)
+ {
+ etfilelist = g_list_copy((GList *)AlbumList->data);
+ if (!concatenated_list)
+ concatenated_list = etfilelist;
+ else
+ concatenated_list = g_list_concat(concatenated_list,etfilelist);
+ AlbumList = AlbumList->next;
+ }
+
+ Run_Audio_Player_Using_File_List(concatenated_list);
+
+ if (concatenated_list)
+ g_list_free(concatenated_list);
+}
+
+void Run_Audio_Player_Using_Browser_Album_List (void)
+{
+ GtkTreeIter iter;
+ GtkTreeSelection *selection;
+ GtkTreeModel *albumListModel;
+ GList *etfilelist;
+
+ if (!BrowserAlbumList) return;
+
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(BrowserAlbumList));
+ if (!gtk_tree_selection_get_selected(selection, &albumListModel, &iter))
+ return;
+
+ gtk_tree_model_get(albumListModel, &iter,
+ ALBUM_ETFILE_LIST_POINTER, &etfilelist,
+ -1);
+
+ Run_Audio_Player_Using_File_List(etfilelist);
+}
+
+
+/*
+ * Check if the executable passed in parameter can be launched
+ * Returns the full path of the file (must be freed if not used)
+ */
+gchar *Check_If_Executable_Exists (const gchar *program)
+{
+ gchar *program_tmp;
+ gchar *tmp;
+
+ if (!program)
+ return NULL;
+
+ program_tmp = g_strdup(program);
+ g_strstrip(program_tmp);
+
+#ifdef WIN32
+ // Remove arguments if found, after '.exe'
+ if ( (tmp=strstr(program_tmp,".exe")) )
+ *(tmp + 4) = 0;
+#else
+ // Remove arguments if found
+ if ( (tmp=strchr(program_tmp,' ')) )
+ *tmp = 0;
+#endif
+
+ if (g_path_is_absolute(program_tmp))
+ {
+ if (access(program_tmp, X_OK) == 0)
+ {
+ return program_tmp;
+ } else
+ {
+ g_free(program_tmp);
+ return NULL;
+ }
+ } else
+ {
+ tmp = g_find_program_in_path(program_tmp);
+ if (tmp)
+ {
+ g_free(program_tmp);
+ return tmp;
+ }else
+ {
+ g_free(program_tmp);
+ return NULL;
+ }
+ }
+
+}
+
+
+
+/*
+ * The returned string must be freed after used
+ */
+gchar *Convert_Size (gfloat size)
+{
+ gchar *data = NULL;
+ /* Units Tab of file size (bytes,kilobytes,...) */
+ gchar *Units_Tab[] = { N_("B"), N_("KB"), N_("MB"), N_("GB"), N_("TB")};
+ gint i = 0;
+
+ while ( (gint)size/1024 && i<(gint)(sizeof(Units_Tab)/sizeof(Units_Tab[0])-1) )
+ {
+ size = size/1024;
+ i++;
+ }
+ return data = g_strdup_printf("%.1f %s",size,_(Units_Tab[i]));
+}
+
+/*
+ * Same that before except that if value in MB, we display 3 numbers after the coma
+ * The returned string must be freed after used
+ */
+gchar *Convert_Size_1 (gfloat size)
+{
+ gchar *data = NULL;
+ /* Units Tab of file size (bytes,kilobytes,...) */
+ gchar *Units_Tab[] = { N_("B"), N_("KB"), N_("MB"), N_("GB"), N_("TB")};
+ guint i = 0;
+
+ while ( (gint)size/1024 && i<(sizeof(Units_Tab)/sizeof(Units_Tab[0])-1) )
+ {
+ size = size/1024;
+ i++;
+ }
+ if (i >= 2) // For big values : display 3 number afer the separator (coma or point)
+ return data = g_strdup_printf("%.3f %s",size,_(Units_Tab[i]));
+ else
+ return data = g_strdup_printf("%.1f %s",size,_(Units_Tab[i]));
+}
+
+/*
+ * Convert a series of seconds into a readable duration
+ * Remember to free the string that is returned
+ */
+gchar *Convert_Duration (gulong duration)
+{
+ guint hour=0;
+ guint minute=0;
+ guint second=0;
+ gchar *data = NULL;
+
+ if (duration<=0)
+ return g_strdup_printf("%d:%.2d",minute,second);
+
+ hour = duration/3600;
+ minute = (duration%3600)/60;
+ second = (duration%3600)%60;
+
+ if (hour)
+ data = g_strdup_printf("%d:%.2d:%.2d",hour,minute,second);
+ else
+ data = g_strdup_printf("%d:%.2d",minute,second);
+
+ return data;
+}
+
+/*
+ * Returns the size of a file in bytes
+ */
+gulong Get_File_Size(gchar *filename)
+{
+ struct stat statbuf;
+
+ if (filename)
+ {
+ stat(filename,&statbuf);
+ return statbuf.st_size;
+ }else
+ {
+ return 0;
+ }
+}
+
+/*
+ * Delete spaces at the end and the beginning of the string
+ */
+void Strip_String (gchar *string)
+{
+ if (!string) return;
+ string = g_strstrip(string);
+}
+
+
+
+/*******************************
+ * Writting playlist functions *
+ *******************************/
+/*
+ * The window to write playlists.
+ */
+void Open_Write_Playlist_Window (void)
+{
+ GtkWidget *Frame;
+ GtkWidget *VBox;
+ GtkWidget *vbox, *hbox;
+ GtkWidget *ButtonBox;
+ GtkWidget *Button;
+ GtkWidget *Separator;
+ GtkWidget *Icon;
+ GtkWidget *MaskStatusIconBox, *MaskStatusIconBox1;
+ GtkTooltips *Tips;
+
+ if (WritePlaylistWindow != NULL)
+ {
+ gdk_window_raise(WritePlaylistWindow->window);
+ return;
+ }
+
+ Tips = gtk_tooltips_new();
+
+ WritePlaylistWindow = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title(GTK_WINDOW(WritePlaylistWindow),_("Generate a playlist"));
+ gtk_window_set_transient_for(GTK_WINDOW(WritePlaylistWindow),GTK_WINDOW(MainWindow));
+ g_signal_connect(G_OBJECT(WritePlaylistWindow),"destroy", G_CALLBACK(Destroy_Write_Playlist_Window),NULL);
+ g_signal_connect(G_OBJECT(WritePlaylistWindow),"delete_event", G_CALLBACK(Destroy_Write_Playlist_Window),NULL);
+ g_signal_connect(G_OBJECT(WritePlaylistWindow),"key_press_event", G_CALLBACK(Write_Playlist_Window_Key_Press),NULL);
+
+ // Just center on mainwindow
+ gtk_window_set_position(GTK_WINDOW(WritePlaylistWindow), GTK_WIN_POS_CENTER_ON_PARENT);
+ gtk_window_set_default_size(GTK_WINDOW(WritePlaylistWindow),PLAYLIST_WINDOW_WIDTH,PLAYLIST_WINDOW_HEIGHT);
+
+ Frame = gtk_frame_new(NULL);
+ gtk_container_add(GTK_CONTAINER(WritePlaylistWindow),Frame);
+ gtk_container_set_border_width(GTK_CONTAINER(Frame),4);
+
+ VBox = gtk_vbox_new(FALSE,2);
+ gtk_container_add(GTK_CONTAINER(Frame),VBox);
+ gtk_container_set_border_width(GTK_CONTAINER(VBox), 4);
+
+ /* Playlist name */
+ if (!PlayListNameMaskModel)
+ PlayListNameMaskModel = gtk_list_store_new(MISC_COMBO_COUNT, G_TYPE_STRING);
+ else
+ gtk_list_store_clear(PlayListNameMaskModel);
+
+ Frame = gtk_frame_new(_("M3U Playlist Name"));
+ gtk_box_pack_start(GTK_BOX(VBox),Frame,TRUE,TRUE,0);
+ vbox = gtk_vbox_new(FALSE,0);
+ gtk_container_add(GTK_CONTAINER(Frame),vbox);
+ gtk_container_set_border_width(GTK_CONTAINER(vbox), 4);
+
+ playlist_use_mask_name = gtk_radio_button_new_with_label(NULL, _("Use mask :"));
+ hbox = gtk_hbox_new(FALSE,0);
+ gtk_box_pack_start(GTK_BOX(vbox),hbox,FALSE,FALSE,0);
+ gtk_box_pack_start(GTK_BOX(hbox),playlist_use_mask_name,FALSE,FALSE,0);
+ PlayListNameMaskCombo = gtk_combo_box_entry_new_with_model(GTK_TREE_MODEL(PlayListNameMaskModel), MISC_COMBO_TEXT);
+ gtk_box_pack_start(GTK_BOX(hbox),PlayListNameMaskCombo,FALSE,FALSE,4);
+ playlist_use_dir_name = gtk_radio_button_new_with_label_from_widget(
+ GTK_RADIO_BUTTON(playlist_use_mask_name),_("Use directory name"));
+ gtk_box_pack_start(GTK_BOX(vbox),playlist_use_dir_name,FALSE,FALSE,0);
+ // History list
+ Load_Play_List_Name_List(PlayListNameMaskModel, MISC_COMBO_TEXT);
+ Add_String_To_Combo_List(PlayListNameMaskModel, PLAYLIST_NAME);
+ gtk_entry_set_text(GTK_ENTRY(GTK_BIN(PlayListNameMaskCombo)->child), PLAYLIST_NAME);
+
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(playlist_use_mask_name),PLAYLIST_USE_MASK_NAME);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(playlist_use_dir_name),PLAYLIST_USE_DIR_NAME);
+
+ // Mask status icon
+ MaskStatusIconBox = Create_Pixmap_Icon_With_Event_Box("easytag-forbidden");
+ gtk_box_pack_start(GTK_BOX(hbox),MaskStatusIconBox,FALSE,FALSE,0);
+ gtk_tooltips_set_tip(Tips,MaskStatusIconBox,_("Invalid Scanner Mask"),NULL);
+ // Signal connection to check if mask is correct into the mask entry
+ g_signal_connect_swapped(G_OBJECT(GTK_BIN(PlayListNameMaskCombo)->child),"changed",
+ G_CALLBACK(Playlist_Check_Content_Mask),G_OBJECT(MaskStatusIconBox));
+
+ // Button for Mask editor
+ Button = gtk_button_new();
+ Icon = gtk_image_new_from_stock("easytag-mask", GTK_ICON_SIZE_BUTTON);
+ gtk_container_add(GTK_CONTAINER(Button),Icon);
+ gtk_box_pack_start(GTK_BOX(hbox),Button,FALSE,FALSE,0);
+ gtk_button_set_relief(GTK_BUTTON(Button),GTK_RELIEF_NONE);
+ gtk_tooltips_set_tip(Tips,Button,_("Edit Masks"),NULL);
+ // The masks will be edited into a tab of the preferences window. In the future...
+ //g_signal_connect(G_OBJECT(Button),"clicked",(GtkSignalFunc)???,NULL);
+ // FIX ME : edit the masks
+ gtk_widget_set_sensitive(GTK_WIDGET(Button),FALSE);
+
+
+ /* Playlist options */
+ Frame = gtk_frame_new(_("Playlist Options"));
+ gtk_box_pack_start(GTK_BOX(VBox),Frame,TRUE,TRUE,0);
+ vbox = gtk_vbox_new(FALSE,0);
+ gtk_container_add(GTK_CONTAINER(Frame),vbox);
+ gtk_container_set_border_width(GTK_CONTAINER(vbox), 4);
+
+ playlist_only_selected_files = gtk_check_button_new_with_label(_("Include only the selected files"));
+ gtk_box_pack_start(GTK_BOX(vbox),playlist_only_selected_files,FALSE,FALSE,0);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(playlist_only_selected_files),PLAYLIST_ONLY_SELECTED_FILES);
+ gtk_tooltips_set_tip(Tips,playlist_only_selected_files,_("If activated, only the selected files will be "
+ "written in the playlist file. Else, all the files will be written."),NULL);
+
+ // Separator line
+ Separator = gtk_hseparator_new();
+ gtk_box_pack_start(GTK_BOX(vbox),Separator,FALSE,FALSE,0);
+
+ playlist_full_path = gtk_radio_button_new_with_label(NULL,_("Use full path for files in playlist"));
+ gtk_box_pack_start(GTK_BOX(vbox),playlist_full_path,FALSE,FALSE,0);
+ playlist_relative_path = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(playlist_full_path),
+ _("Use relative path for files in playlist"));
+ gtk_box_pack_start(GTK_BOX(vbox),playlist_relative_path,FALSE,FALSE,0);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(playlist_full_path),PLAYLIST_FULL_PATH);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(playlist_relative_path),PLAYLIST_RELATIVE_PATH);
+
+ // Separator line
+ Separator = gtk_hseparator_new();
+ gtk_box_pack_start(GTK_BOX(vbox),Separator,FALSE,FALSE,0);
+
+ // Create playlist in parent directory
+ playlist_create_in_parent_dir = gtk_check_button_new_with_label(_("Create playlist in the parent directory"));
+ gtk_box_pack_start(GTK_BOX(vbox),playlist_create_in_parent_dir,FALSE,FALSE,0);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(playlist_create_in_parent_dir),PLAYLIST_CREATE_IN_PARENT_DIR);
+ gtk_tooltips_set_tip(Tips,playlist_create_in_parent_dir,_("If activated, the playlist will be created "
+ "in the parent directory."),NULL);
+
+ // DOS Separator
+ playlist_use_dos_separator = gtk_check_button_new_with_label(_("Use DOS directory separator"));
+#ifndef WIN32 // This has no sense under Win32, so we don't display it
+ gtk_box_pack_start(GTK_BOX(vbox),playlist_use_dos_separator,FALSE,FALSE,0);
+#endif
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(playlist_use_dos_separator),PLAYLIST_USE_DOS_SEPARATOR);
+ gtk_tooltips_set_tip(Tips,playlist_use_dos_separator,_("This option replaces the UNIX directory "
+ "separator '/' into DOS separator '\\'."),NULL);
+
+ /* Playlist content */
+ if (!PlayListContentMaskModel)
+ PlayListContentMaskModel = gtk_list_store_new(MISC_COMBO_COUNT, G_TYPE_STRING);
+ else
+ gtk_list_store_clear(PlayListContentMaskModel);
+
+ Frame = gtk_frame_new(_("Playlist Content"));
+ gtk_box_pack_start(GTK_BOX(VBox),Frame,TRUE,TRUE,0);
+ vbox = gtk_vbox_new(FALSE,0);
+ gtk_container_add(GTK_CONTAINER(Frame),vbox);
+ gtk_container_set_border_width(GTK_CONTAINER(vbox), 4);
+
+ playlist_content_none = gtk_radio_button_new_with_label(NULL,_("Write only list of files"));
+ gtk_box_pack_start(GTK_BOX(vbox),playlist_content_none,FALSE,FALSE,0);
+
+ playlist_content_filename = gtk_radio_button_new_with_label_from_widget(
+ GTK_RADIO_BUTTON(playlist_content_none),_("Write info using filename"));
+ gtk_box_pack_start(GTK_BOX(vbox),playlist_content_filename,FALSE,FALSE,0);
+
+ playlist_content_mask = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(playlist_content_none), _("Write info using :"));
+ hbox = gtk_hbox_new(FALSE,0);
+ gtk_box_pack_start(GTK_BOX(vbox),hbox,FALSE,FALSE,0);
+ gtk_box_pack_start(GTK_BOX(hbox),playlist_content_mask,FALSE,FALSE,0);
+ // Set a label, a combobox and un editor button in the 3rd radio button
+ PlayListContentMaskCombo = gtk_combo_box_entry_new_with_model(GTK_TREE_MODEL(PlayListContentMaskModel), MISC_COMBO_TEXT);
+ gtk_box_pack_start(GTK_BOX(hbox),PlayListContentMaskCombo,FALSE,FALSE,0);
+ // History list
+ Load_Playlist_Content_Mask_List(PlayListContentMaskModel, MISC_COMBO_TEXT);
+ Add_String_To_Combo_List(PlayListContentMaskModel, PLAYLIST_CONTENT_MASK_VALUE);
+ gtk_entry_set_text(GTK_ENTRY(GTK_BIN(PlayListContentMaskCombo)->child), PLAYLIST_CONTENT_MASK_VALUE);
+
+ // Mask status icon
+ MaskStatusIconBox1 = Create_Pixmap_Icon_With_Event_Box("easytag-forbidden");
+ gtk_box_pack_start(GTK_BOX(hbox),MaskStatusIconBox1,FALSE,FALSE,0);
+ gtk_tooltips_set_tip(Tips,MaskStatusIconBox1,_("Invalid Scanner Mask"),NULL);
+ // Signal connection to check if mask is correct into the mask entry
+ g_signal_connect_swapped(G_OBJECT(GTK_BIN(PlayListContentMaskCombo)->child),"changed",
+ G_CALLBACK(Playlist_Check_Content_Mask),G_OBJECT(MaskStatusIconBox1));
+
+ // Button for Mask editor
+ Button = gtk_button_new();
+ Icon = gtk_image_new_from_stock("easytag-mask", GTK_ICON_SIZE_BUTTON);
+ gtk_container_add(GTK_CONTAINER(Button),Icon);
+ gtk_box_pack_start(GTK_BOX(hbox),Button,FALSE,FALSE,0);
+ gtk_button_set_relief(GTK_BUTTON(Button),GTK_RELIEF_NONE);
+ gtk_tooltips_set_tip(Tips,Button,_("Edit Masks"),NULL);
+ // The masks will be edited into a tab of the preferences window. In the future...
+ //g_signal_connect(G_OBJECT(Button),"clicked",(GtkSignalFunc)???,NULL);
+ // FIX ME : edit the masks
+ gtk_widget_set_sensitive(GTK_WIDGET(Button),FALSE);
+
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(playlist_content_none), PLAYLIST_CONTENT_NONE);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(playlist_content_filename),PLAYLIST_CONTENT_FILENAME);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(playlist_content_mask), PLAYLIST_CONTENT_MASK);
+
+
+ /* Separator line */
+ Separator = gtk_hseparator_new();
+ gtk_box_pack_start(GTK_BOX(VBox),Separator,FALSE,FALSE,0);
+
+ ButtonBox = gtk_hbutton_box_new ();
+ gtk_box_pack_start(GTK_BOX(VBox),ButtonBox,FALSE,FALSE,0);
+ gtk_button_box_set_layout(GTK_BUTTON_BOX(ButtonBox),GTK_BUTTONBOX_END);
+ gtk_box_set_spacing(GTK_BOX(ButtonBox), 10);
+
+ /* Button to Cancel */
+ Button = Create_Button_With_Pixmap(BUTTON_CLOSE);
+ gtk_container_add(GTK_CONTAINER(ButtonBox),Button);
+ GTK_WIDGET_SET_FLAGS(Button, GTK_CAN_DEFAULT);
+ gtk_widget_grab_default(Button);
+ g_signal_connect(G_OBJECT(Button),"clicked", G_CALLBACK(Destroy_Write_Playlist_Window),NULL);
+
+ /* Button to Write the playlist */
+ Button = Create_Button_With_Pixmap(BUTTON_WRITE);
+ gtk_container_add(GTK_CONTAINER(ButtonBox),Button);
+ GTK_WIDGET_SET_FLAGS(Button,GTK_CAN_DEFAULT);
+ g_signal_connect_swapped(G_OBJECT(Button),"clicked",G_CALLBACK(Playlist_Write_Button_Pressed),NULL);
+
+ gtk_widget_show_all(WritePlaylistWindow);
+ if (PLAYLIST_WINDOW_X > 0 && PLAYLIST_WINDOW_Y > 0)
+ gdk_window_move(WritePlaylistWindow->window,PLAYLIST_WINDOW_X,PLAYLIST_WINDOW_Y);
+
+ /* To initialize the mask status icon and visibility */
+ g_signal_emit_by_name(G_OBJECT(GTK_BIN(PlayListNameMaskCombo)->child),"changed");
+ g_signal_emit_by_name(G_OBJECT(GTK_BIN(PlayListContentMaskCombo)->child),"changed");
+}
+
+void Destroy_Write_Playlist_Window (void)
+{
+ if (WritePlaylistWindow)
+ {
+ /* Save combobox history lists before exit */
+ Save_Play_List_Name_List(PlayListNameMaskModel, MISC_COMBO_TEXT);
+ Save_Playlist_Content_Mask_List(PlayListContentMaskModel, MISC_COMBO_TEXT);
+
+ Write_Playlist_Window_Apply_Changes();
+
+ gtk_widget_destroy(WritePlaylistWindow);
+ WritePlaylistWindow = (GtkWidget *)NULL;
+ }
+}
+
+/*
+ * For the configuration file...
+ */
+void Write_Playlist_Window_Apply_Changes (void)
+{
+ if (WritePlaylistWindow)
+ {
+ gint x, y, width, height;
+
+ if ( WritePlaylistWindow->window && gdk_window_is_visible(WritePlaylistWindow->window)
+ && gdk_window_get_state(WritePlaylistWindow->window)!=GDK_WINDOW_STATE_MAXIMIZED )
+ {
+ // Position and Origin of the window
+ gdk_window_get_root_origin(WritePlaylistWindow->window,&x,&y);
+ PLAYLIST_WINDOW_X = x;
+ PLAYLIST_WINDOW_Y = y;
+ gdk_window_get_size(WritePlaylistWindow->window,&width,&height);
+ PLAYLIST_WINDOW_WIDTH = width;
+ PLAYLIST_WINDOW_HEIGHT = height;
+ }
+
+ /* List of variables also set in the function 'Playlist_Write_Button_Pressed' */
+ if (PLAYLIST_NAME) g_free(PLAYLIST_NAME);
+ PLAYLIST_NAME = g_strdup(gtk_entry_get_text(GTK_ENTRY(GTK_BIN(PlayListNameMaskCombo)->child)));
+ PLAYLIST_USE_MASK_NAME = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(playlist_use_mask_name));
+ PLAYLIST_USE_DIR_NAME = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(playlist_use_dir_name));
+
+ PLAYLIST_ONLY_SELECTED_FILES = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(playlist_only_selected_files));
+ PLAYLIST_FULL_PATH = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(playlist_full_path));
+ PLAYLIST_RELATIVE_PATH = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(playlist_relative_path));
+ PLAYLIST_CREATE_IN_PARENT_DIR = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(playlist_create_in_parent_dir));
+ PLAYLIST_USE_DOS_SEPARATOR = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(playlist_use_dos_separator));
+
+ PLAYLIST_CONTENT_NONE = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(playlist_content_none));
+ PLAYLIST_CONTENT_FILENAME = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(playlist_content_filename));
+ PLAYLIST_CONTENT_MASK = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(playlist_content_mask));
+ if (PLAYLIST_CONTENT_MASK_VALUE) g_free(PLAYLIST_CONTENT_MASK_VALUE);
+ PLAYLIST_CONTENT_MASK_VALUE = g_strdup(gtk_entry_get_text(GTK_ENTRY(GTK_BIN(PlayListContentMaskCombo)->child)));
+ }
+}
+
+gboolean Write_Playlist_Window_Key_Press (GtkWidget *window, GdkEvent *event)
+{
+ GdkEventKey *kevent;
+
+ if (event && event->type == GDK_KEY_PRESS)
+ {
+ kevent = (GdkEventKey *)event;
+ switch(kevent->keyval)
+ {
+ case GDK_Escape:
+ Destroy_Write_Playlist_Window();
+ break;
+ }
+ }
+ return FALSE;
+}
+
+void Playlist_Write_Button_Pressed (void)
+{
+ gchar *playlist_name = NULL;
+ gchar *playlist_path_utf8; // Path
+ gchar *playlist_basename_utf8; // Filename
+ gchar *playlist_name_utf8; // Path + filename
+ gchar *temp;
+ FILE *file;
+ gchar *msg;
+ GtkWidget *msgbox;
+ gint msgbox_button = 0;
+
+
+ // Check if playlist name was filled
+ if ( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(playlist_use_mask_name))
+ && g_utf8_strlen(gtk_entry_get_text(GTK_ENTRY(GTK_BIN(PlayListNameMaskCombo)->child)), -1)<=0 )
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(playlist_use_dir_name),TRUE);
+
+ /* List of variables also set in the function 'Write_Playlist_Window_Apply_Changes' */
+ /***if (PLAYLIST_NAME) g_free(PLAYLIST_NAME);
+ PLAYLIST_NAME = g_strdup(gtk_entry_get_text(GTK_ENTRY(GTK_BIN(PlayListNameMaskCombo)->child)));
+ PLAYLIST_USE_MASK_NAME = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(playlist_use_mask_name));
+ PLAYLIST_USE_DIR_NAME = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(playlist_use_dir_name));
+
+ PLAYLIST_ONLY_SELECTED_FILES = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(playlist_only_selected_files));
+ PLAYLIST_FULL_PATH = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(playlist_full_path));
+ PLAYLIST_RELATIVE_PATH = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(playlist_relative_path));
+ PLAYLIST_CREATE_IN_PARENT_DIR = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(playlist_create_in_parent_dir));
+ PLAYLIST_USE_DOS_SEPARATOR = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(playlist_use_dos_separator));
+
+ PLAYLIST_CONTENT_NONE = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(playlist_content_none));
+ PLAYLIST_CONTENT_FILENAME = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(playlist_content_filename));
+ PLAYLIST_CONTENT_MASK = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(playlist_content_mask));
+ if (PLAYLIST_CONTENT_MASK_VALUE) g_free(PLAYLIST_CONTENT_MASK_VALUE);
+ PLAYLIST_CONTENT_MASK_VALUE = g_strdup(gtk_entry_get_text(GTK_ENTRY(GTK_BIN(PlayListContentMaskCombo)->child)));***/
+ Write_Playlist_Window_Apply_Changes();
+
+ // Path of the playlist file (may be truncated later if PLAYLIST_CREATE_IN_PARENT_DIR is TRUE)
+ playlist_path_utf8 = filename_to_display(Browser_Get_Current_Path());
+
+ // Build the playlist file name
+ if (PLAYLIST_USE_MASK_NAME)
+ {
+
+ if (!ETCore->ETFileList)
+ return;
+
+ Add_String_To_Combo_List(PlayListNameMaskModel, PLAYLIST_NAME);
+
+ // Generate filename from tag of the current selected file (hummm FIX ME)
+ temp = filename_from_display(PLAYLIST_NAME);
+ playlist_basename_utf8 = Scan_Generate_New_Filename_From_Mask(ETCore->ETFileDisplayed,temp,FALSE);
+ g_free(temp);
+
+ // Replace Characters (with scanner)
+ if (RFS_CONVERT_UNDERSCORE_AND_P20_INTO_SPACE)
+ {
+ Scan_Convert_Underscore_Into_Space(playlist_basename_utf8);
+ Scan_Convert_P20_Into_Space(playlist_basename_utf8);
+ }
+ if (RFS_CONVERT_SPACE_INTO_UNDERSCORE)
+ {
+ Scan_Convert_Space_Into_Undescore(playlist_basename_utf8);
+ }
+
+ }else // PLAYLIST_USE_DIR_NAME
+ {
+
+ if ( strcmp(playlist_path_utf8,G_DIR_SEPARATOR_S)==0 )
+ {
+ playlist_basename_utf8 = g_strdup("playlist");
+ }else
+ {
+ gchar *tmp_string = g_strdup(playlist_path_utf8);
+ // Remove last '/'
+ if (tmp_string[strlen(tmp_string)-1]==G_DIR_SEPARATOR)
+ tmp_string[strlen(tmp_string)-1] = '\0';
+ // Get directory name
+ temp = g_path_get_basename(tmp_string);
+ playlist_basename_utf8 = g_strdup(temp);
+ g_free(tmp_string);
+ g_free(temp);
+ }
+
+ }
+
+ // Must be placed after "Build the playlist file name", as we can truncate the path!
+ if (PLAYLIST_CREATE_IN_PARENT_DIR)
+ {
+ if ( (strcmp(playlist_path_utf8,G_DIR_SEPARATOR_S) != 0) )
+ {
+ gchar *tmp;
+ // Remove last '/'
+ if (playlist_path_utf8[strlen(playlist_path_utf8)-1]==G_DIR_SEPARATOR)
+ playlist_path_utf8[strlen(playlist_path_utf8)-1] = '\0';
+ // Get parent directory
+ if ( (tmp=strrchr(playlist_path_utf8,G_DIR_SEPARATOR)) != NULL )
+ *(tmp + 1) = '\0';
+ }
+ }
+
+ // Generate path + filename of playlist
+ if (playlist_path_utf8[strlen(playlist_path_utf8)-1]==G_DIR_SEPARATOR)
+ playlist_name_utf8 = g_strconcat(playlist_path_utf8,playlist_basename_utf8,".m3u",NULL);
+ else
+ playlist_name_utf8 = g_strconcat(playlist_path_utf8,G_DIR_SEPARATOR_S,playlist_basename_utf8,".m3u",NULL);
+
+ g_free(playlist_path_utf8);
+ g_free(playlist_basename_utf8);
+
+ playlist_name = filename_from_display(playlist_name_utf8);
+
+ // Check if file exists
+ if (CONFIRM_WRITE_PLAYLIST)
+ {
+ if ( (file=fopen(playlist_name,"r")) != NULL )
+ {
+ fclose(file);
+ msg = g_strdup_printf(_("Playlist file '%s' already exists!\nOverwrite?"),playlist_name_utf8);
+ msgbox = msg_box_new(_("Write Playlist..."),msg,GTK_STOCK_DIALOG_QUESTION,BUTTON_NO,BUTTON_YES,0);
+ msg_box_hide_check_button(MSG_BOX(msgbox));
+ msgbox_button = msg_box_run(MSG_BOX(msgbox));
+ gtk_widget_destroy(msgbox);
+ g_free(msg);
+ }
+ }
+
+ // Writing playlist if ok
+ if (msgbox_button==0 || msgbox_button==BUTTON_YES )
+ {
+ if ( Write_Playlist(playlist_name) == FALSE )
+ {
+ // Writing fails...
+ msg = g_strdup_printf(_("Can't write playlist file '%s'!\n(%s)"),playlist_name_utf8,g_strerror(errno));
+ msgbox = msg_box_new(_("Error..."),msg,GTK_STOCK_DIALOG_ERROR,BUTTON_OK,0);
+ msg_box_hide_check_button(MSG_BOX(msgbox));
+ msg_box_run(MSG_BOX(msgbox));
+ gtk_widget_destroy(msgbox);
+ }else
+ {
+ msg = g_strdup_printf(_("Written playlist file '%s'"),playlist_name_utf8);
+ //msgbox = msg_box_new(_("Information..."),msg,GTK_STOCK_DIALOG_INFO,BUTTON_OK,0);
+ Statusbar_Message(msg,TRUE);
+ }
+ g_free(msg);
+ }
+ g_free(playlist_name_utf8);
+ g_free(playlist_name);
+}
+
+gboolean Playlist_Check_Content_Mask (GtkObject *widget_to_show_hide, GtkEntry *widget_source)
+{
+ gchar *tmp = NULL;
+ gchar *mask = NULL;
+
+
+ if (!widget_to_show_hide || !widget_source)
+ goto Bad_Mask;
+
+ mask = g_strdup(gtk_entry_get_text(GTK_ENTRY(widget_source)));
+ if (!mask || strlen(mask)<1)
+ goto Bad_Mask;
+
+ while (mask)
+ {
+ if ( (tmp=strrchr(mask,'%'))==NULL )
+ {
+ /* There is no more code. */
+ /* No code in mask is accepted. */
+ goto Good_Mask;
+ }
+ if (strlen(tmp)>1 && (tmp[1]=='t' || tmp[1]=='a' || tmp[1]=='b' || tmp[1]=='y' ||
+ tmp[1]=='g' || tmp[1]=='n' || tmp[1]=='l' || tmp[1]=='c' || tmp[1]=='i'))
+ {
+ /* The code is valid. */
+ /* No separator is accepted. */
+ *(mask+strlen(mask)-strlen(tmp)) = '\0';
+ }else
+ {
+ goto Bad_Mask;
+ }
+ }
+
+ Bad_Mask:
+ g_free(mask);
+ gtk_widget_show(GTK_WIDGET(widget_to_show_hide));
+ return FALSE;
+
+ Good_Mask:
+ g_free(mask);
+ gtk_widget_hide(GTK_WIDGET(widget_to_show_hide));
+ return TRUE;
+}
+
+/*
+ * Function to replace UNIX ForwardSlash with a DOS BackSlash
+ */
+void Playlist_Convert_Forwardslash_Into_Backslash (gchar *string)
+{
+ gchar *tmp;
+
+ while ((tmp=strchr(string,'/'))!=NULL)
+ *tmp = '\\';
+}
+
+
+/*
+ * Write a playlist
+ * - 'playlist_name' in file system encoding (not UTF-8)
+ */
+gboolean Write_Playlist (gchar *playlist_name)
+{
+ FILE *file;
+ ET_File *etfile;
+ GList *etfilelist = NULL;
+ gchar *filename;
+ gchar *playlist_name_utf8 = filename_to_display(playlist_name);
+ gchar *basedir;
+ gchar *temp;
+ gint duration;
+
+ if ((file = fopen(playlist_name,"wb")) == NULL)
+ {
+ Log_Print(_("ERROR while opening file: '%s' (%s)."),playlist_name_utf8,g_strerror(errno));
+ g_free(playlist_name_utf8);
+ return FALSE;
+ }
+
+ /* 'base directory' where is located the playlist. Used also to write file with a
+ * relative path for file located in this directory and sub-directories
+ */
+ basedir = g_path_get_dirname(playlist_name);
+
+ // 1) First line of the file (if playlist content is not set to "write only list of files")
+ if (!PLAYLIST_CONTENT_NONE)
+ {
+ fprintf(file,"#EXTM3U\r\n");
+ }
+
+ if (PLAYLIST_ONLY_SELECTED_FILES)
+ {
+ GList *selfilelist = NULL;
+ GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(BrowserList));
+
+ selfilelist = gtk_tree_selection_get_selected_rows(selection, NULL);
+ while (selfilelist)
+ {
+ etfile = Browser_List_Get_ETFile_From_Path(selfilelist->data);
+ etfilelist = g_list_append(etfilelist, etfile);
+
+ if (!selfilelist->next) break;
+ selfilelist = selfilelist->next;
+ }
+
+ g_list_foreach(selfilelist, (GFunc) gtk_tree_path_free, NULL);
+ g_list_free(selfilelist);
+ }else
+ {
+ etfilelist = g_list_first(ETCore->ETFileList);
+ }
+ while (etfilelist)
+ {
+ etfile = (ET_File *)etfilelist->data;
+ filename = ((File_Name *)etfile->FileNameCur->data)->value;
+ duration = ((ET_File_Info *)etfile->ETFileInfo)->duration;
+
+ if (PLAYLIST_RELATIVE_PATH)
+ {
+ // Keep only files in this directory and sub-dirs
+ if ( strncmp(filename,basedir,strlen(basedir))==0 )
+ {
+ // 2) Write the header
+ if (PLAYLIST_CONTENT_NONE)
+ {
+ // No header written
+ }else if (PLAYLIST_CONTENT_FILENAME)
+ {
+ // Header uses only filename
+ temp = g_path_get_basename(filename);
+ fprintf(file,"#EXTINF:%d,%s\r\n",duration,temp); // Must be written in system encoding (not UTF-8)
+ g_free(temp);
+ }else if (PLAYLIST_CONTENT_MASK)
+ {
+ // Header uses generated filename from a mask
+ gchar *mask = filename_from_display(gtk_entry_get_text(GTK_ENTRY(GTK_BIN(PlayListContentMaskCombo)->child)));
+ // Special case : we don't replace illegal characters and don't check if there is a directory separator in the mask.
+ gchar *filename_generated_utf8 = Scan_Generate_New_Filename_From_Mask(etfile,mask,TRUE);
+ gchar *filename_generated = filename_from_display(filename_generated_utf8);
+ fprintf(file,"#EXTINF:%d,%s\r\n",duration,filename_generated); // Must be written in system encoding (not UTF-8)
+ g_free(mask);
+ g_free(filename_generated_utf8);
+ }
+
+ // 3) Write the file path
+ if (PLAYLIST_USE_DOS_SEPARATOR)
+ {
+ gchar *filename_conv = g_strdup(filename+strlen(basedir)+1);
+ Playlist_Convert_Forwardslash_Into_Backslash(filename_conv);
+ fprintf(file,"%s\r\n",filename_conv); // Must be written in system encoding (not UTF-8)
+ g_free(filename_conv);
+ }else
+ {
+ fprintf(file,"%s\r\n",filename+strlen(basedir)+1); // Must be written in system encoding (not UTF-8)
+ }
+ }
+ }else // PLAYLIST_FULL_PATH
+ {
+ // 2) Write the header
+ if (PLAYLIST_CONTENT_NONE)
+ {
+ // No header written
+ }else if (PLAYLIST_CONTENT_FILENAME)
+ {
+ // Header uses only filename
+ temp = g_path_get_basename(filename);
+ fprintf(file,"#EXTINF:%d,%s\r\n",duration,temp); // Must be written in system encoding (not UTF-8)
+ g_free(temp);
+ }else if (PLAYLIST_CONTENT_MASK)
+ {
+ // Header uses generated filename from a mask
+ gchar *mask = filename_from_display(gtk_entry_get_text(GTK_ENTRY(GTK_BIN(PlayListContentMaskCombo)->child)));
+ gchar *filename_generated_utf8 = Scan_Generate_New_Filename_From_Mask(etfile,mask,TRUE);
+ gchar *filename_generated = filename_from_display(filename_generated_utf8);
+ fprintf(file,"#EXTINF:%d,%s\r\n",duration,filename_generated); // Must be written in system encoding (not UTF-8)
+ g_free(mask);
+ g_free(filename_generated_utf8);
+ }
+
+ // 3) Write the file path
+ if (PLAYLIST_USE_DOS_SEPARATOR)
+ {
+ gchar *filename_conv = g_strdup(filename);
+ Playlist_Convert_Forwardslash_Into_Backslash(filename_conv);
+ fprintf(file,"%s\r\n",filename_conv); // Must be written in system encoding (not UTF-8)
+ g_free(filename_conv);
+ }else
+ {
+ fprintf(file,"%s\r\n",filename); // Must be written in system encoding (not UTF-8)
+ }
+ }
+ etfilelist = etfilelist->next;
+ }
+ fclose(file);
+
+ if (PLAYLIST_ONLY_SELECTED_FILES)
+ g_list_free(etfilelist);
+ g_free(playlist_name_utf8);
+ g_free(basedir);
+ return TRUE;
+}
+
+
+
+
+/*****************************
+ * Searching files functions *
+ *****************************/
+/*
+ * The window to search keywords in the list of files.
+ */
+void Open_Search_File_Window (void)
+{
+ GtkWidget *VBox;
+ GtkWidget *Frame;
+ GtkWidget *Table;
+ GtkWidget *Label;
+ GtkWidget *Button;
+ GtkWidget *Separator;
+ GtkTooltips *Tips;
+ GtkWidget *ScrollWindow;
+ GtkTreeViewColumn* column;
+ GtkCellRenderer* renderer;
+ gchar *SearchResultList_Titles[] = { N_("File Name"),
+ N_("Title"),
+ N_("Artist"),
+ N_("Album"),
+ N_("CD"),
+ N_("Year"),
+ N_("Track"),
+ N_("Genre"),
+ N_("Comment"),
+ N_("Composer"),
+ N_("Orig. Artist"),
+ N_("Copyright"),
+ N_("URL"),
+ N_("Encoded by")
+ };
+
+
+ if (SearchFileWindow != NULL)
+ {
+ gdk_window_raise(SearchFileWindow->window);
+ return;
+ }
+
+ SearchFileWindow = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title(GTK_WINDOW(SearchFileWindow),_("Search a file"));
+ g_signal_connect(G_OBJECT(SearchFileWindow),"destroy", G_CALLBACK(Destroy_Search_File_Window),NULL);
+ g_signal_connect(G_OBJECT(SearchFileWindow),"delete_event", G_CALLBACK(Destroy_Search_File_Window),NULL);
+ g_signal_connect(G_OBJECT(SearchFileWindow),"key_press_event", G_CALLBACK(Search_File_Window_Key_Press),NULL);
+ gtk_window_set_default_size(GTK_WINDOW(SearchFileWindow),SEARCH_WINDOW_WIDTH,SEARCH_WINDOW_HEIGHT);
+
+ // The tooltips
+ Tips = gtk_tooltips_new();
+
+ VBox = gtk_vbox_new(FALSE,0);
+ gtk_container_add(GTK_CONTAINER(SearchFileWindow),VBox);
+ gtk_container_set_border_width(GTK_CONTAINER(VBox), 1);
+
+ Frame = gtk_frame_new(NULL);
+ //gtk_container_add(GTK_CONTAINER(SearchFileWindow),Frame);
+ gtk_box_pack_start(GTK_BOX(VBox),Frame,TRUE,TRUE,0);
+ gtk_container_set_border_width(GTK_CONTAINER(Frame),2);
+
+ Table = gtk_table_new(3,6,FALSE);
+ gtk_container_add(GTK_CONTAINER(Frame),Table);
+ gtk_container_set_border_width(GTK_CONTAINER(Table), 2);
+ gtk_table_set_row_spacings(GTK_TABLE(Table),4);
+ gtk_table_set_col_spacings(GTK_TABLE(Table),4);
+
+ // Words to search
+ if (!SearchStringModel)
+ SearchStringModel = gtk_list_store_new(MISC_COMBO_COUNT, G_TYPE_STRING);
+ else
+ gtk_list_store_clear(SearchStringModel);
+
+ Label = gtk_label_new(_("Search :"));
+ gtk_misc_set_alignment(GTK_MISC(Label),1.0,0.5);
+ gtk_table_attach(GTK_TABLE(Table),Label,0,1,0,1,GTK_FILL,GTK_FILL,0,0);
+ SearchStringCombo = gtk_combo_box_entry_new_with_model(GTK_TREE_MODEL(SearchStringModel), MISC_COMBO_TEXT);
+ gtk_widget_set_size_request(GTK_WIDGET(SearchStringCombo),200,-1);
+ gtk_table_attach(GTK_TABLE(Table),SearchStringCombo,1,5,0,1,GTK_EXPAND|GTK_FILL,GTK_FILL,0,0);
+ // History List
+ Load_Search_File_List(SearchStringModel, MISC_COMBO_TEXT);
+ gtk_entry_set_text(GTK_ENTRY(GTK_BIN(SearchStringCombo)->child),"");
+ gtk_tooltips_set_tip(Tips,GTK_WIDGET(GTK_ENTRY(GTK_BIN(SearchStringCombo)->child)),
+ _("Type the word to search into files. Or type nothing to display all files."),NULL);
+
+ // Set content of the clipboard if available
+ gtk_editable_paste_clipboard(GTK_EDITABLE(GTK_BIN(SearchStringCombo)->child));
+
+ // Where...
+ Label = gtk_label_new(_("In :"));
+ gtk_misc_set_alignment(GTK_MISC(Label),1.0,0.5);
+ gtk_table_attach(GTK_TABLE(Table),Label,0,1,1,2,GTK_FILL,GTK_FILL,0,0);
+ SearchInFilename = gtk_check_button_new_with_label(_("the File Name"));
+ // Note : label changed to "the Tag" (to be the only one) to fix a Hungarian grammatical problem (which uses one word to say "in the tag" like here)
+ SearchInTag = gtk_check_button_new_with_label(_("the Tag"));
+ gtk_table_attach(GTK_TABLE(Table),SearchInFilename,1,2,1,2,GTK_FILL,GTK_FILL,0,0);
+ gtk_table_attach(GTK_TABLE(Table),SearchInTag,2,3,1,2,GTK_FILL,GTK_FILL,0,0);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(SearchInFilename),SEARCH_IN_FILENAME);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(SearchInTag),SEARCH_IN_TAG);
+
+ Separator = gtk_vseparator_new();
+ gtk_table_attach(GTK_TABLE(Table),Separator,3,4,1,2,GTK_FILL,GTK_FILL,4,0);
+
+ // Property of the search
+ SearchCaseSensitive = gtk_check_button_new_with_label(_("Case sensitive"));
+ gtk_table_attach(GTK_TABLE(Table),SearchCaseSensitive,4,5,1,2,GTK_EXPAND|GTK_FILL,GTK_FILL,0,0);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(SearchCaseSensitive),SEARCH_CASE_SENSITIVE);
+
+ // Results list
+ ScrollWindow = gtk_scrolled_window_new(NULL,NULL);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ScrollWindow),GTK_POLICY_AUTOMATIC,GTK_POLICY_AUTOMATIC);
+ gtk_widget_set_size_request(GTK_WIDGET(ScrollWindow), -1, 130);
+ gtk_table_attach(GTK_TABLE(Table),ScrollWindow,0,6,2,3,GTK_EXPAND|GTK_FILL,GTK_EXPAND|GTK_FILL,0,0);
+
+ SearchResultListModel = gtk_list_store_new(SEARCH_COLUMN_COUNT,
+ G_TYPE_STRING, /* Filename */
+ G_TYPE_STRING, /* Title */
+ G_TYPE_STRING, /* Artist */
+ G_TYPE_STRING, /* Album */
+ G_TYPE_STRING, /* Disc Number */
+ G_TYPE_STRING, /* Year */
+ G_TYPE_STRING, /* Track + Track Total */
+ G_TYPE_STRING, /* Genre */
+ G_TYPE_STRING, /* Comment */
+ G_TYPE_STRING, /* Composer */
+ G_TYPE_STRING, /* Orig. Artist */
+ G_TYPE_STRING, /* Copyright */
+ G_TYPE_STRING, /* URL */
+ G_TYPE_STRING, /* Encoded by */
+
+ G_TYPE_INT, /* Font Weight for Filename */
+ G_TYPE_INT, /* Font Weight for Title */
+ G_TYPE_INT, /* Font Weight for Artist */
+ G_TYPE_INT, /* Font Weight for Album */
+ G_TYPE_INT, /* Font Weight for Disc Number */
+ G_TYPE_INT, /* Font Weight for Year */
+ G_TYPE_INT, /* Font Weight for Track + Track Total */
+ G_TYPE_INT, /* Font Weight for Genre */
+ G_TYPE_INT, /* Font Weight for Comment */
+ G_TYPE_INT, /* Font Weight for Composer */
+ G_TYPE_INT, /* Font Weight for Orig. Artist */
+ G_TYPE_INT, /* Font Weight for Copyright */
+ G_TYPE_INT, /* Font Weight for URL */
+ G_TYPE_INT, /* Font Weight for Encoded by */
+
+ GDK_TYPE_COLOR, /* Color Weight for Filename */
+ GDK_TYPE_COLOR, /* Color Weight for Title */
+ GDK_TYPE_COLOR, /* Color Weight for Artist */
+ GDK_TYPE_COLOR, /* Color Weight for Album */
+ GDK_TYPE_COLOR, /* Color Weight for Disc Number */
+ GDK_TYPE_COLOR, /* Color Weight for Year */
+ GDK_TYPE_COLOR, /* Color Weight for Track + Track Total */
+ GDK_TYPE_COLOR, /* Color Weight for Genre */
+ GDK_TYPE_COLOR, /* Color Weight for Comment */
+ GDK_TYPE_COLOR, /* Color Weight for Composer */
+ GDK_TYPE_COLOR, /* Color Weight for Orig. Artist */
+ GDK_TYPE_COLOR, /* Color Weight for Copyright */
+ GDK_TYPE_COLOR, /* Color Weight for URL */
+ GDK_TYPE_COLOR, /* Color Weight for Encoded by */
+
+ G_TYPE_POINTER);
+ SearchResultList = gtk_tree_view_new_with_model(GTK_TREE_MODEL(SearchResultListModel));
+
+ renderer = gtk_cell_renderer_text_new(); /* Filename */
+ column = gtk_tree_view_column_new_with_attributes(_(SearchResultList_Titles[0]), renderer,
+ "text", SEARCH_RESULT_FILENAME,
+ "weight", SEARCH_RESULT_FILENAME_WEIGHT,
+ "foreground-gdk", SEARCH_RESULT_FILENAME_FOREGROUND,
+ NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(SearchResultList), column);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+
+ renderer = gtk_cell_renderer_text_new(); /* Title */
+ column = gtk_tree_view_column_new_with_attributes(_(SearchResultList_Titles[1]), renderer,
+ "text", SEARCH_RESULT_TITLE,
+ "weight", SEARCH_RESULT_TITLE_WEIGHT,
+ "foreground-gdk", SEARCH_RESULT_TITLE_FOREGROUND,
+ NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(SearchResultList), column);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+
+ renderer = gtk_cell_renderer_text_new(); /* Artist */
+ column = gtk_tree_view_column_new_with_attributes(_(SearchResultList_Titles[2]), renderer,
+ "text", SEARCH_RESULT_ARTIST,
+ "weight", SEARCH_RESULT_ARTIST_WEIGHT,
+ "foreground-gdk", SEARCH_RESULT_ARTIST_FOREGROUND,
+ NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(SearchResultList), column);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+
+ renderer = gtk_cell_renderer_text_new(); /* Album */
+ column = gtk_tree_view_column_new_with_attributes(_(SearchResultList_Titles[3]), renderer,
+ "text", SEARCH_RESULT_ALBUM,
+ "weight", SEARCH_RESULT_ALBUM_WEIGHT,
+ "foreground-gdk", SEARCH_RESULT_ALBUM_FOREGROUND,
+ NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(SearchResultList), column);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+
+ renderer = gtk_cell_renderer_text_new(); /* Disc Number */
+ column = gtk_tree_view_column_new_with_attributes(_(SearchResultList_Titles[4]), renderer,
+ "text", SEARCH_RESULT_DISC_NUMBER,
+ "weight", SEARCH_RESULT_DISC_NUMBER_WEIGHT,
+ "foreground-gdk", SEARCH_RESULT_DISC_NUMBER_FOREGROUND,
+ NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(SearchResultList), column);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+
+ renderer = gtk_cell_renderer_text_new(); /* Year */
+ column = gtk_tree_view_column_new_with_attributes(_(SearchResultList_Titles[5]), renderer,
+ "text", SEARCH_RESULT_YEAR,
+ "weight", SEARCH_RESULT_YEAR_WEIGHT,
+ "foreground-gdk", SEARCH_RESULT_YEAR_FOREGROUND,
+ NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(SearchResultList), column);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+
+ renderer = gtk_cell_renderer_text_new(); /* Track */
+ column = gtk_tree_view_column_new_with_attributes(_(SearchResultList_Titles[6]), renderer,
+ "text", SEARCH_RESULT_TRACK,
+ "weight", SEARCH_RESULT_TRACK_WEIGHT,
+ "foreground-gdk", SEARCH_RESULT_TRACK_FOREGROUND,
+ NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(SearchResultList), column);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+
+ renderer = gtk_cell_renderer_text_new(); /* Genre */
+ column = gtk_tree_view_column_new_with_attributes(_(SearchResultList_Titles[7]), renderer,
+ "text", SEARCH_RESULT_GENRE,
+ "weight", SEARCH_RESULT_GENRE_WEIGHT,
+ "foreground-gdk", SEARCH_RESULT_GENRE_FOREGROUND,
+ NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(SearchResultList), column);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+
+ renderer = gtk_cell_renderer_text_new(); /* Comment */
+ column = gtk_tree_view_column_new_with_attributes(_(SearchResultList_Titles[8]), renderer,
+ "text", SEARCH_RESULT_COMMENT,
+ "weight", SEARCH_RESULT_COMMENT_WEIGHT,
+ "foreground-gdk", SEARCH_RESULT_COMMENT_FOREGROUND,
+ NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(SearchResultList), column);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+
+ renderer = gtk_cell_renderer_text_new(); /* Composer */
+ column = gtk_tree_view_column_new_with_attributes(_(SearchResultList_Titles[9]), renderer,
+ "text", SEARCH_RESULT_COMPOSER,
+ "weight", SEARCH_RESULT_COMPOSER_WEIGHT,
+ "foreground-gdk", SEARCH_RESULT_COMPOSER_FOREGROUND,
+ NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(SearchResultList), column);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+
+ renderer = gtk_cell_renderer_text_new(); /* Orig. Artist */
+ column = gtk_tree_view_column_new_with_attributes(_(SearchResultList_Titles[10]), renderer,
+ "text", SEARCH_RESULT_ORIG_ARTIST,
+ "weight", SEARCH_RESULT_ORIG_ARTIST_WEIGHT,
+ "foreground-gdk", SEARCH_RESULT_ORIG_ARTIST_FOREGROUND,
+ NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(SearchResultList), column);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+
+ renderer = gtk_cell_renderer_text_new(); /* Copyright */
+ column = gtk_tree_view_column_new_with_attributes(_(SearchResultList_Titles[11]), renderer,
+ "text", SEARCH_RESULT_COPYRIGHT,
+ "weight", SEARCH_RESULT_COPYRIGHT_WEIGHT,
+ "foreground-gdk", SEARCH_RESULT_COPYRIGHT_FOREGROUND,
+ NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(SearchResultList), column);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+
+ renderer = gtk_cell_renderer_text_new(); /* URL */
+ column = gtk_tree_view_column_new_with_attributes(_(SearchResultList_Titles[12]), renderer,
+ "text", SEARCH_RESULT_URL,
+ "weight", SEARCH_RESULT_URL_WEIGHT,
+ "foreground-gdk", SEARCH_RESULT_URL_FOREGROUND,
+ NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(SearchResultList), column);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+
+ renderer = gtk_cell_renderer_text_new(); /* Encoded by */
+ column = gtk_tree_view_column_new_with_attributes(_(SearchResultList_Titles[13]), renderer,
+ "text", SEARCH_RESULT_ENCODED_BY,
+ "weight", SEARCH_RESULT_ENCODED_BY_WEIGHT,
+ "foreground-gdk", SEARCH_RESULT_ENCODED_BY_FOREGROUND,
+ NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(SearchResultList), column);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+
+ gtk_container_add(GTK_CONTAINER(ScrollWindow),SearchResultList);
+ gtk_tree_selection_set_mode(gtk_tree_view_get_selection(GTK_TREE_VIEW(SearchResultList)),
+ GTK_SELECTION_MULTIPLE);
+ //gtk_tree_view_columns_autosize(GTK_TREE_VIEW(SearchResultList)); // Doesn't seem to work...
+ gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW(SearchResultList), FALSE);
+ gtk_widget_set_sensitive(GTK_WIDGET(SearchResultList), FALSE);
+ g_signal_connect(G_OBJECT(gtk_tree_view_get_selection(GTK_TREE_VIEW(SearchResultList))),
+ "changed", G_CALLBACK(Search_Result_List_Row_Selected), NULL);
+
+ // Button to run the search
+ Button = Create_Button_With_Pixmap(BUTTON_SEARCH);
+ gtk_table_attach(GTK_TABLE(Table),Button,5,6,0,1,GTK_FILL,GTK_FILL,0,0);
+ GTK_WIDGET_SET_FLAGS(Button,GTK_CAN_DEFAULT);
+ gtk_widget_grab_default(Button);
+ g_signal_connect(G_OBJECT(Button),"clicked", G_CALLBACK(Search_File),NULL);
+ g_signal_connect(G_OBJECT(GTK_BIN(SearchStringCombo)->child),"activate", G_CALLBACK(Search_File),NULL);
+
+ // Button to cancel
+ Button = Create_Button_With_Pixmap(BUTTON_CLOSE);
+ gtk_table_attach(GTK_TABLE(Table),Button,5,6,1,2,GTK_FILL,GTK_FILL,0,0);
+ g_signal_connect(G_OBJECT(Button),"clicked", G_CALLBACK(Destroy_Search_File_Window),NULL);
+
+ // Status bar
+ SearchStatusBar = gtk_statusbar_new();
+ //gtk_table_attach(GTK_TABLE(Table),SearchStatusBar,0,6,3,4,GTK_FILL,GTK_FILL,0,0);
+ gtk_box_pack_start(GTK_BOX(VBox),SearchStatusBar,FALSE,TRUE,0);
+ SearchStatusBarContext = gtk_statusbar_get_context_id(GTK_STATUSBAR(SearchStatusBar),"Messages");
+ gtk_statusbar_push(GTK_STATUSBAR(SearchStatusBar),SearchStatusBarContext,_("Ready to search..."));
+
+ gtk_widget_show_all(SearchFileWindow);
+
+ if (SET_SEARCH_WINDOW_POSITION)
+ gdk_window_move(SearchFileWindow->window, SEARCH_WINDOW_X, SEARCH_WINDOW_Y);
+ //else
+ // gtk_window_set_position(GTK_WINDOW(SearchFileWindow), GTK_WIN_POS_CENTER_ON_PARENT); // Must use gtk_window_set_transient_for to work
+}
+
+void Destroy_Search_File_Window (void)
+{
+ if (SearchFileWindow)
+ {
+ /* Save combobox history lists before exit */
+ Save_Search_File_List(SearchStringModel, MISC_COMBO_TEXT);
+
+ Search_File_Window_Apply_Changes();
+
+ gtk_widget_destroy(SearchFileWindow);
+ SearchFileWindow = (GtkWidget *)NULL;
+ }
+}
+
+/*
+ * For the configuration file...
+ */
+void Search_File_Window_Apply_Changes (void)
+{
+ if (SearchFileWindow)
+ {
+ gint x, y, width, height;
+
+ if ( SearchFileWindow->window!=NULL && gdk_window_is_visible(SearchFileWindow->window)
+ && gdk_window_get_state(SearchFileWindow->window)!=GDK_WINDOW_STATE_MAXIMIZED )
+ {
+ // Position and Origin of the scanner window
+ gdk_window_get_root_origin(SearchFileWindow->window,&x,&y);
+ SEARCH_WINDOW_X = x;
+ SEARCH_WINDOW_Y = y;
+ gdk_window_get_size(SearchFileWindow->window,&width,&height);
+ SEARCH_WINDOW_WIDTH = width;
+ SEARCH_WINDOW_HEIGHT = height;
+ }
+
+ SEARCH_IN_FILENAME = GTK_TOGGLE_BUTTON(SearchInFilename)->active;
+ SEARCH_IN_TAG = GTK_TOGGLE_BUTTON(SearchInTag)->active;
+ SEARCH_CASE_SENSITIVE = GTK_TOGGLE_BUTTON(SearchCaseSensitive)->active;
+ }
+}
+
+gboolean Search_File_Window_Key_Press (GtkWidget *window, GdkEvent *event)
+{
+ GdkEventKey *kevent;
+
+ if (event && event->type == GDK_KEY_PRESS)
+ {
+ kevent = (GdkEventKey *)event;
+ switch(kevent->keyval)
+ {
+ case GDK_Escape:
+ Destroy_Search_File_Window();
+ break;
+ }
+ }
+ return FALSE;
+}
+
+/*
+ * This function and the one below could do with improving
+ * as we are looking up tag data twice (once when searching, once when adding to list)
+ */
+void Search_File (GtkWidget *search_button)
+{
+ const gchar *string_to_search = NULL;
+ GList *etfilelist;
+ ET_File *ETFile;
+ gchar *msg;
+ gchar *temp = NULL;
+ gchar *title2, *artist2, *album2, *disc_number2, *year2, *track2,
+ *track_total2, *genre2, *comment2, *composer2, *orig_artist2,
+ *copyright2, *url2, *encoded_by2, *string_to_search2;
+ gint resultCount = 0;
+
+
+ if (!SearchStringCombo || !SearchInFilename || !SearchInTag || !SearchResultList)
+ return;
+
+ string_to_search = gtk_entry_get_text(GTK_ENTRY(GTK_BIN(SearchStringCombo)->child));
+ if (!string_to_search)
+ return;
+
+ Add_String_To_Combo_List(SearchStringModel, (gchar*)string_to_search);
+
+ gtk_widget_set_sensitive(GTK_WIDGET(search_button),FALSE);
+ gtk_list_store_clear(SearchResultListModel);
+ gtk_statusbar_push(GTK_STATUSBAR(SearchStatusBar),SearchStatusBarContext,"");
+
+ etfilelist = g_list_first(ETCore->ETFileList);
+ while (etfilelist)
+ {
+ ETFile = (ET_File *)etfilelist->data;
+
+ // Search in the filename
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(SearchInFilename)))
+ {
+ gchar *filename_utf8 = ((File_Name *)ETFile->FileNameNew->data)->value_utf8;
+ gchar *basename_utf8;
+
+ // To search without case sensivity
+ if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(SearchCaseSensitive)))
+ {
+ temp = g_path_get_basename(filename_utf8);
+ basename_utf8 = g_utf8_casefold(temp, -1);
+ g_free(temp);
+ string_to_search2 = g_utf8_casefold(string_to_search, -1);
+ } else
+ {
+ basename_utf8 = g_path_get_basename(filename_utf8);
+ string_to_search2 = g_strdup(string_to_search);
+ }
+
+ if ( basename_utf8 && strstr(basename_utf8,string_to_search2) )
+ {
+ Add_Row_To_Search_Result_List(ETFile, string_to_search2);
+ etfilelist = etfilelist->next;
+ g_free(basename_utf8);
+ g_free(string_to_search2);
+ continue;
+ }
+ g_free(basename_utf8);
+ g_free(string_to_search2);
+ }
+
+ // Search in the tag
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(SearchInTag)))
+ {
+ File_Tag *FileTag = (File_Tag *)ETFile->FileTag->data;
+
+ // To search with or without case sensivity
+ if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(SearchCaseSensitive)))
+ {
+ // To search without case sensivity...
+ // Duplicate and convert the strings into UTF-8 in loxer case
+ if (FileTag->title) title2 = g_utf8_casefold(FileTag->title, -1); else title2 = NULL;
+ if (FileTag->artist) artist2 = g_utf8_casefold(FileTag->artist, -1); else artist2 = NULL;
+ if (FileTag->album) album2 = g_utf8_casefold(FileTag->album, -1); else album2 = NULL;
+ if (FileTag->disc_number) disc_number2 = g_utf8_casefold(FileTag->disc_number, -1); else disc_number2 = NULL;
+ if (FileTag->year) year2 = g_utf8_casefold(FileTag->year, -1); else year2 = NULL;
+ if (FileTag->track) track2 = g_utf8_casefold(FileTag->track, -1); else track2 = NULL;
+ if (FileTag->track_total) track_total2 = g_utf8_casefold(FileTag->track_total, -1); else track_total2 = NULL;
+ if (FileTag->genre) genre2 = g_utf8_casefold(FileTag->genre, -1); else genre2 = NULL;
+ if (FileTag->comment) comment2 = g_utf8_casefold(FileTag->comment, -1); else comment2 = NULL;
+ if (FileTag->composer) composer2 = g_utf8_casefold(FileTag->composer, -1); else composer2 = NULL;
+ if (FileTag->orig_artist) orig_artist2 = g_utf8_casefold(FileTag->orig_artist, -1); else orig_artist2 = NULL;
+ if (FileTag->copyright) copyright2 = g_utf8_casefold(FileTag->copyright, -1); else copyright2 = NULL;
+ if (FileTag->url) url2 = g_utf8_casefold(FileTag->url, -1); else url2 = NULL;
+ if (FileTag->encoded_by) encoded_by2 = g_utf8_casefold(FileTag->encoded_by, -1); else encoded_by2 = NULL;
+ string_to_search2 = g_utf8_strdown(string_to_search, -1);
+ }else
+ {
+ // To search with case sensivity...
+ // Duplicate and convert the strings into UTF-8
+ title2 = g_strdup(FileTag->title);
+ artist2 = g_strdup(FileTag->artist);
+ album2 = g_strdup(FileTag->album);
+ disc_number2 = g_strdup(FileTag->disc_number);
+ year2 = g_strdup(FileTag->year);
+ track2 = g_strdup(FileTag->track);
+ track_total2 = g_strdup(FileTag->track_total);
+ genre2 = g_strdup(FileTag->genre);
+ comment2 = g_strdup(FileTag->comment);
+ composer2 = g_strdup(FileTag->composer);
+ orig_artist2 = g_strdup(FileTag->orig_artist);
+ copyright2 = g_strdup(FileTag->copyright);
+ url2 = g_strdup(FileTag->url);
+ encoded_by2 = g_strdup(FileTag->encoded_by);
+ string_to_search2 = g_strdup(string_to_search);
+ }
+
+ // FIX ME : should use UTF-8 functions?
+ if ( (title2 && strstr(title2, string_to_search2) )
+ || (artist2 && strstr(artist2, string_to_search2) )
+ || (album2 && strstr(album2, string_to_search2) )
+ || (disc_number2 && strstr(disc_number2, string_to_search2) )
+ || (year2 && strstr(year2, string_to_search2) )
+ || (track2 && strstr(track2, string_to_search2) )
+ || (track_total2 && strstr(track_total2, string_to_search2) )
+ || (genre2 && strstr(genre2, string_to_search2) )
+ || (comment2 && strstr(comment2, string_to_search2) )
+ || (composer2 && strstr(composer2, string_to_search2) )
+ || (orig_artist2 && strstr(orig_artist2, string_to_search2) )
+ || (copyright2 && strstr(copyright2, string_to_search2) )
+ || (url2 && strstr(url2, string_to_search2) )
+ || (encoded_by2 && strstr(encoded_by2, string_to_search2) ) )
+ {
+ Add_Row_To_Search_Result_List(ETFile, string_to_search);
+ }
+ g_free(title2);
+ g_free(artist2);
+ g_free(album2);
+ g_free(disc_number2);
+ g_free(year2);
+ g_free(track2);
+ g_free(track_total2);
+ g_free(genre2);
+ g_free(comment2);
+ g_free(composer2);
+ g_free(orig_artist2);
+ g_free(copyright2);
+ g_free(url2);
+ g_free(encoded_by2);
+ g_free(string_to_search2);
+ }
+ etfilelist = etfilelist->next;
+ }
+
+ gtk_widget_set_sensitive(GTK_WIDGET(search_button),TRUE);
+
+ // Display the number of files in the statusbar
+ resultCount = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(SearchResultListModel), NULL);
+ msg = g_strdup_printf(_("Found : %d file(s)"), resultCount);
+ gtk_statusbar_push(GTK_STATUSBAR(SearchStatusBar),SearchStatusBarContext,msg);
+ g_free(msg);
+
+ // Disable result list if no row inserted
+ if (resultCount > 0 )
+ {
+ gtk_widget_set_sensitive(GTK_WIDGET(SearchResultList), TRUE);
+ } else
+ {
+ gtk_widget_set_sensitive(GTK_WIDGET(SearchResultList), FALSE);
+ }
+}
+
+void Add_Row_To_Search_Result_List(ET_File *ETFile,const gchar *string_to_search)
+{
+ gchar *SearchResultList_Text[14]; // Because : 14 columns to display
+ gint SearchResultList_Weight[14] = {PANGO_WEIGHT_NORMAL, PANGO_WEIGHT_NORMAL,
+ PANGO_WEIGHT_NORMAL, PANGO_WEIGHT_NORMAL,
+ PANGO_WEIGHT_NORMAL, PANGO_WEIGHT_NORMAL,
+ PANGO_WEIGHT_NORMAL, PANGO_WEIGHT_NORMAL,
+ PANGO_WEIGHT_NORMAL, PANGO_WEIGHT_NORMAL,
+ PANGO_WEIGHT_NORMAL, PANGO_WEIGHT_NORMAL,
+ PANGO_WEIGHT_NORMAL, PANGO_WEIGHT_NORMAL};
+ GdkColor *SearchResultList_Color[14] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL};
+ gchar *track, *track_total;
+ gboolean case_sensitive;
+ gint column;
+ GtkTreeIter iter;
+
+ if (!ETFile || !string_to_search)
+ return;
+
+ case_sensitive = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(SearchCaseSensitive));
+
+ // Filename
+ SearchResultList_Text[SEARCH_RESULT_FILENAME] = g_path_get_basename( ((File_Name *)ETFile->FileNameNew->data)->value_utf8 );
+ // Title
+ SearchResultList_Text[SEARCH_RESULT_TITLE] = g_strdup(((File_Tag *)ETFile->FileTag->data)->title);
+ // Artist
+ SearchResultList_Text[SEARCH_RESULT_ARTIST] = g_strdup(((File_Tag *)ETFile->FileTag->data)->artist);
+ // Album
+ SearchResultList_Text[SEARCH_RESULT_ALBUM] = g_strdup(((File_Tag *)ETFile->FileTag->data)->album);
+ // Disc Number
+ SearchResultList_Text[SEARCH_RESULT_DISC_NUMBER] = g_strdup(((File_Tag *)ETFile->FileTag->data)->disc_number);
+ // Year
+ SearchResultList_Text[SEARCH_RESULT_YEAR] = g_strdup(((File_Tag *)ETFile->FileTag->data)->year);
+ //Genre
+ SearchResultList_Text[SEARCH_RESULT_GENRE] = g_strdup(((File_Tag *)ETFile->FileTag->data)->genre);
+ // Comment
+ SearchResultList_Text[SEARCH_RESULT_COMMENT] = g_strdup(((File_Tag *)ETFile->FileTag->data)->comment);
+ // Composer
+ SearchResultList_Text[SEARCH_RESULT_COMPOSER] = g_strdup(((File_Tag *)ETFile->FileTag->data)->composer);
+ // Orig. Artist
+ SearchResultList_Text[SEARCH_RESULT_ORIG_ARTIST] = g_strdup(((File_Tag *)ETFile->FileTag->data)->orig_artist);
+ // Copyright
+ SearchResultList_Text[SEARCH_RESULT_COPYRIGHT] = g_strdup(((File_Tag *)ETFile->FileTag->data)->copyright);
+ // URL
+ SearchResultList_Text[SEARCH_RESULT_URL] = g_strdup(((File_Tag *)ETFile->FileTag->data)->url);
+ // Encoded by
+ SearchResultList_Text[SEARCH_RESULT_ENCODED_BY] = g_strdup(((File_Tag *)ETFile->FileTag->data)->encoded_by);
+
+ // Track
+ track = ((File_Tag *)ETFile->FileTag->data)->track;
+ track_total = ((File_Tag *)ETFile->FileTag->data)->track_total;
+ if (track)
+ {
+ if (track_total)
+ SearchResultList_Text[SEARCH_RESULT_TRACK] = g_strconcat(track,"/",track_total,NULL);
+ else
+ SearchResultList_Text[SEARCH_RESULT_TRACK] = g_strdup(track);
+ } else
+ {
+ SearchResultList_Text[SEARCH_RESULT_TRACK] = NULL;
+ }
+
+
+
+ // Highlight the keywords in the result list
+ // Don't display files to red if the searched string is '' (to display all files)
+ for (column=0;column<14;column++)
+ {
+ if (case_sensitive)
+ {
+ if ( SearchResultList_Text[column] && strlen(string_to_search) && strstr(SearchResultList_Text[column],string_to_search) )
+ {
+ if (CHANGED_FILES_DISPLAYED_TO_BOLD)
+ SearchResultList_Weight[column] = PANGO_WEIGHT_BOLD;
+ else
+ SearchResultList_Color[column] = &RED;
+ }
+
+ } else
+ {
+ // Search wasn't case sensitive
+ gchar *list_text = NULL;
+ gchar *string_to_search2 = g_utf8_casefold(string_to_search, -1);
+
+ if (!SearchResultList_Text[column])
+ {
+ g_free(string_to_search2);
+ continue;
+ }
+
+ list_text = g_utf8_casefold(SearchResultList_Text[column], -1);
+
+ if ( list_text && strlen(string_to_search2) && strstr(list_text,string_to_search2) )
+ {
+ if (CHANGED_FILES_DISPLAYED_TO_BOLD)
+ SearchResultList_Weight[column] = PANGO_WEIGHT_BOLD;
+ else
+ SearchResultList_Color[column] = &RED;
+ }
+
+ g_free(list_text);
+ g_free(string_to_search2);
+ }
+ }
+
+ // Load the row in the list
+ gtk_list_store_append(SearchResultListModel, &iter);
+ gtk_list_store_set(SearchResultListModel, &iter,
+ SEARCH_RESULT_FILENAME, SearchResultList_Text[SEARCH_RESULT_FILENAME],
+ SEARCH_RESULT_TITLE, SearchResultList_Text[SEARCH_RESULT_TITLE],
+ SEARCH_RESULT_ARTIST, SearchResultList_Text[SEARCH_RESULT_ARTIST],
+ SEARCH_RESULT_ALBUM, SearchResultList_Text[SEARCH_RESULT_ALBUM],
+ SEARCH_RESULT_DISC_NUMBER, SearchResultList_Text[SEARCH_RESULT_DISC_NUMBER],
+ SEARCH_RESULT_YEAR, SearchResultList_Text[SEARCH_RESULT_YEAR],
+ SEARCH_RESULT_TRACK, SearchResultList_Text[SEARCH_RESULT_TRACK],
+ SEARCH_RESULT_GENRE, SearchResultList_Text[SEARCH_RESULT_GENRE],
+ SEARCH_RESULT_COMMENT, SearchResultList_Text[SEARCH_RESULT_COMMENT],
+ SEARCH_RESULT_COMPOSER, SearchResultList_Text[SEARCH_RESULT_COMPOSER],
+ SEARCH_RESULT_ORIG_ARTIST, SearchResultList_Text[SEARCH_RESULT_ORIG_ARTIST],
+ SEARCH_RESULT_COPYRIGHT, SearchResultList_Text[SEARCH_RESULT_COPYRIGHT],
+ SEARCH_RESULT_URL, SearchResultList_Text[SEARCH_RESULT_URL],
+ SEARCH_RESULT_ENCODED_BY, SearchResultList_Text[SEARCH_RESULT_ENCODED_BY],
+
+ SEARCH_RESULT_FILENAME_WEIGHT, SearchResultList_Weight[SEARCH_RESULT_FILENAME],
+ SEARCH_RESULT_TITLE_WEIGHT, SearchResultList_Weight[SEARCH_RESULT_TITLE],
+ SEARCH_RESULT_ARTIST_WEIGHT, SearchResultList_Weight[SEARCH_RESULT_ARTIST],
+ SEARCH_RESULT_ALBUM_WEIGHT, SearchResultList_Weight[SEARCH_RESULT_ALBUM],
+ SEARCH_RESULT_DISC_NUMBER_WEIGHT, SearchResultList_Weight[SEARCH_RESULT_DISC_NUMBER],
+ SEARCH_RESULT_YEAR_WEIGHT, SearchResultList_Weight[SEARCH_RESULT_YEAR],
+ SEARCH_RESULT_TRACK_WEIGHT, SearchResultList_Weight[SEARCH_RESULT_TRACK],
+ SEARCH_RESULT_GENRE_WEIGHT, SearchResultList_Weight[SEARCH_RESULT_GENRE],
+ SEARCH_RESULT_COMMENT_WEIGHT, SearchResultList_Weight[SEARCH_RESULT_COMMENT],
+ SEARCH_RESULT_COMPOSER_WEIGHT, SearchResultList_Weight[SEARCH_RESULT_COMPOSER],
+ SEARCH_RESULT_ORIG_ARTIST_WEIGHT, SearchResultList_Weight[SEARCH_RESULT_ORIG_ARTIST],
+ SEARCH_RESULT_COPYRIGHT_WEIGHT, SearchResultList_Weight[SEARCH_RESULT_COPYRIGHT],
+ SEARCH_RESULT_URL_WEIGHT, SearchResultList_Weight[SEARCH_RESULT_URL],
+ SEARCH_RESULT_ENCODED_BY_WEIGHT, SearchResultList_Weight[SEARCH_RESULT_ENCODED_BY],
+
+ SEARCH_RESULT_FILENAME_FOREGROUND, SearchResultList_Color[SEARCH_RESULT_FILENAME],
+ SEARCH_RESULT_TITLE_FOREGROUND, SearchResultList_Color[SEARCH_RESULT_TITLE],
+ SEARCH_RESULT_ARTIST_FOREGROUND, SearchResultList_Color[SEARCH_RESULT_ARTIST],
+ SEARCH_RESULT_ALBUM_FOREGROUND, SearchResultList_Color[SEARCH_RESULT_ALBUM],
+ SEARCH_RESULT_DISC_NUMBER_FOREGROUND, SearchResultList_Color[SEARCH_RESULT_DISC_NUMBER],
+ SEARCH_RESULT_YEAR_FOREGROUND, SearchResultList_Color[SEARCH_RESULT_YEAR],
+ SEARCH_RESULT_TRACK_FOREGROUND, SearchResultList_Color[SEARCH_RESULT_TRACK],
+ SEARCH_RESULT_GENRE_FOREGROUND, SearchResultList_Color[SEARCH_RESULT_GENRE],
+ SEARCH_RESULT_COMMENT_FOREGROUND, SearchResultList_Color[SEARCH_RESULT_COMMENT],
+ SEARCH_RESULT_COMPOSER_FOREGROUND, SearchResultList_Color[SEARCH_RESULT_COMPOSER],
+ SEARCH_RESULT_ORIG_ARTIST_FOREGROUND, SearchResultList_Color[SEARCH_RESULT_ORIG_ARTIST],
+ SEARCH_RESULT_COPYRIGHT_FOREGROUND, SearchResultList_Color[SEARCH_RESULT_COPYRIGHT],
+ SEARCH_RESULT_URL_FOREGROUND, SearchResultList_Color[SEARCH_RESULT_URL],
+ SEARCH_RESULT_ENCODED_BY_FOREGROUND, SearchResultList_Color[SEARCH_RESULT_ENCODED_BY],
+
+ SEARCH_RESULT_POINTER, ETFile,
+ -1);
+
+ // Frees allocated data
+ g_free(SearchResultList_Text[SEARCH_RESULT_FILENAME]);
+ g_free(SearchResultList_Text[SEARCH_RESULT_TITLE]);
+ g_free(SearchResultList_Text[SEARCH_RESULT_ARTIST]);
+ g_free(SearchResultList_Text[SEARCH_RESULT_ALBUM]);
+ g_free(SearchResultList_Text[SEARCH_RESULT_DISC_NUMBER]);
+ g_free(SearchResultList_Text[SEARCH_RESULT_YEAR]);
+ g_free(SearchResultList_Text[SEARCH_RESULT_TRACK]);
+ g_free(SearchResultList_Text[SEARCH_RESULT_GENRE]);
+ g_free(SearchResultList_Text[SEARCH_RESULT_COMMENT]);
+ g_free(SearchResultList_Text[SEARCH_RESULT_COMPOSER]);
+ g_free(SearchResultList_Text[SEARCH_RESULT_ORIG_ARTIST]);
+ g_free(SearchResultList_Text[SEARCH_RESULT_COPYRIGHT]);
+ g_free(SearchResultList_Text[SEARCH_RESULT_URL]);
+ g_free(SearchResultList_Text[SEARCH_RESULT_ENCODED_BY]);
+}
+
+/*
+ * Callback to select-row event
+ * Select all results that are selected in the search result list also in the browser list
+ */
+void Search_Result_List_Row_Selected(GtkTreeSelection *selection, gpointer data)
+{
+ GList *selectedRows;
+ GList *selectedRowsCopy;
+ ET_File *ETFile;
+ GtkTreeIter currentFile;
+ gboolean found;
+
+ selectedRows = gtk_tree_selection_get_selected_rows(selection, NULL);
+ selectedRowsCopy = selectedRows;
+
+ /* We might be called with no rows selected */
+ if (g_list_length(selectedRows) == 0)
+ {
+ g_list_foreach(selectedRowsCopy, (GFunc) gtk_tree_path_free, NULL);
+ g_list_free(selectedRowsCopy);
+ return;
+ }
+
+ Browser_List_Unselect_All_Files();
+
+ while (selectedRows)
+ {
+ found = gtk_tree_model_get_iter(GTK_TREE_MODEL(SearchResultListModel), &currentFile, (GtkTreePath*)selectedRows->data);
+ if (found)
+ {
+ gtk_tree_model_get(GTK_TREE_MODEL(SearchResultListModel), &currentFile, SEARCH_RESULT_POINTER, &ETFile, -1);
+ Browser_List_Select_File_By_Etfile(ETFile, TRUE);
+ Action_Select_Nth_File_By_Etfile(ETFile);
+ }
+ selectedRows = selectedRows->next;
+ }
+
+ g_list_foreach(selectedRowsCopy, (GFunc) gtk_tree_path_free, NULL);
+ g_list_free(selectedRowsCopy);
+}
+
+
+/********************************************
+ * Load Filenames from a TXT file functions *
+ ********************************************/
+/*
+ * The window to load the filenames from a txt.
+ */
+void Open_Load_Filename_Window (void)
+{
+ GtkWidget *VBox, *hbox;
+ GtkWidget *Frame;
+ GtkWidget *Label;
+ GtkWidget *ButtonBox;
+ GtkWidget *Button;
+ GtkWidget *Entry;
+ GtkWidget *ButtonLoad;
+ GtkWidget *Separator;
+ GtkWidget *ScrollWindow;
+ GtkWidget *loadedvbox;
+ GtkWidget *filelistvbox;
+ GtkWidget *vboxpaned;
+ GtkTooltips *Tips;
+ gchar *path;
+ GtkCellRenderer* renderer;
+ GtkTreeViewColumn* column;
+
+ if (LoadFilenameWindow != NULL)
+ {
+ gdk_window_raise(LoadFilenameWindow->window);
+ return;
+ }
+
+ LoadFilenameWindow = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title(GTK_WINDOW(LoadFilenameWindow),_("Load the filenames from a TXT file"));
+ gtk_window_set_transient_for(GTK_WINDOW(LoadFilenameWindow),GTK_WINDOW(MainWindow));
+ g_signal_connect(G_OBJECT(LoadFilenameWindow),"destroy",G_CALLBACK(Destroy_Load_Filename_Window),NULL);
+ g_signal_connect(G_OBJECT(LoadFilenameWindow),"delete_event", G_CALLBACK(Destroy_Load_Filename_Window),NULL);
+ g_signal_connect(G_OBJECT(LoadFilenameWindow),"key_press_event", G_CALLBACK(Load_Filename_Window_Key_Press),NULL);
+
+ // Just center on mainwindow
+ gtk_window_set_position(GTK_WINDOW(LoadFilenameWindow), GTK_WIN_POS_CENTER_ON_PARENT);
+ gtk_window_set_default_size(GTK_WINDOW(LoadFilenameWindow),LOAD_FILE_WINDOW_WIDTH,LOAD_FILE_WINDOW_HEIGHT);
+
+ Tips = gtk_tooltips_new();
+
+ Frame = gtk_frame_new(NULL);
+ gtk_container_add(GTK_CONTAINER(LoadFilenameWindow),Frame);
+ gtk_container_set_border_width(GTK_CONTAINER(Frame),2);
+
+ VBox = gtk_vbox_new(FALSE,4);
+ gtk_container_add(GTK_CONTAINER(Frame),VBox);
+ gtk_container_set_border_width(GTK_CONTAINER(VBox), 2);
+
+ // Hbox for file entry and browser/load buttons
+ hbox = gtk_hbox_new(FALSE,4);
+ gtk_box_pack_start(GTK_BOX(VBox),hbox,FALSE,TRUE,0);
+
+ // File to load
+ if (!FileToLoadModel)
+ FileToLoadModel = gtk_list_store_new(MISC_COMBO_COUNT, G_TYPE_STRING);
+ else
+ gtk_list_store_clear(FileToLoadModel);
+
+ Label = gtk_label_new(_("File :"));
+ gtk_misc_set_alignment(GTK_MISC(Label),1.0,0.5);
+ gtk_box_pack_start(GTK_BOX(hbox),Label,FALSE,FALSE,0);
+ FileToLoadCombo = gtk_combo_box_entry_new_with_model(GTK_TREE_MODEL(FileToLoadModel), MISC_COMBO_TEXT);
+ gtk_widget_set_size_request(GTK_WIDGET(FileToLoadCombo), 200, -1);
+ gtk_box_pack_start(GTK_BOX(hbox),FileToLoadCombo,TRUE,TRUE,0);
+ // History List
+ Load_File_To_Load_List(FileToLoadModel, MISC_COMBO_TEXT);
+ // Initial value
+ if ((path=Browser_Get_Current_Path())!=NULL)
+ gtk_entry_set_text(GTK_ENTRY(GTK_BIN(FileToLoadCombo)->child),path);
+ // the 'changed' signal is attached below to enable/disable the button to load
+ // Button 'browse'
+ Button = Create_Button_With_Pixmap(BUTTON_BROWSE);
+ gtk_box_pack_start(GTK_BOX(hbox),Button,FALSE,FALSE,0);
+ g_signal_connect_swapped(G_OBJECT(Button),"clicked", G_CALLBACK(File_Selection_Window_For_File), G_OBJECT(GTK_BIN(FileToLoadCombo)->child));
+ // Button 'load'
+ // the signal attached to this button, to load the file, is placed after the LoadFileContentList definition
+ ButtonLoad = Create_Button_With_Icon_And_Label("easytag-add",_(" Load "));
+ //ButtonLoad = gtk_button_new_with_label(_(" Load "));
+ gtk_box_pack_start(GTK_BOX(hbox),ButtonLoad,FALSE,FALSE,0);
+ g_signal_connect_swapped(G_OBJECT(GTK_BIN(FileToLoadCombo)->child),"changed", G_CALLBACK(Button_Load_Set_Sensivity), G_OBJECT(ButtonLoad));
+
+ // Separator line
+ Separator = gtk_hseparator_new();
+ gtk_box_pack_start(GTK_BOX(VBox),Separator,FALSE,FALSE,0);
+
+ loadedvbox = gtk_vbox_new(FALSE, 4);
+
+ // Label of file content
+ Label = gtk_label_new(_("Loaded File Content :"));
+ gtk_box_pack_start(GTK_BOX(loadedvbox), Label, FALSE, FALSE, 0);
+
+ // Content of the loaded file
+ ScrollWindow = gtk_scrolled_window_new(NULL,NULL);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ScrollWindow),GTK_POLICY_AUTOMATIC,GTK_POLICY_AUTOMATIC);
+ gtk_widget_set_size_request(GTK_WIDGET(ScrollWindow), 250, 200);
+ LoadFileContentListModel = gtk_list_store_new(LOAD_FILE_CONTENT_COUNT, G_TYPE_STRING);
+ LoadFileContentList = gtk_tree_view_new_with_model(GTK_TREE_MODEL(LoadFileContentListModel));
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes("",
+ renderer, "text", LOAD_FILE_CONTENT_TEXT, NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(LoadFileContentList), column);
+ gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(LoadFileContentList), FALSE);
+ gtk_container_add(GTK_CONTAINER(ScrollWindow),LoadFileContentList);
+ gtk_box_pack_start(GTK_BOX(loadedvbox), ScrollWindow, TRUE, TRUE, 0);
+
+ // Signal to automatically load the file
+ g_signal_connect_swapped(G_OBJECT(ButtonLoad),"clicked", G_CALLBACK(Load_File_Content), G_OBJECT(GTK_BIN(FileToLoadCombo)->child));
+ g_signal_connect(G_OBJECT(LoadFileContentList),"key-press-event", G_CALLBACK(Load_Filename_List_Key_Press),NULL);
+
+ gtk_widget_show_all(loadedvbox);
+
+ filelistvbox = gtk_vbox_new(FALSE, 4);
+
+ // Label of current list
+ Label = gtk_label_new(_("Files Name List :"));
+ gtk_box_pack_start(GTK_BOX(filelistvbox), Label, FALSE, FALSE, 0);
+
+ // List of current filenames
+ ScrollWindow = gtk_scrolled_window_new(NULL,NULL);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ScrollWindow),GTK_POLICY_AUTOMATIC,GTK_POLICY_AUTOMATIC);
+ gtk_widget_set_size_request(GTK_WIDGET(ScrollWindow), 250, 200);
+ gtk_box_pack_start(GTK_BOX(filelistvbox), ScrollWindow, TRUE, TRUE, 0);
+ LoadFileNameListModel = gtk_list_store_new(LOAD_FILE_NAME_COUNT, G_TYPE_STRING,G_TYPE_POINTER);
+ LoadFileNameList = gtk_tree_view_new_with_model(GTK_TREE_MODEL(LoadFileNameListModel));
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes("", renderer, "text", LOAD_FILE_NAME_TEXT, NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(LoadFileNameList), column);
+ gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(LoadFileNameList), FALSE);
+ g_signal_connect(G_OBJECT(LoadFileNameList),"key-press-event", G_CALLBACK(Load_Filename_List_Key_Press),NULL);
+ gtk_container_add(GTK_CONTAINER(ScrollWindow),LoadFileNameList);
+
+ // Signals to 'select' the same row into the other list (to show the corresponding filenames)
+ g_signal_connect_swapped(G_OBJECT(gtk_tree_view_get_selection(GTK_TREE_VIEW(LoadFileContentList))),"changed", G_CALLBACK(Load_Filename_Select_Row_In_Other_List), G_OBJECT(LoadFileNameList));
+ g_signal_connect_swapped(G_OBJECT(gtk_tree_view_get_selection(GTK_TREE_VIEW(LoadFileNameList))),"changed", G_CALLBACK(Load_Filename_Select_Row_In_Other_List), G_OBJECT(LoadFileContentList));
+
+ gtk_widget_show_all(filelistvbox);
+
+ // Load the list of files in the list widget
+ Load_File_List();
+
+ vboxpaned = gtk_hpaned_new();
+ gtk_paned_pack1(GTK_PANED(vboxpaned),loadedvbox, TRUE,FALSE);
+ gtk_paned_pack2(GTK_PANED(vboxpaned),filelistvbox,TRUE,FALSE);
+ gtk_box_pack_start(GTK_BOX(VBox),vboxpaned,TRUE,TRUE,0);
+
+ // Create popup menus
+ Create_Load_Filename_Popup_Menu(LoadFileContentList);
+ Create_Load_Filename_Popup_Menu(LoadFileNameList);
+
+ // Entry to edit a line into the list
+ Entry = gtk_entry_new();
+ gtk_box_pack_start(GTK_BOX(VBox),Entry,FALSE,TRUE,0);
+ g_signal_connect(G_OBJECT(Entry),"changed",G_CALLBACK(Load_Filename_Update_Text_Line),G_OBJECT(LoadFileContentList));
+
+ // Signal to load the line text in the editing entry
+ g_signal_connect(G_OBJECT(gtk_tree_view_get_selection(GTK_TREE_VIEW(LoadFileContentList))),"changed", G_CALLBACK(Load_Filename_Edit_Text_Line), G_OBJECT(Entry));
+
+ // Separator line
+ Separator = gtk_hseparator_new();
+ gtk_box_pack_start(GTK_BOX(VBox),Separator,FALSE,FALSE,0);
+
+ LoadFileRunScanner = gtk_check_button_new_with_label(_("Run the current scanner for each file"));
+ gtk_box_pack_start(GTK_BOX(VBox),LoadFileRunScanner,FALSE,TRUE,0);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(LoadFileRunScanner),LOAD_FILE_RUN_SCANNER);
+ gtk_tooltips_set_tip(Tips,LoadFileRunScanner,_("When activating this option, after loading the "
+ "filenames, the current selected scanner will be ran (the scanner window must be opened)."),NULL);
+
+ // Separator line
+ Separator = gtk_hseparator_new();
+ gtk_box_pack_start(GTK_BOX(VBox),Separator,FALSE,FALSE,0);
+
+ ButtonBox = gtk_hbutton_box_new ();
+ gtk_box_pack_start(GTK_BOX(VBox),ButtonBox,FALSE,FALSE,0);
+ gtk_button_box_set_layout(GTK_BUTTON_BOX(ButtonBox),GTK_BUTTONBOX_END);
+ gtk_box_set_spacing(GTK_BOX(ButtonBox), 10);
+
+ // Button to cancel
+ Button = Create_Button_With_Pixmap(BUTTON_CLOSE);
+ gtk_container_add(GTK_CONTAINER(ButtonBox),Button);
+ GTK_WIDGET_SET_FLAGS(Button, GTK_CAN_DEFAULT);
+ gtk_widget_grab_default(Button);
+ g_signal_connect(G_OBJECT(Button),"clicked", G_CALLBACK(Destroy_Load_Filename_Window),NULL);
+
+ // Button to load filenames
+ Button = Create_Button_With_Pixmap(BUTTON_APPLY);
+ gtk_container_add(GTK_CONTAINER(ButtonBox),Button);
+ GTK_WIDGET_SET_FLAGS(Button,GTK_CAN_DEFAULT);
+ g_signal_connect(G_OBJECT(Button),"clicked", G_CALLBACK(Load_Filename_Set_Filenames),NULL);
+
+
+ // To initialize 'ButtonLoad' sensivity
+ g_signal_emit_by_name(G_OBJECT(GTK_BIN(FileToLoadCombo)->child),"changed");
+
+ gtk_widget_show_all(LoadFilenameWindow);
+ if (LOAD_FILE_WINDOW_X > 0 && LOAD_FILE_WINDOW_Y > 0)
+ gdk_window_move(LoadFilenameWindow->window,LOAD_FILE_WINDOW_X,LOAD_FILE_WINDOW_Y);
+}
+
+void Destroy_Load_Filename_Window (void)
+{
+ if (LoadFilenameWindow)
+ {
+ /* Save combobox history lists before exit */
+ Save_File_To_Load_List(FileToLoadModel, MISC_COMBO_TEXT);
+
+ Load_Filename_Window_Apply_Changes();
+
+ gtk_widget_destroy(LoadFilenameWindow);
+ LoadFilenameWindow = (GtkWidget *)NULL;
+ }
+}
+
+/*
+ * For the configuration file...
+ */
+void Load_Filename_Window_Apply_Changes (void)
+{
+ if (LoadFilenameWindow)
+ {
+ gint x, y, width, height;
+
+ if ( LoadFilenameWindow->window && gdk_window_is_visible(LoadFilenameWindow->window)
+ && gdk_window_get_state(LoadFilenameWindow->window)!=GDK_WINDOW_STATE_MAXIMIZED )
+ {
+ // Position and Origin of the window
+ gdk_window_get_root_origin(LoadFilenameWindow->window,&x,&y);
+ LOAD_FILE_WINDOW_X = x;
+ LOAD_FILE_WINDOW_Y = y;
+ gdk_window_get_size(LoadFilenameWindow->window,&width,&height);
+ LOAD_FILE_WINDOW_WIDTH = width;
+ LOAD_FILE_WINDOW_HEIGHT = height;
+ }
+
+ LOAD_FILE_RUN_SCANNER = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(LoadFileRunScanner));
+ }
+}
+
+gboolean Load_Filename_Window_Key_Press (GtkWidget *window, GdkEvent *event)
+{
+ GdkEventKey *kevent;
+
+ if (event && event->type == GDK_KEY_PRESS)
+ {
+ kevent = (GdkEventKey *)event;
+ switch(kevent->keyval)
+ {
+ case GDK_Escape:
+ Destroy_Load_Filename_Window();
+ break;
+ }
+ }
+ return FALSE;
+}
+
+/*
+ * To enable/disable sensivity of the button 'Load'
+ */
+void Button_Load_Set_Sensivity (GtkWidget *button, GtkWidget *entry)
+{
+ struct stat statbuf;
+ gchar *path;
+
+ if (!entry || !button)
+ return;
+
+ path = filename_from_display(gtk_entry_get_text(GTK_ENTRY(entry)));
+ if ( path && stat(path,&statbuf)==0 && S_ISREG(statbuf.st_mode))
+ gtk_widget_set_sensitive(GTK_WIDGET(button),TRUE);
+ else
+ gtk_widget_set_sensitive(GTK_WIDGET(button),FALSE);
+
+ g_free(path);
+}
+
+void Load_Filename_List_Key_Press (GtkWidget *treeview, GdkEvent *event)
+{
+ if (event && event->type == GDK_KEY_PRESS)
+ {
+ GdkEventKey *kevent = (GdkEventKey *)event;
+
+ switch(kevent->keyval)
+ {
+ case GDK_Delete:
+ Load_Filename_List_Delete_Line(treeview);
+ break;
+ case GDK_I:
+ case GDK_i:
+ Load_Filename_List_Insert_Blank_Line(treeview);
+ break;
+ }
+ }
+}
+
+/*
+ * Load content of the file into the LoadFileContentList list
+ */
+void Load_File_Content (GtkWidget *entry)
+{
+ FILE *file;
+ gchar *filename;
+ const gchar *filename_utf8;
+ gchar buffer[MAX_STRING_LEN];
+ gchar *text;
+ gchar *valid;
+ GtkTreeIter iter;
+
+ if (!entry)
+ return;
+
+ // The file to read
+ filename_utf8 = gtk_entry_get_text(GTK_ENTRY(entry)); // Don't free me!
+ Add_String_To_Combo_List(FileToLoadModel, (gchar*)filename_utf8);
+ filename = filename_from_display(filename_utf8);
+
+ if ( (file=fopen(filename,"r"))==0 )
+ {
+ Log_Print(_("Can't open file '%s' (%s)"),filename_utf8,g_strerror(errno));
+ g_free(filename);
+ return;
+ }
+
+ gtk_list_store_clear(LoadFileContentListModel);
+ while (fgets(buffer, sizeof(buffer), file))
+ {
+ if (buffer[strlen(buffer)-1]=='\n')
+ buffer[strlen(buffer)-1]='\0';
+ if (buffer[strlen(buffer)-1]=='\r')
+ buffer[strlen(buffer)-1]='\0';
+ text = &buffer[0];
+
+ if (g_utf8_validate(text, -1, NULL))
+ {
+ valid = g_strdup(buffer);
+ } else
+ {
+ valid = convert_to_utf8(text);
+ }
+
+ gtk_list_store_append(LoadFileContentListModel, &iter);
+ gtk_list_store_set(LoadFileContentListModel, &iter,
+ LOAD_FILE_CONTENT_TEXT, valid,
+ -1);
+ g_free(valid);
+ }
+
+ fclose(file);
+ g_free(filename);
+}
+
+/*
+ * Load the names of the current list of files
+ */
+void Load_File_List (void)
+{
+ GList *etfilelist;
+ ET_File *etfile;
+ gchar *filename_utf8;
+ gchar *pos;
+ GtkTreeIter iter;
+
+ gtk_list_store_clear(LoadFileNameListModel);
+ etfilelist = g_list_first(ETCore->ETFileList);
+ while (etfilelist)
+ {
+ etfile = (ET_File *)etfilelist->data;
+ filename_utf8 = g_path_get_basename(((File_Name *)etfile->FileNameNew->data)->value_utf8);
+ // Remove the extension ('filename' must be allocated to don't affect the initial value)
+ if ((pos=strrchr(filename_utf8,'.'))!=NULL)
+ *pos = 0;
+ gtk_list_store_append(LoadFileNameListModel, &iter);
+ gtk_list_store_set(LoadFileNameListModel, &iter,
+ LOAD_FILE_NAME_TEXT, filename_utf8,
+ LOAD_FILE_NAME_POINTER, etfilelist->data,
+ -1);
+ g_free(filename_utf8);
+ etfilelist = etfilelist->next;
+ }
+}
+/*
+ * To select the corresponding row in the other list
+ */
+void Load_Filename_Select_Row_In_Other_List(GtkWidget* treeview_target, gpointer origselection)
+{
+ GtkAdjustment *ct_adj, *ce_adj;
+ GtkTreeSelection *selection_orig;
+ GtkTreeSelection *selection_target;
+ GtkTreeView *treeview_orig;
+ GtkTreeModel *treemodel_orig;
+ GtkTreeModel *treemodel_target;
+ GtkTreeIter iter_orig;
+ GtkTreeIter iter_target;
+ GtkTreePath *path_orig;
+ gint *indices_orig;
+ gchar *stringiter;
+
+ if (!treeview_target || !origselection)
+ return;
+
+ selection_orig = (GtkTreeSelection*) origselection;
+ selection_target = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview_target));
+ treemodel_target = gtk_tree_view_get_model(GTK_TREE_VIEW(treeview_target));
+
+ if (!gtk_tree_selection_get_selected(selection_orig, &treemodel_orig, &iter_orig))
+ return; /* Might be called with no selection */
+
+ treeview_orig = gtk_tree_selection_get_tree_view(selection_orig);
+ path_orig = gtk_tree_model_get_path(treemodel_orig, &iter_orig);
+ gtk_tree_selection_unselect_all(selection_target);
+
+ // Synchronize the two lists
+ ce_adj = gtk_tree_view_get_vadjustment(treeview_orig);
+ ct_adj = gtk_tree_view_get_vadjustment(GTK_TREE_VIEW(treeview_target));
+
+ if (GTK_ADJUSTMENT(ct_adj)->upper >= GTK_ADJUSTMENT(ct_adj)->page_size
+ && GTK_ADJUSTMENT(ce_adj)->upper >= GTK_ADJUSTMENT(ce_adj)->page_size)
+ {
+ // Rules are displayed in the both clist
+ if (GTK_ADJUSTMENT(ce_adj)->value <= GTK_ADJUSTMENT(ct_adj)->upper - GTK_ADJUSTMENT(ct_adj)->page_size)
+ {
+ gtk_adjustment_set_value(GTK_ADJUSTMENT(ct_adj),GTK_ADJUSTMENT(ce_adj)->value);
+ } else
+ {
+ gtk_adjustment_set_value(GTK_ADJUSTMENT(ct_adj),GTK_ADJUSTMENT(ct_adj)->upper - GTK_ADJUSTMENT(ct_adj)->page_size);
+ indices_orig = gtk_tree_path_get_indices(path_orig);
+
+ if (indices_orig[0] <= (gtk_tree_model_iter_n_children(treemodel_target, NULL) - 1))
+ gtk_adjustment_set_value(GTK_ADJUSTMENT(ce_adj),GTK_ADJUSTMENT(ct_adj)->value);
+
+ }
+ }else if (GTK_ADJUSTMENT(ct_adj)->upper < GTK_ADJUSTMENT(ct_adj)->page_size) // Target Clist rule not visible
+ {
+ indices_orig = gtk_tree_path_get_indices(path_orig);
+
+ if (indices_orig[0] <= (gtk_tree_model_iter_n_children(treemodel_target, NULL) - 1))
+ gtk_adjustment_set_value(GTK_ADJUSTMENT(ce_adj),GTK_ADJUSTMENT(ct_adj)->value);
+ }
+
+ // Must block the select signal of the target to avoid looping
+ g_signal_handlers_block_by_func(G_OBJECT(selection_target), G_CALLBACK(Load_Filename_Select_Row_In_Other_List), G_OBJECT(treeview_orig));
+
+ stringiter = gtk_tree_model_get_string_from_iter(treemodel_orig, &iter_orig);
+ if (gtk_tree_model_get_iter_from_string(treemodel_target, &iter_target, stringiter))
+ {
+ gtk_tree_selection_select_iter(selection_target, &iter_target);
+ }
+
+ g_free(stringiter);
+ g_signal_handlers_unblock_by_func(G_OBJECT(selection_target), G_CALLBACK(Load_Filename_Select_Row_In_Other_List), G_OBJECT(treeview_orig));
+}
+
+/*
+ * Set the new file name of each file.
+ * Associate lines from LoadFileContentList with LoadFileNameList
+ */
+void Load_Filename_Set_Filenames (void)
+{
+ gint row;
+ ET_File *ETFile;
+ File_Name *FileName;
+ gchar *list_text;
+ gint rowcount;
+ gboolean found;
+
+ GtkTreePath *currentPath = NULL;
+ GtkTreeIter iter_name;
+ GtkTreeIter iter_content;
+
+ if ( !ETCore->ETFileList || !LoadFileContentList || !LoadFileNameList)
+ return;
+
+ /* Save current file */
+ ET_Save_File_Data_From_UI(ETCore->ETFileDisplayed);
+
+ rowcount = MIN(gtk_tree_model_iter_n_children(GTK_TREE_MODEL(LoadFileNameListModel), NULL),
+ gtk_tree_model_iter_n_children(GTK_TREE_MODEL(LoadFileContentListModel), NULL));
+
+ for (row=0; row < rowcount; row++)
+ {
+ if (row == 0)
+ currentPath = gtk_tree_path_new_first();
+ else
+ gtk_tree_path_next(currentPath);
+
+ found = gtk_tree_model_get_iter(GTK_TREE_MODEL(LoadFileNameListModel), &iter_name, currentPath);
+ if (found)
+ gtk_tree_model_get(GTK_TREE_MODEL(LoadFileNameListModel), &iter_name, LOAD_FILE_NAME_POINTER, &ETFile, -1);
+
+ found = gtk_tree_model_get_iter(GTK_TREE_MODEL(LoadFileContentListModel), &iter_content, currentPath);
+ if (found)
+ gtk_tree_model_get(GTK_TREE_MODEL(LoadFileContentListModel), &iter_content, LOAD_FILE_CONTENT_TEXT, &list_text, -1);
+
+ if (ETFile && list_text && g_utf8_strlen(list_text, -1)>0)
+ {
+ gchar *list_text_tmp;
+ gchar *filename_new_utf8;
+
+ list_text_tmp = g_strdup(list_text);
+ ET_File_Name_Convert_Character(list_text_tmp); // Replace invalid characters
+
+ /* Build the filename with the path */
+ filename_new_utf8 = ET_File_Name_Generate(ETFile,list_text_tmp);
+ g_free(list_text_tmp);
+
+ /* Set the new filename */
+ // Create a new 'File_Name' item
+ FileName = ET_File_Name_Item_New();
+ // Save changes of the 'File_Name' item
+ ET_Set_Filename_File_Name_Item(FileName,filename_new_utf8,NULL);
+ ET_Manage_Changes_Of_File_Data(ETFile,FileName,NULL);
+
+ g_free(filename_new_utf8);
+
+ // Then run current scanner if asked...
+ if (ScannerWindow && gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(LoadFileRunScanner)) )
+ Scan_Select_Mode_And_Run_Scanner(ETFile);
+ }
+ g_free(list_text);
+ }
+
+ g_free(currentPath);
+
+ Browser_List_Refresh_Whole_List();
+ ET_Display_File_Data_To_UI(ETCore->ETFileDisplayed);
+}
+
+/*
+ * Create and attach a popup menu on the two clist of the LoadFileWindow
+ */
+gboolean Load_Filename_Popup_Menu_Handler (GtkMenu *menu, GdkEventButton *event)
+{
+ if (event && (event->type==GDK_BUTTON_PRESS) && (event->button==3))
+ {
+ gtk_menu_popup(menu,NULL,NULL,NULL,NULL,event->button,event->time);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+GtkWidget *Create_Load_Filename_Popup_Menu(GtkWidget *list)
+{
+ GtkWidget *BrowserPopupMenu;
+ GtkWidget *Image;
+ GtkWidget *MenuItem;
+
+
+ BrowserPopupMenu = gtk_menu_new();
+ g_signal_connect_swapped(G_OBJECT(list),"button_press_event", G_CALLBACK(Load_Filename_Popup_Menu_Handler), G_OBJECT(BrowserPopupMenu));
+
+ MenuItem = gtk_image_menu_item_new_with_label(_("Insert a blank line"));
+ Image = gtk_image_new_from_stock(GTK_STOCK_ADD,GTK_ICON_SIZE_MENU);
+ gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(MenuItem),Image);
+ gtk_menu_shell_append(GTK_MENU_SHELL(BrowserPopupMenu), MenuItem);
+ g_signal_connect_swapped(G_OBJECT(MenuItem),"activate", G_CALLBACK(Load_Filename_List_Insert_Blank_Line), G_OBJECT(list));
+
+ MenuItem = gtk_image_menu_item_new_with_label(_("Delete this line"));
+ Image = gtk_image_new_from_stock(GTK_STOCK_REMOVE,GTK_ICON_SIZE_MENU);
+ gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(MenuItem),Image);
+ gtk_menu_shell_append(GTK_MENU_SHELL(BrowserPopupMenu),MenuItem);
+ g_signal_connect_swapped(G_OBJECT(MenuItem),"activate", G_CALLBACK(Load_Filename_List_Delete_Line), G_OBJECT(list));
+
+ MenuItem = gtk_image_menu_item_new_with_label(_("Delete all blank lines"));
+ Image = gtk_image_new_from_stock(GTK_STOCK_REMOVE,GTK_ICON_SIZE_MENU);
+ gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(MenuItem),Image);
+ gtk_menu_shell_append(GTK_MENU_SHELL(BrowserPopupMenu),MenuItem);
+ g_signal_connect_swapped(G_OBJECT(MenuItem),"activate", G_CALLBACK(Load_Filename_List_Delete_All_Blank_Lines),G_OBJECT(list));
+
+ MenuItem = gtk_menu_item_new();
+ gtk_menu_shell_append(GTK_MENU_SHELL(BrowserPopupMenu),MenuItem);
+
+ MenuItem = gtk_image_menu_item_new_with_label(_("Reload"));
+ Image = gtk_image_new_from_stock(GTK_STOCK_REFRESH,GTK_ICON_SIZE_MENU);
+ gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(MenuItem),Image);
+ gtk_menu_shell_append(GTK_MENU_SHELL(BrowserPopupMenu),MenuItem);
+ g_signal_connect_swapped(G_OBJECT(MenuItem),"activate", G_CALLBACK(Load_Filename_List_Reload), G_OBJECT(list));
+
+ gtk_widget_show_all(BrowserPopupMenu);
+ return BrowserPopupMenu;
+}
+
+/*
+ * Insert a blank line before the selected line in the treeview passed as parameter
+ */
+void Load_Filename_List_Insert_Blank_Line(GtkWidget *treeview)
+{
+ GtkTreeSelection *selection;
+ GtkTreeIter selectedIter;
+ GtkTreeIter *temp;
+ GtkTreeModel *model;
+
+ if (!treeview) return;
+
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
+
+ if (gtk_tree_selection_get_selected(selection, &model, &selectedIter) != TRUE)
+ return;
+
+ temp = &selectedIter; /* Not used here, but it must be non-NULL to keep GTK+ happy! */
+ gtk_list_store_insert_before(GTK_LIST_STORE(model), temp, &selectedIter);
+}
+
+/*
+ * Delete all blank lines in the treeview passed in as parameter
+ */
+void Load_Filename_List_Delete_All_Blank_Lines (GtkWidget *treeview)
+{
+ gchar *text = NULL;
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+
+ model = gtk_tree_view_get_model(GTK_TREE_VIEW(treeview));
+
+ if (!gtk_tree_model_get_iter_first(model, &iter))
+ return;
+
+ while (TRUE)
+ {
+ gtk_tree_model_get(model, &iter, LOAD_FILE_NAME_TEXT, &text, -1);
+
+ /* Check for blank entry */
+ if (!text || g_utf8_strlen(text, -1) == 0)
+ {
+ g_free(text);
+
+ if (!gtk_list_store_remove(GTK_LIST_STORE(model), &iter))
+ break;
+ else
+ continue;
+ }
+ g_free(text);
+
+ if (!gtk_tree_model_iter_next(model, &iter))
+ break;
+ }
+}
+
+/*
+ * Delete the selected line in the treeview passed in as parameter
+ */
+void Load_Filename_List_Delete_Line(GtkWidget *treeview)
+{
+ GtkTreeSelection *selection;
+ GtkTreeIter selectedIter, itercopy;
+ GtkTreeModel *model;
+ gboolean rowafter;
+
+ if (!treeview) return;
+
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
+
+ if (gtk_tree_selection_get_selected(selection, &model, &selectedIter) != TRUE)
+ {
+ return;
+ }
+
+ // If there is a line following the one about to be removed, select it for convenience
+ itercopy = selectedIter;
+ rowafter = gtk_tree_model_iter_next(model, &itercopy);
+
+ // Remove the line to be deleted
+ gtk_list_store_remove(GTK_LIST_STORE(model), &selectedIter);
+
+ if (rowafter)
+ gtk_tree_selection_select_iter(selection, &itercopy);
+}
+
+/*
+ * Reload a list of choice
+ * The list parameter refers to a GtkTreeView (LoadFileNameList or LoadFileContentList)
+ */
+void Load_Filename_List_Reload (GtkWidget *treeview)
+{
+ if (!treeview) return;
+
+ if (GTK_TREE_VIEW(treeview) == (GtkTreeView *)LoadFileContentList)
+ {
+ Load_File_Content(GTK_BIN(FileToLoadCombo)->child);
+ } else if (GTK_TREE_VIEW(treeview) == (GtkTreeView *)LoadFileNameList)
+ {
+ Load_File_List();
+ }
+}
+
+/*
+ * Update the text of the selected line into the list, with the text entered into the entry
+ */
+void Load_Filename_Update_Text_Line(GtkWidget *entry, GtkWidget *list)
+{
+ GtkTreeIter SelectedRow;
+ GtkTreeSelection *selection;
+ GtkTreeModel *model;
+ gboolean hasSelectedRows = FALSE;
+
+ if (!list || !entry) return;
+
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(list));
+ hasSelectedRows = gtk_tree_selection_get_selected(selection, &model, &SelectedRow);
+ if (hasSelectedRows)
+ {
+ const gchar *text = gtk_entry_get_text(GTK_ENTRY(entry));
+ gtk_list_store_set(GTK_LIST_STORE(model), &SelectedRow, LOAD_FILE_CONTENT_TEXT, text, -1);
+ }
+}
+
+/*
+ * Set the text of the selected line of the list into the entry
+ */
+void Load_Filename_Edit_Text_Line(GtkTreeSelection *selection, gpointer data)
+{
+ gchar *text;
+ GtkTreeIter selectedIter;
+ GtkEntry *entry = (GtkEntry*)data;
+ gulong handler;
+
+ if (gtk_tree_selection_get_selected(selection, NULL, &selectedIter) != TRUE)
+ return;
+
+ gtk_tree_model_get(GTK_TREE_MODEL(LoadFileContentListModel), &selectedIter, LOAD_FILE_NAME_TEXT, &text, -1);
+
+ handler = g_signal_handler_find(entry, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, Load_Filename_Update_Text_Line, NULL);
+ g_signal_handler_block(entry, handler);
+ if (text)
+ {
+ gtk_entry_set_text(entry, text);
+ g_free(text);
+ } else
+ gtk_entry_set_text(entry, "");
+
+ g_signal_handler_unblock(entry, handler);
+}
diff --git a/src/misc.h b/src/misc.h
new file mode 100755
index 0000000..2fbadde
--- /dev/null
+++ b/src/misc.h
@@ -0,0 +1,122 @@
+/* misc.h - 2000/06/28 */
+/*
+ * EasyTAG - Tag editor for MP3 and Ogg Vorbis files
+ * Copyright (C) 2000-2003 Jerome Couderc <easytag@gmail.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __MISC_H__
+#define __MISC_H__
+
+
+#include <gtk/gtk.h>
+
+/***************
+ * Declaration *
+ ***************/
+
+GtkWidget *WritePlaylistWindow;
+GtkWidget *playlist_use_mask_name;
+GtkWidget *PlayListNameMaskCombo;
+GtkWidget *playlist_use_dir_name;
+GtkWidget *playlist_only_selected_files;
+GtkWidget *playlist_full_path;
+GtkWidget *playlist_relative_path;
+GtkWidget *playlist_create_in_parent_dir;
+GtkWidget *playlist_use_dos_separator;
+GtkWidget *playlist_content_none;
+GtkWidget *playlist_content_filename;
+GtkWidget *playlist_content_mask;
+GtkWidget *PlayListContentMaskCombo;
+GtkListStore *PlayListNameMaskModel;
+GtkListStore *PlayListContentMaskModel;
+
+GtkWidget *LoadFilenameWindow;
+GtkWidget *LoadFileRunScanner;
+
+
+
+/**************
+ * Prototypes *
+ **************/
+
+/*
+ * Create Pixmaps, buttons...
+ */
+GtkWidget *Create_Button_With_Pixmap (guint button_type);
+GtkWidget *Create_Pixmap_Icon_With_Event_Box (const gchar *pixmap_name);
+GtkWidget *Create_Button_With_Icon_And_Label (const gchar *pixmap_name, gchar *label);
+GtkWidget *Create_Xpm_Image (const char **xpm_name);
+
+
+/*
+ * Combobox misc functions
+ */
+gboolean Add_String_To_Combo_List(GtkListStore *liststore, gchar *string);
+gchar *Get_Active_Combo_Box_Item(GtkComboBox *combo);
+
+
+/*
+ * Other
+ */
+void Entry_Changed_Disable_Object (GtkObject *widget_to_disable, GtkEditable *source_widget);
+void Insert_Only_Digit (GtkEditable *editable,const gchar *text,gint length,gint *position,gpointer data);
+gboolean Parse_Date (void);
+void Load_Genres_List_To_UI (void);
+void Load_Track_List_To_UI (void);
+void Init_Character_Translation_Table (void);
+void Init_Custom_Icons (void);
+gchar *Check_If_Executable_Exists (const gchar *program);
+
+// Mouse cursor
+void Init_Mouse_Cursor (void);
+void Destroy_Mouse_Cursor (void);
+void Set_Busy_Cursor (void);
+void Set_Unbusy_Cursor (void);
+
+// Run Audio Player
+void Run_Audio_Player_Using_File_List (GList *etfilelist);
+void Run_Audio_Player_Using_Directory (void);
+void Run_Audio_Player_Using_Selection (void);
+void Run_Audio_Player_Using_Browser_Artist_List (void);
+void Run_Audio_Player_Using_Browser_Album_List (void);
+
+gchar *Convert_Size (gfloat size);
+gchar *Convert_Size_1 (gfloat size);
+gchar *Convert_Duration (gulong duration);
+
+gulong Get_File_Size (gchar *filename);
+
+void Strip_String (gchar *string);
+gint Combo_Alphabetic_Sort (GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer data);
+
+void File_Selection_Window_For_File (GtkWidget *entry);
+void File_Selection_Window_For_Directory (GtkWidget *entry);
+
+// Playlist window
+void Open_Write_Playlist_Window (void);
+void Write_Playlist_Window_Apply_Changes (void);
+
+// Search file window
+void Open_Search_File_Window (void);
+void Search_File_Window_Apply_Changes (void);
+
+// Load filenames window
+void Open_Load_Filename_Window (void);
+void Load_Filename_Window_Apply_Changes (void);
+
+
+#endif /* __MISC_H__ */
diff --git a/src/monkeyaudio_header.c b/src/monkeyaudio_header.c
new file mode 100755
index 0000000..e6cf0ab
--- /dev/null
+++ b/src/monkeyaudio_header.c
@@ -0,0 +1,122 @@
+/* monkeyaudio_header.c */
+/*
+ * EasyTAG - Tag editor for MP3, Ogg Vorbis and MPC files
+ * Copyright (C) 2001-2003 Jerome Couderc <easytag@gmail.com>
+ * Copyright (C) 2002-2003 Artur Polaczyñski <artii@o2.pl>
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+
+#include <gtk/gtk.h>
+#include <glib/gi18n-lib.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "easytag.h"
+#include "et_core.h"
+#include "misc.h"
+#include "setting.h"
+#include "charset.h"
+#include "monkeyaudio_header.h"
+#include "libapetag/info_mac.h"
+
+
+/***************
+ * Header info *
+ ***************/
+
+gboolean Mac_Header_Read_File_Info(gchar *filename, ET_File_Info *ETFileInfo)
+{
+ StreamInfoMac Info;
+
+ if (info_mac_read(filename, &Info))
+ {
+ gchar *filename_utf8 = filename_to_display(filename);
+ fprintf(stderr,"MAC header not found for file '%s'\n", filename_utf8);
+ g_free(filename_utf8);
+ return FALSE;
+ }
+ ETFileInfo->mpc_profile = g_strdup(Info.CompresionName);
+ ETFileInfo->version = Info.Version;
+ ETFileInfo->bitrate = Info.Bitrate/1000.0;
+ ETFileInfo->samplerate = Info.SampleFreq;
+ ETFileInfo->mode = Info.Channels;
+ ETFileInfo->size = Info.FileSize;
+ ETFileInfo->duration = Info.Duration/1000;
+
+ return TRUE;
+}
+
+
+
+gboolean Mac_Header_Display_File_Info_To_UI (gchar *filename_utf8, ET_File_Info *ETFileInfo)
+{
+ gchar *text;
+ gchar *time = NULL;
+ gchar *time1 = NULL;
+ gchar *size = NULL;
+ gchar *size1 = NULL;
+
+ /* Mode changed to profile name */
+ text = g_strdup_printf(_("Profile:"));
+ gtk_label_set_text(GTK_LABEL(ModeLabel),text);
+ g_free(text);
+ text = g_strdup_printf("%s",ETFileInfo->mpc_profile);
+ gtk_label_set_text(GTK_LABEL(ModeValueLabel),text);
+ g_free(text);
+
+ /* Bitrate */
+ text = g_strdup_printf(_("%d kb/s"),ETFileInfo->bitrate);
+ gtk_label_set_text(GTK_LABEL(BitrateValueLabel),text);
+ g_free(text);
+
+ /* Samplerate */
+ text = g_strdup_printf(_("%d Hz"),ETFileInfo->samplerate);
+ gtk_label_set_text(GTK_LABEL(SampleRateValueLabel),text);
+ g_free(text);
+
+ /* Version changed to encoder version */
+ text = g_strdup_printf(_("Encoder:"));
+ gtk_label_set_text(GTK_LABEL(VersionLabel),text);
+ g_free(text);
+ text = g_strdup_printf("%i.%i",ETFileInfo->version/1000,ETFileInfo->version%1000);
+ gtk_label_set_text(GTK_LABEL(VersionValueLabel),text);
+ g_free(text);
+
+ /* Size */
+ size = Convert_Size(ETFileInfo->size);
+ size1 = Convert_Size(ETCore->ETFileDisplayedList_TotalSize);
+ text = g_strdup_printf("%s (%s)",size,size1);
+ gtk_label_set_text(GTK_LABEL(SizeValueLabel),text);
+ g_free(size);
+ g_free(size1);
+ g_free(text);
+
+ /* Duration */
+ time = Convert_Duration(ETFileInfo->duration);
+ time1 = Convert_Duration(ETCore->ETFileDisplayedList_TotalDuration);
+ text = g_strdup_printf("%s (%s)",time,time1);
+ gtk_label_set_text(GTK_LABEL(DurationValueLabel),text);
+ g_free(time);
+ g_free(time1);
+ g_free(text);
+
+ return TRUE;
+}
diff --git a/src/monkeyaudio_header.h b/src/monkeyaudio_header.h
new file mode 100755
index 0000000..a717472
--- /dev/null
+++ b/src/monkeyaudio_header.h
@@ -0,0 +1,42 @@
+/* monkeyaudio_header.h - 16 IV 2003 */
+/*
+ * EasyTAG - Tag editor for MP3 and Ogg Vorbis files
+ * Copyright (C) 2000-2003 Jerome Couderc <easytag@gmail.com>
+ * Copyright (C) 2002-2003 Artur Polaczyñski <artii@o2.pl>
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef __MONKEYAUDIO_HEADER_H__
+#define __MONKEYAUDIO_HEADER_H__
+
+
+#include "et_core.h"
+
+/****************
+ * Declarations *
+ ****************/
+
+
+/**************
+ * Prototypes *
+ **************/
+
+gboolean Mac_Header_Read_File_Info(gchar *filename, ET_File_Info *ETFileInfo);
+gboolean Mac_Header_Display_File_Info_To_UI (gchar *filename, ET_File_Info *ETFileInfo);
+
+
+#endif /* __MONKAYAUDIO_HEADER_H__ */
diff --git a/src/mp4_header.c b/src/mp4_header.c
new file mode 100755
index 0000000..0ba1449
--- /dev/null
+++ b/src/mp4_header.c
@@ -0,0 +1,315 @@
+/* mp4_header.c - 2005/02/05 */
+/*
+ * EasyTAG - Tag editor for MP3 and Ogg Vorbis files
+ * Copyright (C) 2000-2003 Jerome Couderc <easytag@gmail.com>
+ * Copyright (C) 2005 Stewart Whitman <swhitman@cox.net>
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h> // For definition of ENABLE_MP4
+
+#ifdef ENABLE_MP4
+
+#include <gtk/gtk.h>
+#include <glib/gi18n-lib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "mp4_header.h"
+#include "easytag.h"
+#include "et_core.h"
+#include "log.h"
+#include "misc.h"
+#include "charset.h"
+
+/* These undefs are because the mpeg4ip library contains a gnu config file in it's .h file */
+#undef PACKAGE_BUGREPORT
+#undef PACKAGE_NAME
+#undef PACKAGE_STRING
+#undef PACKAGE_TARNAME
+#undef PACKAGE_VERSION
+#include <mp4.h>
+
+
+/****************
+ * Declarations *
+ ****************/
+
+static const struct
+{
+ uint8_t profile;
+ const char *format;
+ const char *subformat;
+} MP4AudioProfileToName[] = {
+ { MP4_MPEG4_AAC_MAIN_AUDIO_TYPE, "MPEG", "4, AAC main", },
+ { MP4_MPEG4_AAC_LC_AUDIO_TYPE, "MPEG", "4, AAC LC", },
+ { MP4_MPEG4_AAC_SSR_AUDIO_TYPE, "MPEG", "4, AAC SSR", },
+ { MP4_MPEG4_AAC_LTP_AUDIO_TYPE, "MPEG", "4, AAC LTP", },
+ { MP4_MPEG4_AAC_HE_AUDIO_TYPE, "MPEG", "4, AAC HE", },
+ { MP4_MPEG4_AAC_SCALABLE_AUDIO_TYPE, "MPEG", "4, AAC Scalable", },
+ { 7, "MPEG", "4, TwinVQ", },
+ { MP4_MPEG4_CELP_AUDIO_TYPE, "MPEG", "4, CELP", },
+ { MP4_MPEG4_HVXC_AUDIO_TYPE, "MPEG", "4, HVXC", },
+ // 10, 11 unused
+ { MP4_MPEG4_TTSI_AUDIO_TYPE, "MPEG", "4, TTSI", },
+ { MP4_MPEG4_MAIN_SYNTHETIC_AUDIO_TYPE, "MPEG", "4, Main Synthetic", },
+ { MP4_MPEG4_WAVETABLE_AUDIO_TYPE, "MPEG", "4, Wavetable Syn", },
+ { MP4_MPEG4_MIDI_AUDIO_TYPE, "MPEG", "4, General MIDI", },
+ { MP4_MPEG4_ALGORITHMIC_FX_AUDIO_TYPE, "MPEG", "4, Algo Syn and Audio FX", },
+ { 17, "MPEG", "4, ER AAC LC", },
+ // 18 unused
+ { 19, "MPEG", "4, ER AAC LTP", },
+ { 20, "MPEG", "4, ER AAC Scalable", },
+ { 21, "MPEG", "4, ER TwinVQ", },
+ { 22, "MPEG", "4, ER BSAC", },
+ { 23, "MPEG", "4, ER ACC LD", },
+ { 24, "MPEG", "4, ER CELP", },
+ { 25, "MPEG", "4, ER HVXC", },
+ { 26, "MPEG", "4, ER HILN", },
+ { 27, "MPEG", "4, ER Parametric", },
+};
+
+static const struct
+{
+ uint8_t profile;
+ const char *format;
+ const char *subformat;
+} AudioProfileToName[] = {
+ { MP4_MPEG2_AAC_MAIN_AUDIO_TYPE, "MPEG", "2, AAC Main" },
+ { MP4_MPEG2_AAC_LC_AUDIO_TYPE, "MPEG", "2, AAC LC" },
+ { MP4_MPEG2_AAC_SSR_AUDIO_TYPE, "MPEG", "2, AAC SSR" },
+ { MP4_MPEG2_AUDIO_TYPE, "MPEG", "2, Audio (13818-3)" },
+ { MP4_MPEG1_AUDIO_TYPE, "MPEG", "1, Audio (11172-3)" },
+ // mpeg4ip's private definitions
+ { MP4_PCM16_LITTLE_ENDIAN_AUDIO_TYPE, "PCM16", "Little Endian" },
+ { MP4_VORBIS_AUDIO_TYPE, "Vorbis", "" },
+ { MP4_ALAW_AUDIO_TYPE, "G.711", "aLaw" },
+ { MP4_ULAW_AUDIO_TYPE, "G.711", "uLaw" },
+ { MP4_G723_AUDIO_TYPE, "G.723.1", "" },
+ { MP4_PCM16_BIG_ENDIAN_AUDIO_TYPE, "PCM16", "Big Endian" },
+};
+
+#define NUMBER_OF(A) (sizeof(A) / sizeof(A[0]))
+
+
+/**************
+ * Prototypes *
+ **************/
+
+
+/*************
+ * Functions *
+ *************/
+
+/*
+ * getType:
+ *
+ * Returns a format/sub-format information. Taken from mp4.h/mp4info.
+ */
+static void getType(MP4FileHandle file, MP4TrackId trackId, const char **format, const char **subformat )
+{
+ unsigned i;
+ const char *media_data_name = MP4GetTrackMediaDataName(file, trackId);
+
+ *format = _("Audio");
+ *subformat = _("Unknown");
+
+ if (media_data_name == NULL)
+ {
+ ;
+ } else if (strcasecmp(media_data_name, "samr") == 0)
+ {
+ *subformat = "AMR";
+ } else if (strcasecmp(media_data_name, "sawb") == 0)
+ {
+ *subformat = "AMR-WB";
+ } else if (strcasecmp(media_data_name, "mp4a") == 0)
+ {
+ u_int8_t type = MP4GetTrackEsdsObjectTypeId(file, trackId);
+
+ if( type == MP4_MPEG4_AUDIO_TYPE )
+ {
+ u_int8_t* pAacConfig = NULL;
+ u_int32_t aacConfigLength;
+
+ MP4GetTrackESConfiguration(file, trackId, &pAacConfig, &aacConfigLength);
+
+ if (pAacConfig != NULL)
+ {
+ type = aacConfigLength >= 2 ? ((pAacConfig[0] >> 3) & 0x1f) : 0;
+ free(pAacConfig);
+
+ for (i = 0; i < NUMBER_OF(MP4AudioProfileToName); i++)
+ {
+ if (type == MP4AudioProfileToName[i].profile)
+ {
+ *format = MP4AudioProfileToName[i].format;
+ *subformat = MP4AudioProfileToName[i].subformat;
+ return;
+ }
+ }
+ }
+ *format = "MPEG";
+ *subformat = "4, Unknown";
+ } else
+ {
+ for (i = 0; i < NUMBER_OF(AudioProfileToName); i++)
+ {
+ if (type == AudioProfileToName[i].profile)
+ {
+ *format = AudioProfileToName[i].format;
+ *subformat = AudioProfileToName[i].subformat;
+ return;
+ }
+ }
+ }
+ } else
+ {
+ *subformat = media_data_name;
+ }
+}
+
+
+/*
+ * Mp4_Header_Read_File_Info:
+ *
+ * Get header info into the ETFileInfo structure
+ */
+gboolean Mp4_Header_Read_File_Info (gchar *filename, ET_File_Info *ETFileInfo)
+{
+ MP4FileHandle file;
+ MP4TrackId trackId = 1;
+ //const char* trackType;
+ const char *format, *subformat;
+
+ if (!filename || !ETFileInfo)
+ return FALSE;
+
+ /* Get size of file */
+ ETFileInfo->size = Get_File_Size(filename);
+
+ if ((file = MP4Read(filename, 0)) == MP4_INVALID_FILE_HANDLE )
+ {
+ gchar *filename_utf8 = filename_to_display(filename);
+ //g_print(_("ERROR while opening file: '%s' (%s)."),filename_utf8,g_strerror(errno));
+ Log_Print(_("ERROR while opening file: '%s' (%s)."),filename_utf8,_("MP4 format invalid"));
+ g_free(filename_utf8);
+ return FALSE;
+ }
+
+ /* Check for audio track */
+ if( MP4GetNumberOfTracks(file,MP4_AUDIO_TRACK_TYPE,0) < 1 )
+ {
+ gchar *filename_utf8 = filename_to_display(filename);
+ Log_Print(_("ERROR while opening file: '%s' (%s)."),filename_utf8,("Contains no audio track"));
+ MP4Close(file);
+ g_free(filename_utf8);
+ return FALSE;
+ }
+
+ /* Get the first track id (index 0) */
+ trackId = MP4FindTrackId(file, 0, MP4_AUDIO_TRACK_TYPE, 0);
+
+ /* Get format/subformat */
+ {
+ getType( file, trackId, &format, &subformat );
+ ETFileInfo->mpc_version = g_strdup( format );
+ ETFileInfo->mpc_profile = g_strdup( subformat );
+ }
+
+ ETFileInfo->version = 4;
+ ETFileInfo->mpeg25 = 0;
+ ETFileInfo->layer = 14;
+
+ ETFileInfo->variable_bitrate = TRUE;
+ ETFileInfo->bitrate = MP4GetTrackBitRate(file, trackId) / 1000;
+ ETFileInfo->samplerate = MP4GetTrackTimeScale(file, trackId);
+ ETFileInfo->mode = MP4GetTrackAudioChannels(file, trackId);
+ ETFileInfo->duration = MP4ConvertFromTrackDuration(file, trackId, MP4GetTrackDuration(file, trackId), MP4_SECS_TIME_SCALE);
+
+ MP4Close(file);
+ return TRUE;
+}
+
+
+
+/*
+ * Mp4_Header_Display_File_Info_To_UI:
+ *
+ * Display header info in the main window
+ */
+gboolean Mp4_Header_Display_File_Info_To_UI(gchar *filename, ET_File_Info *ETFileInfo)
+{
+ gchar *text;
+ gchar *time = NULL;
+ gchar *time1 = NULL;
+ gchar *size = NULL;
+ gchar *size1 = NULL;
+
+ /* MPEG, Layer versions */
+ gtk_label_set_text(GTK_LABEL(VersionLabel),ETFileInfo->mpc_version);
+ //text = g_strdup_printf("%d",ETFileInfo->version);
+ gtk_label_set_text(GTK_LABEL(VersionValueLabel),ETFileInfo->mpc_profile);
+ //g_free(text);
+
+ /* Bitrate */
+ if (ETFileInfo->variable_bitrate)
+ text = g_strdup_printf(_("~%d kb/s"),ETFileInfo->bitrate);
+ else
+ text = g_strdup_printf(_("%d kb/s"),ETFileInfo->bitrate);
+ gtk_label_set_text(GTK_LABEL(BitrateValueLabel),text);
+ g_free(text);
+
+ /* Samplerate */
+ text = g_strdup_printf(_("%d Hz"),ETFileInfo->samplerate);
+ gtk_label_set_text(GTK_LABEL(SampleRateValueLabel),text);
+ g_free(text);
+
+ /* Mode */
+ /* mpeg4ip library seems to always return -1 */
+ gtk_label_set_text(GTK_LABEL(ModeLabel),_("Channels:"));
+ if( ETFileInfo->mode == -1 )
+ text = g_strdup_printf("Unknown");
+ else
+ text = g_strdup_printf("%d",ETFileInfo->mode);
+ gtk_label_set_text(GTK_LABEL(ModeValueLabel),text);
+ g_free(text);
+
+ /* Size */
+ size = Convert_Size(ETFileInfo->size);
+ size1 = Convert_Size(ETCore->ETFileDisplayedList_TotalSize);
+ text = g_strdup_printf("%s (%s)",size,size1);
+ gtk_label_set_text(GTK_LABEL(SizeValueLabel),text);
+ if (size) g_free(size);
+ if (size1) g_free(size1);
+ g_free(text);
+
+ /* Duration */
+ time = Convert_Duration(ETFileInfo->duration);
+ time1 = Convert_Duration(ETCore->ETFileDisplayedList_TotalDuration);
+ text = g_strdup_printf("%s (%s)",time,time1);
+ gtk_label_set_text(GTK_LABEL(DurationValueLabel),text);
+ if (time) g_free(time);
+ if (time1) g_free(time1);
+ g_free(text);
+
+ return TRUE;
+}
+
+#endif
diff --git a/src/mp4_header.h b/src/mp4_header.h
new file mode 100755
index 0000000..2e2f2a1
--- /dev/null
+++ b/src/mp4_header.h
@@ -0,0 +1,42 @@
+/* mp4_header.h - 2005/02/15 */
+/*
+ * EasyTAG - Tag editor for MP3 and Ogg Vorbis files
+ * Copyright (C) 2000-2005 Jerome Couderc <easytag@gmail.com>
+ * Copyright (C) 2005 Stewart Whitman <swhitman@cox.net>
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef __MP4_HEADER_H__
+#define __MP4_HEADER_H__
+
+
+#include "et_core.h"
+
+/****************
+ * Declarations *
+ ****************/
+
+
+/**************
+ * Prototypes *
+ **************/
+
+gboolean Mp4_Header_Read_File_Info (gchar *filename, ET_File_Info *ETFileInfo);
+gboolean Mp4_Header_Display_File_Info_To_UI (gchar *filename, ET_File_Info *ETFileInfo);
+
+
+#endif /* __MP4_HEADER_H__ */
diff --git a/src/mp4_tag.c b/src/mp4_tag.c
new file mode 100755
index 0000000..86b4cff
--- /dev/null
+++ b/src/mp4_tag.c
@@ -0,0 +1,429 @@
+/* mp4_tag.c - 2005/08/06 */
+/*
+ * EasyTAG - Tag editor for MP3 and Ogg Vorbis files
+ * Copyright (C) 2001-2005 Jerome Couderc <easytag@gmail.com>
+ * Copyright (C) 2005 Michael Ihde <mike.ihde@randomwalking.com>
+ * Copyright (C) 2005 Stewart Whitman <swhitman@cox.net>
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* Portions of this code was borrowed from the MPEG4IP tools project */
+#include <config.h> // For definition of ENABLE_MP4
+
+#ifdef ENABLE_MP4
+
+#include <gtk/gtk.h>
+#include <glib/gi18n-lib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "mp4_tag.h"
+#include "picture.h"
+#include "easytag.h"
+#include "setting.h"
+#include "log.h"
+#include "misc.h"
+#include "et_core.h"
+#include "charset.h"
+
+/* These undefs are because the mpeg4ip library contains a gnu config file in it's .h file */
+#undef PACKAGE_BUGREPORT
+#undef PACKAGE_NAME
+#undef PACKAGE_STRING
+#undef PACKAGE_TARNAME
+#undef PACKAGE_VERSION
+#include <mp4.h>
+
+
+/****************
+ * Declarations *
+ ****************/
+
+
+/**************
+ * Prototypes *
+ **************/
+
+
+/*************
+ * Functions *
+ *************/
+
+/*
+ * Mp4_Tag_Read_File_Tag:
+ *
+ * Read tag data into an Mp4 file.
+ *
+ * Note:
+ * - for string fields, //if field is found but contains no info (strlen(str)==0), we don't read it
+ * - for track numbers, if they are zero, then we don't read it
+ */
+gboolean Mp4tag_Read_File_Tag (gchar *filename, File_Tag *FileTag)
+{
+ FILE *file;
+ MP4FileHandle mp4file = NULL;
+ uint16_t track, track_total;
+ uint16_t disk, disktotal;
+ u_int8_t *coverArt;
+ u_int32_t coverSize;
+
+ if (!filename || !FileTag)
+ return FALSE;
+
+ if ( (file=fopen(filename,"r"))==NULL )
+ {
+ gchar *filename_utf8 = filename_to_display(filename);
+ Log_Print(_("ERROR while opening file: '%s' (%s)."),filename_utf8,g_strerror(errno));
+ g_free(filename_utf8);
+ return FALSE;
+ }
+ fclose(file); // We close it cause mp4 opens/closes file itself
+
+ /* Get data from tag */
+ mp4file = MP4Read(filename, 0);
+ if (mp4file == MP4_INVALID_FILE_HANDLE)
+ {
+ gchar *filename_utf8 = filename_to_display(filename);
+ Log_Print(_("ERROR while opening file: '%s' (%s)."),filename_utf8,_("MP4 format invalid"));
+ g_free(filename_utf8);
+ return FALSE;
+ }
+
+ /* TODO Add error detection */
+
+ /*********
+ * Title *
+ *********/
+ MP4GetMetadataName(mp4file, &FileTag->title);
+
+ /**********
+ * Artist *
+ **********/
+ MP4GetMetadataArtist(mp4file, &FileTag->artist);
+
+ /*********
+ * Album *
+ *********/
+ MP4GetMetadataAlbum(mp4file, &FileTag->album);
+
+ /**********************
+ * Disk / Total Disks *
+ **********************/
+ if (MP4GetMetadataDisk(mp4file, &disk, &disktotal))
+ {
+ if (disk != 0 && disktotal != 0)
+ FileTag->disc_number = g_strdup_printf("%d/%d",(gint)disk,(gint)disktotal);
+ else if (disk != 0)
+ FileTag->disc_number = g_strdup_printf("%d",(gint)disk);
+ else if (disktotal != 0)
+ FileTag->disc_number = g_strdup_printf("/%d",(gint)disktotal);
+ //if (disktotal != 0)
+ // FileTag->disk_number_total = g_strdup_printf("%d",(gint)disktotal);
+ }
+
+ /********
+ * Year *
+ ********/
+ MP4GetMetadataYear(mp4file, &FileTag->year);
+
+ /*************************
+ * Track and Total Track *
+ *************************/
+ if (MP4GetMetadataTrack(mp4file, &track, &track_total))
+ {
+ if (track != 0)
+ FileTag->track = g_strdup_printf("%.*d",NUMBER_TRACK_FORMATED_SPIN_BUTTON,(gint)track); // Just to have numbers like this : '01', '05', '12', ...
+ if (track_total != 0)
+ FileTag->track_total = g_strdup_printf("%.*d",NUMBER_TRACK_FORMATED_SPIN_BUTTON,(gint)track_total); // Just to have numbers like this : '01', '05', '12', ...
+ }
+
+ /*********
+ * Genre *
+ *********/
+ MP4GetMetadataGenre(mp4file, &FileTag->genre);
+
+ /***********
+ * Comment *
+ ***********/
+ MP4GetMetadataComment(mp4file, &FileTag->comment);
+
+ /**********************
+ * Composer or Writer *
+ **********************/
+ MP4GetMetadataWriter(mp4file, &FileTag->composer);
+
+ /*****************
+ * Encoding Tool *
+ *****************/
+ MP4GetMetadataTool(mp4file, &FileTag->encoded_by);
+
+ /* Unimplemented
+
+ Tempo / BPM
+ MP4GetMetadataTempo(file, &string)
+ */
+
+ /***********
+ * Picture *
+ ***********/
+ // There is only one picture!
+ if ( MP4GetMetadataCoverArt( mp4file, &coverArt, &coverSize ) )
+ {
+ Picture *pic = Picture_Allocate();
+ pic->size = coverSize;
+ pic->data = coverArt;
+ pic->type = PICTURE_TYPE_FRONT_COVER;
+ pic->description = NULL;
+
+ FileTag->picture = pic;
+ }
+
+
+ /* Free allocated data */
+ MP4Close(mp4file);
+
+ return TRUE;
+}
+
+
+/*
+ * Mp4_Tag_Write_File_Tag:
+ *
+ * Write tag data into an Mp4 file.
+ *
+ * Note:
+ * - for track numbers, we write 0's if one or the other is blank
+ */
+gboolean Mp4tag_Write_File_Tag (ET_File *ETFile)
+{
+ File_Tag *FileTag;
+ gchar *filename;
+ gchar *filename_utf8;
+ FILE *file;
+ MP4FileHandle mp4file = NULL;
+ gint error = 0;
+
+ if (!ETFile || !ETFile->FileTag)
+ return FALSE;
+
+ FileTag = (File_Tag *)ETFile->FileTag->data;
+ filename = ((File_Name *)ETFile->FileNameCur->data)->value;
+ filename_utf8 = ((File_Name *)ETFile->FileNameCur->data)->value_utf8;
+
+ /* Test to know if we can write into the file */
+ if ( (file=fopen(filename,"r+"))==NULL )
+ {
+ Log_Print(_("ERROR while opening file: '%s' (%s)."),filename_utf8,g_strerror(errno));
+ return FALSE;
+ }
+ fclose(file);
+
+ /* Open file for writing */
+ mp4file = MP4Modify(filename,0,0);
+ if (mp4file == MP4_INVALID_FILE_HANDLE)
+ {
+ Log_Print(_("ERROR while opening file: '%s' (%s)."),filename_utf8,_("MP4 format invalid"));
+ return FALSE;
+ }
+
+ /*********
+ * Title *
+ *********/
+ if (FileTag->title && g_utf8_strlen(FileTag->title, -1) > 0)
+ {
+ MP4SetMetadataName(mp4file, FileTag->title);
+ }else
+ {
+ //MP4DeleteMetadataName(mp4file); // Not available on mpeg4ip-1.2 (only in 1.3)
+ MP4SetMetadataName(mp4file, "");
+ }
+
+ /**********
+ * Artist *
+ **********/
+ if (FileTag->artist && g_utf8_strlen(FileTag->artist, -1) > 0)
+ {
+ MP4SetMetadataArtist(mp4file, FileTag->artist);
+ }else
+ {
+ //MP4DeleteMetadataArtist(mp4file);
+ MP4SetMetadataArtist(mp4file, "");
+ }
+
+ /*********
+ * Album *
+ *********/
+ if (FileTag->album && g_utf8_strlen(FileTag->album, -1) > 0)
+ {
+ MP4SetMetadataAlbum(mp4file, FileTag->album);
+ }else
+ {
+ //MP4DeleteMetadataAlbum(mp4file);
+ MP4SetMetadataAlbum(mp4file, "");
+ }
+
+ /**********************
+ * Disk / Total Disks *
+ **********************/
+ if (FileTag->disc_number && g_utf8_strlen(FileTag->disc_number, -1) > 0)
+ //|| FileTag->disc_number_total && g_utf8_strlen(FileTag->disc_number_total, -1) > 0)
+ {
+ uint16_t disk = 0;
+ uint16_t disktotal = 0;
+
+ /* At the present time, we manage only disk number like '1' or '1/2', we
+ * don't use disk number total... so here we try to decompose */
+ if (FileTag->disc_number)
+ {
+ gchar *dn_tmp = g_strdup(FileTag->disc_number);
+ gchar *tmp = strchr(dn_tmp,'/');
+ if (tmp)
+ {
+ // A disc_number_total was entered
+ if ( (tmp+1) && atoi(tmp+1) )
+ disktotal = atoi(tmp+1);
+
+ // Fill disc_number
+ *tmp = '\0';
+ disk = atoi(dn_tmp);
+ }else
+ {
+ disk = atoi(FileTag->disc_number);
+ }
+ g_free(dn_tmp);
+ }
+ /*if (FileTag->disc_number)
+ disk = atoi(FileTag->disc_number);
+ if (FileTag->disc_number_total)
+ disktotal = atoi(FileTag->disc_number_total);
+ */
+ MP4SetMetadataDisk(mp4file, disk, disktotal);
+ }else
+ {
+ //MP4DeleteMetadataDisk(mp4file);
+ MP4SetMetadataDisk(mp4file, 0, 0);
+ }
+
+ /********
+ * Year *
+ ********/
+ if (FileTag->year && g_utf8_strlen(FileTag->year, -1) > 0)
+ {
+ MP4SetMetadataYear(mp4file, FileTag->year);
+ }else
+ {
+ //MP4DeleteMetadataYear(mp4file);
+ MP4SetMetadataYear(mp4file, "");
+ }
+
+ /*************************
+ * Track and Total Track *
+ *************************/
+ if ( (FileTag->track && g_utf8_strlen(FileTag->track, -1) > 0)
+ || (FileTag->track_total && g_utf8_strlen(FileTag->track_total, -1) > 0) )
+ {
+ uint16_t track = 0;
+ uint16_t track_total = 0;
+ if (FileTag->track)
+ track = atoi(FileTag->track);
+ if (FileTag->track_total)
+ track_total = atoi(FileTag->track_total);
+ MP4SetMetadataTrack(mp4file, track, track_total);
+ }else
+ {
+ //MP4DeleteMetadataTrack(mp4file);
+ MP4SetMetadataTrack(mp4file, 0, 0);
+ }
+
+ /*********
+ * Genre *
+ *********/
+ if (FileTag->genre && g_utf8_strlen(FileTag->genre, -1) > 0 )
+ {
+ MP4SetMetadataGenre(mp4file, FileTag->genre);
+ }else
+ {
+ //MP4DeleteMetadataGenre(mp4file);
+ MP4SetMetadataGenre(mp4file, "");
+ }
+
+ /***********
+ * Comment *
+ ***********/
+ if (FileTag->comment && g_utf8_strlen(FileTag->comment, -1) > 0)
+ {
+ MP4SetMetadataComment(mp4file, FileTag->comment);
+ }else
+ {
+ //MP4DeleteMetadataComment(mp4file);
+ MP4SetMetadataComment(mp4file, "");
+ }
+
+ /**********************
+ * Composer or Writer *
+ **********************/
+ if (FileTag->composer && g_utf8_strlen(FileTag->composer, -1) > 0)
+ {
+ MP4SetMetadataWriter(mp4file, FileTag->composer);
+ }else
+ {
+ //MP4DeleteMetadataWriter(mp4file);
+ MP4SetMetadataWriter(mp4file, "");
+ }
+
+ /*****************
+ * Encoding Tool *
+ *****************/
+ if (FileTag->encoded_by && g_utf8_strlen(FileTag->encoded_by, -1) > 0)
+ {
+ MP4SetMetadataTool(mp4file, FileTag->encoded_by);
+ }else
+ {
+ //MP4DeleteMetadataTool(mp4file);
+ MP4SetMetadataTool(mp4file, "");
+ }
+
+ /***********
+ * Picture *
+ ***********/
+ {
+ Picture *pic;
+
+ //MP4DeleteMetadataCoverArt(mp4file);
+ MP4SetMetadataCoverArt(mp4file, NULL, 0);
+ for( pic = FileTag->picture; pic; pic = pic->next )
+ {
+ if( pic->type == PICTURE_TYPE_FRONT_COVER )
+ {
+ MP4SetMetadataCoverArt(mp4file, pic->data, pic->size);
+ }
+ }
+ }
+
+
+ MP4Close(mp4file);
+
+ if (error) return FALSE;
+ else return TRUE;
+}
+
+
+#endif /* ENABLE_MP4 */
diff --git a/src/mp4_tag.h b/src/mp4_tag.h
new file mode 100755
index 0000000..b25d3a2
--- /dev/null
+++ b/src/mp4_tag.h
@@ -0,0 +1,41 @@
+/* mp4_tag.h - 2005/08/06 */
+/*
+ * EasyTAG - Tag editor for MP3 and Ogg Vorbis files
+ * Copyright (C) 2001-2005 Jerome Couderc <easytag@gmail.com>
+ * Copyright (C) 2005 Michael Ihde <mike.ihde@randomwalking.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef __MP4_TAG_H__
+#define __MP4_TAG_H__
+
+
+#include "et_core.h"
+
+/****************
+ * Declarations *
+ ****************/
+
+
+
+/**************
+ * Prototypes *
+ **************/
+gboolean Mp4tag_Read_File_Tag (gchar *filename, File_Tag *FileTag);
+gboolean Mp4tag_Write_File_Tag (ET_File *ETFile);
+
+#endif /* __MP4_TAG_H__ */
diff --git a/src/mpeg_header.c b/src/mpeg_header.c
new file mode 100755
index 0000000..1c9400d
--- /dev/null
+++ b/src/mpeg_header.c
@@ -0,0 +1,401 @@
+/* mpeg_header.c - 2000/05/12 */
+/*
+ * EasyTAG - Tag editor for MP3 and Ogg Vorbis files
+ * Copyright (C) 2000-2003 Jerome Couderc <easytag@gmail.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+
+#include <gtk/gtk.h>
+#include <glib/gi18n-lib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "mpeg_header.h"
+#include "easytag.h"
+#include "et_core.h"
+#include "log.h"
+#include "misc.h"
+#include "charset.h"
+
+// Set to :
+// - 1 to use ID3lib for reading headers
+// - 0 to use mpeg123 for reading headers
+#define USE_ID3LIB_4_HEADER 0
+
+#if USE_ID3LIB_4_HEADER
+# include <id3.h>
+# include "id3lib/id3_bugfix.h"
+#else
+# include "libmpg123/mpg123.h"
+#endif
+
+
+
+
+/****************
+ * Declarations *
+ ****************/
+gchar *layer_names[3] =
+{
+ "I", /* Layer 1 */
+ "II", /* Layer 2 */
+ "III" /* Layer 3 */
+};
+
+
+
+/**************
+ * Prototypes *
+ **************/
+static gchar* channel_mode_name(int mode);
+
+
+
+/*************
+ * Functions *
+ *************/
+
+static gchar* channel_mode_name(int mode)
+{
+ static const gchar *channel_mode[] =
+ {
+ N_("Stereo"),
+ N_("Joint stereo"),
+ N_("Dual channel"),
+ N_("Single channel")
+ };
+ if (mode < 0 || mode > 3)
+ return "";
+ return _(channel_mode[mode]);
+}
+
+
+
+/*
+ * Read infos into header of first frame
+ */
+gboolean Mpeg_Header_Read_File_Info (gchar *filename, ET_File_Info *ETFileInfo)
+{
+#if (!USE_ID3LIB_4_HEADER)
+ FILE *file;
+ gulong filesize;
+
+
+ if (!filename || !ETFileInfo)
+ return FALSE;
+
+ /* Get size of file */
+ filesize = Get_File_Size(filename);
+ ETFileInfo->size = filesize;
+
+
+ /*
+ * This part was taken from XMMS
+ */
+ if ((file = fopen(filename, "rb")) != NULL)
+ {
+ guint32 head;
+ unsigned char tmp[4];
+ struct frame frm;
+ gboolean id3_found = FALSE;
+
+ if (fread(tmp, 1, 4, file) != 4)
+ {
+ fclose(file);
+ return FALSE;
+ }
+
+ // Skip data of the ID3v2.x tag (It may contain data similar to mpeg frame
+ // as, for example, in the id3 APIC frame) (patch from Artur Polaczynski)
+ if (tmp[0] == 'I' && tmp[1] == 'D' && tmp[2] == '3' && tmp[3] < 0xFF)
+ {
+ // ID3v2 tag skipeer $49 44 33 yy yy xx zz zz zz zz [zz size]
+ long id3v2size;
+ fseek(file, 2, SEEK_CUR); // Size is 6-9 position
+ if (fread(tmp, 1, 4, file) != 4) // Read bytes of tag size
+ {
+ fclose(file);
+ return FALSE;
+ }
+ id3v2size = 10 + ( (long)(tmp[3]) | ((long)(tmp[2]) << 7) | ((long)(tmp[1]) << 14) | ((long)(tmp[0]) << 21) );
+ fseek(file, id3v2size, SEEK_SET);
+ if (fread(tmp, 1, 4, file) != 4) // Read mpeg header
+ {
+ fclose(file);
+ return FALSE;
+ }
+ }
+
+ head = ((guint32) tmp[0] << 24) | ((guint32) tmp[1] << 16) | ((guint32) tmp[2] << 8) | (guint32) tmp[3];
+ while (!mpg123_head_check(head))
+ {
+ head <<= 8;
+ if (fread(tmp, 1, 1, file) != 1)
+ {
+ fclose(file);
+ return FALSE;
+ }
+ head |= tmp[0];
+ }
+ if (mpg123_decode_header(&frm, head))
+ {
+ guchar *buf;
+ gdouble tpf;
+ gint pos;
+ XHEADDATA xing_header;
+ guint32 num_frames;
+
+ buf = g_malloc(frm.framesize + 4);
+ fseek(file, -4, SEEK_CUR);
+ fread(buf, 1, frm.framesize + 4, file);
+ xing_header.toc = NULL;
+ tpf = mpg123_compute_tpf(&frm);
+ // MPEG and Layer version
+ ETFileInfo->mpeg25 = frm.mpeg25;
+ if (!ETFileInfo->mpeg25)
+ ETFileInfo->version = frm.lsf+1;
+ ETFileInfo->layer = frm.lay;
+ //if (ETFileInfo->mpeg25) g_print("mpeg_level: MPEG 2.5, layer %d\n",ETFileInfo->layer);
+ //else g_print("mpeg_level: MPEG %d, layer %d\n",ETFileInfo->version,ETFileInfo->layer);
+
+ pos = ftell(file);
+ fseek(file, 0, SEEK_END);
+ // Variable bitrate? + bitrate
+ if ( (ETFileInfo->variable_bitrate=mpg123_get_xing_header(&xing_header,buf)) )
+ {
+ num_frames = xing_header.frames;
+ ETFileInfo->bitrate = (gint) ((xing_header.bytes * 8) / (tpf * xing_header.frames * 1000));
+ //g_print("Bitrate: Variable,\navg. bitrate: %d kb/s\n",ETFileInfo->bitrate);
+ } else
+ {
+ num_frames = ((ftell(file) - pos - (id3_found ? 128 : 0)) / mpg123_compute_bpf(&frm)) + 1;
+ ETFileInfo->bitrate = tabsel_123[frm.lsf][frm.lay - 1][frm.bitrate_index];
+ //g_print("Bitrate: %d kb/s\n",ETFileInfo->bitrate);
+ }
+ // Samplerate
+ ETFileInfo->samplerate = mpg123_freqs[frm.sampling_frequency];
+ // Mode
+ ETFileInfo->mode = frm.mode;
+ //g_print("Samplerate: %ld Hz\n", mpg123_freqs[frm.sampling_frequency]);
+ //g_print("%s\nError protection: %s\nCopyright: %s\nOriginal: %s\nEmphasis: %s\n", channel_mode_name(frm.mode), bool_label[frm.error_protection], bool_label[frm.copyright], bool_label[frm.original], emphasis[frm.emphasis]);
+ //g_print("%d frames\nFilesize: %lu B\n", num_frames, ftell(file));
+ g_free(buf);
+ }
+
+ // Duration
+ ETFileInfo->duration = mpg123_get_song_time(file)/1000;
+ //g_print("time %s\n",Convert_Duration(ETFileInfo->duration));
+
+ fclose(file);
+ }else
+ {
+ gchar *filename_utf8 = filename_to_display(filename);
+ Log_Print(_("ERROR while opening file: '%s' (%s)."),filename_utf8,g_strerror(errno));
+ g_free(filename_utf8);
+ return FALSE;
+ }
+
+#else
+ // Needs to uncomment some #include at the beginning
+
+ /*
+ * With id3lib, the header frame couldn't be read if the file contains an ID3v2 tag with an APIC frame
+ */
+ gulong filesize;
+ ID3Tag *id3_tag = NULL; /* Tag defined by the id3lib */
+ const Mp3_Headerinfo* headerInfo = NULL;
+
+
+ if (!filename || !ETFileInfo)
+ return FALSE;
+
+ /* Get size of file */
+ filesize = Get_File_Size(filename);
+ ETFileInfo->size = filesize;
+
+ /* Get data from tag */
+ if ( (id3_tag = ID3Tag_New()) == NULL )
+ return FALSE;
+
+ /* Link the file to the tag (uses ID3TT_ID3V2 to get header if APIC is present in Tag) */
+ ID3Tag_LinkWithFlags(id3_tag,filename,ID3TT_ID3V2);
+
+ /*ID3_STRUCT(Mp3_Headerinfo)
+ {
+ Mpeg_Layers layer;
+ Mpeg_Version version;
+ MP3_BitRates bitrate;
+ Mp3_ChannelMode channelmode;
+ Mp3_ModeExt modeext;
+ Mp3_Emphasis emphasis;
+ Mp3_Crc crc;
+ uint32 vbr_bitrate; // avg bitrate from xing header
+ uint32 frequency; // samplerate
+ uint32 framesize;
+ uint32 frames; // nr of frames
+ uint32 time; // nr of seconds in song
+ bool privatebit;
+ bool copyrighted;
+ bool original;
+ };*/
+
+ if ( (headerInfo = ID3Tag_GetMp3HeaderInfo(id3_tag)) )
+ {
+ switch (headerInfo->version)
+ {
+ case MPEGVERSION_1:
+ ETFileInfo->version = 1;
+ ETFileInfo->mpeg25 = FALSE;
+ break;
+ case MPEGVERSION_2:
+ ETFileInfo->version = 2;
+ ETFileInfo->mpeg25 = FALSE;
+ break;
+ case MPEGVERSION_2_5:
+ ETFileInfo->mpeg25 = TRUE;
+ ETFileInfo->mpeg25 = FALSE;
+ break;
+ default:
+ break;
+ }
+
+ switch (headerInfo->layer)
+ {
+ case MPEGLAYER_I:
+ ETFileInfo->layer = 1;
+ break;
+ case MPEGLAYER_II:
+ ETFileInfo->layer = 2;
+ break;
+ case MPEGLAYER_III:
+ ETFileInfo->layer = 3;
+ break;
+ default:
+ break;
+ }
+
+ // Samplerate
+ ETFileInfo->samplerate = headerInfo->frequency;
+
+ // Mode -> Seems to be detected but incorrect?!
+ switch (headerInfo->modeext)
+ {
+ case MP3CHANNELMODE_STEREO:
+ ETFileInfo->mode = 0;
+ break;
+ case MP3CHANNELMODE_JOINT_STEREO:
+ ETFileInfo->mode = 1;
+ break;
+ case MP3CHANNELMODE_DUAL_CHANNEL:
+ ETFileInfo->mode = 2;
+ break;
+ case MP3CHANNELMODE_SINGLE_CHANNEL:
+ ETFileInfo->mode = 3;
+ break;
+ default:
+ break;
+ }
+
+ // Bitrate
+ if (headerInfo->vbr_bitrate <= 0)
+ {
+ ETFileInfo->variable_bitrate = FALSE;
+ ETFileInfo->bitrate = headerInfo->bitrate/1000;
+ }else
+ {
+ ETFileInfo->variable_bitrate = TRUE;
+ ETFileInfo->bitrate = headerInfo->vbr_bitrate/1000;
+ }
+
+ // Duration
+ ETFileInfo->duration = headerInfo->time;
+ }
+
+ /* Free allocated data */
+ ID3Tag_Delete(id3_tag);
+
+#endif
+
+ return TRUE;
+}
+
+
+
+/*
+ * Display header infos in the main window
+ */
+gboolean Mpeg_Header_Display_File_Info_To_UI(gchar *filename_utf8, ET_File_Info *ETFileInfo)
+{
+ gchar *text;
+ gchar *time = NULL;
+ gchar *time1 = NULL;
+ gchar *size = NULL;
+ gchar *size1 = NULL;
+ gint ln_num = sizeof(layer_names)/sizeof(layer_names[0]);
+
+
+ /* MPEG, Layer versions */
+ gtk_label_set_text(GTK_LABEL(VersionLabel),_("MPEG"));
+ ln_num = sizeof(layer_names)/sizeof(layer_names[0]); // Used to avoid problem with layer_names[]
+ if (ETFileInfo->mpeg25)
+ text = g_strdup_printf("2.5, Layer %s",(ETFileInfo->layer>=1 && ETFileInfo->layer<=ln_num)?layer_names[ETFileInfo->layer-1]:"?");
+ else
+ text = g_strdup_printf("%d, Layer %s",ETFileInfo->version,(ETFileInfo->layer>=1 && ETFileInfo->layer<=ln_num)?layer_names[ETFileInfo->layer-1]:"?");
+ gtk_label_set_text(GTK_LABEL(VersionValueLabel),text);
+ g_free(text);
+
+ /* Bitrate */
+ if (ETFileInfo->variable_bitrate)
+ text = g_strdup_printf(_("~%d kb/s"),ETFileInfo->bitrate);
+ else
+ text = g_strdup_printf(_("%d kb/s"),ETFileInfo->bitrate);
+ gtk_label_set_text(GTK_LABEL(BitrateValueLabel),text);
+ g_free(text);
+
+ /* Samplerate */
+ text = g_strdup_printf(_("%d Hz"),ETFileInfo->samplerate);
+ gtk_label_set_text(GTK_LABEL(SampleRateValueLabel),text);
+ g_free(text);
+
+ /* Mode */
+ gtk_label_set_text(GTK_LABEL(ModeLabel),_("Mode:"));
+ text = g_strdup_printf("%s",_(channel_mode_name(ETFileInfo->mode)));
+ gtk_label_set_text(GTK_LABEL(ModeValueLabel),text);
+ g_free(text);
+
+ /* Size */
+ size = Convert_Size(ETFileInfo->size);
+ size1 = Convert_Size(ETCore->ETFileDisplayedList_TotalSize);
+ text = g_strdup_printf("%s (%s)",size,size1);
+ gtk_label_set_text(GTK_LABEL(SizeValueLabel),text);
+ g_free(size);
+ g_free(size1);
+ g_free(text);
+
+ /* Duration */
+ time = Convert_Duration(ETFileInfo->duration);
+ time1 = Convert_Duration(ETCore->ETFileDisplayedList_TotalDuration);
+ text = g_strdup_printf("%s (%s)",time,time1);
+ gtk_label_set_text(GTK_LABEL(DurationValueLabel),text);
+ g_free(time);
+ g_free(time1);
+ g_free(text);
+
+ return TRUE;
+}
diff --git a/src/mpeg_header.h b/src/mpeg_header.h
new file mode 100755
index 0000000..7cb46cd
--- /dev/null
+++ b/src/mpeg_header.h
@@ -0,0 +1,41 @@
+/* mpeg_header.h - 2000/05/12 */
+/*
+ * EasyTAG - Tag editor for MP3 and Ogg Vorbis files
+ * Copyright (C) 2000-2003 Jerome Couderc <easytag@gmail.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef __MPEG_HEADER_H__
+#define __MPEG_HEADER_H__
+
+
+#include "et_core.h"
+
+/****************
+ * Declarations *
+ ****************/
+
+
+/**************
+ * Prototypes *
+ **************/
+
+gboolean Mpeg_Header_Read_File_Info (gchar *filename, ET_File_Info *ETFileInfo);
+gboolean Mpeg_Header_Display_File_Info_To_UI (gchar *filename, ET_File_Info *ETFileInfo);
+
+
+#endif /* __MPEG_HEADER_H__ */
diff --git a/src/msgbox.c b/src/msgbox.c
new file mode 100755
index 0000000..3099e50
--- /dev/null
+++ b/src/msgbox.c
@@ -0,0 +1,312 @@
+/* msgbox.c - 2000/10/13 */
+/*
+ * EasyTAG - Tag editor for MP3 and Ogg Vorbis files
+ * Copyright (C) 2000-2003 Jerome Couderc <easytag@gmail.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+/* Note: Some pieces of codes come from gnome-dialog.c
+ */
+
+#include <config.h>
+
+#include <gtk/gtk.h>
+#include <glib/gi18n-lib.h>
+#include <stdarg.h>
+
+#include "msgbox.h"
+#include "easytag.h"
+#include "misc.h"
+#include "setting.h"
+
+
+static void msg_box_class_init (MsgBoxClass *class);
+static void msg_box_init (MsgBox *mb);
+
+static void msg_box_show (GtkWidget *widget);
+static gint msg_box_delete_event (GtkWidget *widget, GdkEventAny *event);
+static void msg_box_button_clicked (GtkButton *button, gpointer data);
+void check_button_toggled (GtkCheckButton *checkbutton, gpointer data);
+
+
+static GtkDialogClass *parent_klass = NULL;
+
+
+GType msg_box_get_type(void)
+{
+ static GType mb_type = 0;
+
+ if (!mb_type)
+ {
+ GTypeInfo mb_info =
+ {
+ sizeof(MsgBoxClass),
+ NULL,
+ NULL,
+ (GClassInitFunc) msg_box_class_init,
+ NULL,
+ NULL,
+ sizeof(MsgBox),
+ 0,
+ (GInstanceInitFunc) msg_box_init
+ };
+ mb_type = g_type_register_static(GTK_TYPE_DIALOG, "MsgBox", &mb_info, 0);
+ }
+ return mb_type;
+}
+
+
+static void msg_box_button_clicked (GtkButton *button, gpointer data)
+{
+ MsgBox *mb;
+
+ g_return_if_fail( (mb = MSG_BOX(data)) );
+
+ mb->button_clicked = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(button),"button_value"));
+
+ if (gtk_main_level()>1)
+ gtk_main_quit();
+}
+
+
+static void msg_box_class_init (MsgBoxClass *class)
+{
+ GtkObjectClass *object_class;
+ GtkWidgetClass *widget_class;
+ GtkDialogClass *dialog_class;
+
+ object_class = (GtkObjectClass*) class;
+ widget_class = (GtkWidgetClass*) class;
+ dialog_class = (GtkDialogClass*) class;
+
+ parent_klass = gtk_type_class(gtk_dialog_get_type());
+
+ object_class->destroy = msg_box_destroy;
+ widget_class->delete_event = msg_box_delete_event;
+ widget_class->show = msg_box_show;
+}
+
+
+static void msg_box_init (MsgBox *mb)
+{
+ g_return_if_fail(mb != NULL);
+ g_return_if_fail(IS_MSG_BOX(mb));
+
+ mb->icon = -1;
+ mb->check_button = NULL;
+ mb->check_button_state = 0;
+
+}
+
+
+/*
+ * Create a new message box
+ */
+GtkWidget *msg_box_new (gchar *title, gchar *message, const gchar *stock_id,
+ /* Put all the buttons to display, and terminate by 0 */
+ ...)
+{
+ MsgBox *mb;
+ GtkWidget *Table;
+ GtkWidget *Pixmap;
+ GtkWidget *Label;
+ GtkWidget *ButtonBox;
+ GtkWidget *Button = NULL;
+ va_list cursor;
+ gint cursor_value;
+
+
+ g_return_val_if_fail(message!=NULL, NULL);
+
+ mb = MSG_BOX(g_object_new(TYPE_MSG_BOX, NULL));
+ //mb->icon = icon;
+ mb->check_button = gtk_check_button_new_with_label(_("Repeat action for the rest of the files")); /* Can save or cancel all files */
+
+ /* Window configuration */
+ gtk_window_set_title(GTK_WINDOW(mb),title);
+ gtk_window_set_transient_for(GTK_WINDOW(mb),GTK_WINDOW(MainWindow));
+ gtk_window_set_modal(GTK_WINDOW(mb),TRUE);
+
+ if (MESSAGE_BOX_POSITION_NONE)
+ gtk_window_set_position(GTK_WINDOW(mb),GTK_WIN_POS_NONE);
+ else if (MESSAGE_BOX_POSITION_CENTER)
+ gtk_window_set_position(GTK_WINDOW(mb),GTK_WIN_POS_CENTER);
+ else if (MESSAGE_BOX_POSITION_MOUSE)
+ gtk_window_set_position(GTK_WINDOW(mb),GTK_WIN_POS_MOUSE);
+ else if (MESSAGE_BOX_POSITION_CENTER_ON_PARENT)
+ gtk_window_set_position(GTK_WINDOW(mb),GTK_WIN_POS_CENTER_ON_PARENT);
+
+
+ /* Table to put: the pixmap, the message and the check button */
+ Table = gtk_table_new(2,2,FALSE);
+ gtk_box_pack_start(GTK_BOX(GTK_DIALOG(mb)->vbox),Table,TRUE,TRUE,0);
+ gtk_container_set_border_width(GTK_CONTAINER(Table),4);
+ gtk_widget_show(Table);
+
+ /* The Pixmap */
+ Pixmap = gtk_image_new_from_stock(stock_id, GTK_ICON_SIZE_DIALOG);
+ gtk_table_attach_defaults(GTK_TABLE(Table),Pixmap,0,1,0,1);
+ //gtk_table_attach(GTK_TABLE(Table),Pixmap,0,1,0,1,GTK_FILL,GTK_FILL,0,0);
+ gtk_misc_set_padding(GTK_MISC(Pixmap),6,6);
+ gtk_widget_show(Pixmap);
+
+ /* The Message */
+ Label = gtk_label_new (message);
+ gtk_table_attach_defaults(GTK_TABLE(Table),Label,1,2,0,1);
+ gtk_misc_set_padding(GTK_MISC(Label),6,6);
+ gtk_label_set_justify(GTK_LABEL(Label),GTK_JUSTIFY_CENTER);
+ //gtk_label_set_line_wrap(GTK_LABEL(Label),TRUE);
+ gtk_widget_show(Label);
+
+ /* The Check Button */
+ gtk_table_attach_defaults(GTK_TABLE(Table),mb->check_button,0,2,1,2);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(mb->check_button),mb->check_button_state);
+ g_signal_connect(G_OBJECT(mb->check_button),"toggled",G_CALLBACK(check_button_toggled),mb);
+ gtk_widget_show(mb->check_button);
+
+
+ /* Buttons */
+ ButtonBox = gtk_hbutton_box_new();
+ gtk_box_set_spacing(GTK_BOX(ButtonBox), 15);
+ gtk_button_box_set_layout(GTK_BUTTON_BOX(ButtonBox), GTK_BUTTONBOX_END);
+ gtk_container_add(GTK_CONTAINER(GTK_DIALOG(mb)->action_area),ButtonBox);
+
+ /* Read buttons from variable arguments */
+ va_start (cursor,stock_id);
+ while ( (cursor_value = va_arg(cursor,gint)) != 0 )
+ {
+ Button = Create_Button_With_Pixmap(cursor_value);
+ gtk_container_add(GTK_CONTAINER(ButtonBox),Button);
+ GTK_WIDGET_SET_FLAGS(Button,GTK_CAN_DEFAULT);
+ g_signal_connect(G_OBJECT(Button),"destroy", G_CALLBACK(gtk_widget_destroyed),&Button);
+ g_signal_connect(G_OBJECT(Button),"clicked", G_CALLBACK(msg_box_button_clicked),mb);
+ g_object_set_data(G_OBJECT(Button),"button_value", GINT_TO_POINTER(cursor_value));
+ }
+ va_end(cursor);
+ gtk_widget_grab_default(Button);
+ gtk_widget_show_all(ButtonBox);
+
+ return GTK_WIDGET(mb);
+}
+
+
+void msg_box_destroy (GtkObject *object)
+{
+ MsgBox *mb;
+
+ g_return_if_fail( (mb = MSG_BOX(object)) );
+
+ if (mb->check_button)
+ gtk_widget_destroy(mb->check_button);
+
+/* FIXME: causes segfault in some cases (when callin gtk_widget_destroy(msgbox) ) */
+/* if (GTK_OBJECT_CLASS(parent_klass)->destroy)
+ * (* GTK_OBJECT_CLASS(parent_klass)->destroy) (object);
+ */
+}
+
+
+static gint msg_box_delete_event (GtkWidget *widget, GdkEventAny *event)
+{
+ MsgBox *mb;
+
+ g_return_val_if_fail((mb = MSG_BOX(widget)), FALSE);
+
+ /* If the window is closed whitout pressing a button, we return -1 */
+ mb->button_clicked = -1;
+
+ if (gtk_main_level()>1)
+ gtk_main_quit();
+
+ return TRUE;
+}
+
+
+/*
+ * "Run" and show the msgbox, and send which button was pressed (or not a button)
+ */
+gint msg_box_run (MsgBox *msgbox)
+{
+ MsgBox *mb;
+
+ g_return_val_if_fail((mb = MSG_BOX(msgbox)),0);
+
+ gtk_widget_show(GTK_WIDGET(mb));
+ gtk_main();
+ gtk_widget_hide(GTK_WIDGET(mb));
+ gtk_window_set_modal(GTK_WINDOW(mb),FALSE);
+
+ return (MSG_BOX(mb)->button_clicked);
+}
+
+
+static void msg_box_show (GtkWidget *widget)
+{
+ if (GTK_WIDGET_CLASS(parent_klass)->show)
+ (* (GTK_WIDGET_CLASS(parent_klass)->show))(widget);
+}
+
+
+/*
+ * Callback when the check_button is pressed
+ */
+void check_button_toggled (GtkCheckButton *checkbutton, gpointer data)
+{
+ MsgBox *mb;
+
+ g_return_if_fail((mb = MSG_BOX(data)));
+
+ mb->check_button_state = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(checkbutton));
+}
+
+
+/*
+ * Set the check_button to the 'is_active' state
+ */
+void msg_box_check_button_set_active (MsgBox *msgbox, gboolean is_active)
+{
+ MsgBox *mb;
+
+ g_return_if_fail((mb = MSG_BOX(msgbox)));
+
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(mb->check_button),is_active);
+ mb->check_button_state = is_active;
+}
+
+
+/*
+ * Get state of the check_button
+ */
+gboolean msg_box_check_button_get_active (MsgBox *msgbox)
+{
+ MsgBox *mb;
+
+ g_return_val_if_fail((mb = MSG_BOX(msgbox)),FALSE);
+
+ return gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(mb->check_button));
+}
+
+
+/*
+ * Hide the check_button.
+ */
+void msg_box_hide_check_button (MsgBox *msgbox)
+{
+ MsgBox *mb;
+
+ g_return_if_fail((mb = MSG_BOX(msgbox)));
+
+ gtk_widget_hide(mb->check_button);
+}
diff --git a/src/msgbox.h b/src/msgbox.h
new file mode 100755
index 0000000..6b5d663
--- /dev/null
+++ b/src/msgbox.h
@@ -0,0 +1,103 @@
+/* msgbox.h - 2000/10/13 */
+/*
+ * EasyTAG - Tag editor for MP3 and Ogg Vorbis files
+ * Copyright (C) 2000-2003 Jerome Couderc <easytag@gmail.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef __MSGBOX_H__
+#define __MSGBOX_H__
+
+
+#include <gtk/gtkdialog.h>
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+
+#define TYPE_MSG_BOX (msg_box_get_type())
+#define MSG_BOX(obj) (GTK_CHECK_CAST((obj), TYPE_MSG_BOX, MsgBox))
+#define MSG_BOX_CLASS(klass) (GTK_CHECK_CLASS_CAST((klass), TYPE_MSG_BOX, MsgBoxClass))
+#define IS_MSG_BOX(obj) (GTK_CHECK_TYPE((obj), TYPE_MSG_BOX))
+#define IS_MSG_BOX_CLASS(klass) (GTK_CHECK_CLASS_TYPE((klass), TYPE_MSG_BOX))
+
+
+typedef struct _MsgBox MsgBox;
+typedef struct _MsgBoxClass MsgBoxClass;
+
+struct _MsgBox
+{
+ GtkDialog dialog;
+
+ gint icon;
+
+ /* Check button */
+ GtkWidget *check_button;
+ gint check_button_state;
+
+ /* To know which button have been pressed */
+ gint button_clicked;
+};
+
+struct _MsgBoxClass
+{
+ GtkDialogClass parent_class;
+};
+
+
+enum Button_Type
+{
+ BUTTON_OK = 1<<0 ,
+ BUTTON_YES = 1<<1 ,
+ BUTTON_NO = 1<<2 ,
+ BUTTON_APPLY = 1<<3 ,
+ BUTTON_SAVE = 1<<4 ,
+ BUTTON_CANCEL = 1<<5 ,
+ BUTTON_CLOSE = 1<<6 ,
+ BUTTON_WRITE = 1<<7 ,
+ BUTTON_EXECUTE = 1<<8 ,
+ BUTTON_SEARCH = 1<<9 ,
+ BUTTON_BROWSE = 1<<10
+};
+
+enum Message_Type
+{
+ MSG_INFO = 1<<0,
+ MSG_ERROR = 1<<1,
+ MSG_QUESTION = 1<<2,
+ MSG_WARNING = 1<<3
+};
+
+
+GType msg_box_get_type(void);
+
+GtkWidget *msg_box_new (gchar *title, gchar *message, const gchar *icon, ...);
+gint msg_box_run (MsgBox *msgbox);
+void msg_box_destroy (GtkObject *object);
+void msg_box_check_button_set_active (MsgBox *msgbox, gboolean is_active);
+gboolean msg_box_check_button_get_active (MsgBox *msgbox);
+void msg_box_hide_check_button (MsgBox *msgbox);
+
+
+#ifdef __cplusplus
+};
+#endif /* __cplusplus */
+
+
+#endif /* __MSGBOX_H__ */
diff --git a/src/musepack_header.c b/src/musepack_header.c
new file mode 100755
index 0000000..8bd4982
--- /dev/null
+++ b/src/musepack_header.c
@@ -0,0 +1,125 @@
+/* musepack_header.c */
+/*
+ * EasyTAG - Tag editor for MP3, Ogg Vorbis and MPC files
+ * Copyright (C) 2001-2003 Jerome Couderc <easytag@gmail.com>
+ * Copyright (C) 2002-2003 Artur Polaczyñski <artii@o2.pl>
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+
+#include <gtk/gtk.h>
+#include <glib/gi18n-lib.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "easytag.h"
+#include "et_core.h"
+#include "misc.h"
+#include "setting.h"
+#include "charset.h"
+#include "musepack_header.h"
+#include "libapetag/info_mpc.h"
+
+
+/***************
+ * Header info *
+ ***************/
+
+gboolean Mpc_Header_Read_File_Info (gchar *filename, ET_File_Info *ETFileInfo)
+{
+ StreamInfoMpc Info;
+
+ if (info_mpc_read(filename, &Info))
+ {
+ gchar *filename_utf8 = filename_to_display(filename);
+ fprintf(stderr,"MPC header not found for file '%s'\n", filename_utf8);
+ g_free(filename_utf8);
+ return FALSE;
+ }
+ //printf("%",Info.fields);
+ ETFileInfo->mpc_profile = g_strdup(Info.ProfileName);
+ ETFileInfo->version = Info.StreamVersion;
+ ETFileInfo->bitrate = Info.Bitrate/1000.0;
+ ETFileInfo->samplerate = Info.SampleFreq;
+ ETFileInfo->mode = Info.Channels;
+ ETFileInfo->size = Info.FileSize;
+ ETFileInfo->duration = Info.Duration/1000;
+ ETFileInfo->mpc_version = g_strdup_printf("%s",Info.Encoder);
+
+ return TRUE;
+}
+
+
+
+gboolean Mpc_Header_Display_File_Info_To_UI (gchar *filename_utf8, ET_File_Info *ETFileInfo)
+{
+ gchar *text;
+ gchar *time = NULL;
+ gchar *time1 = NULL;
+ gchar *size = NULL;
+ gchar *size1 = NULL;
+
+ /* Mode changed to profile name */
+ text = g_strdup_printf(_("Profile:"));
+ gtk_label_set_text(GTK_LABEL(ModeLabel),text);
+ g_free(text);
+ text = g_strdup_printf("%s (SV%d)",ETFileInfo->mpc_profile,ETFileInfo->version);
+ gtk_label_set_text(GTK_LABEL(ModeValueLabel),text);
+ g_free(text);
+
+ /* Bitrate */
+ text = g_strdup_printf(_("%d kb/s"),ETFileInfo->bitrate);
+ gtk_label_set_text(GTK_LABEL(BitrateValueLabel),text);
+ g_free(text);
+
+ /* Samplerate */
+ text = g_strdup_printf(_("%d Hz"),ETFileInfo->samplerate);
+ gtk_label_set_text(GTK_LABEL(SampleRateValueLabel),text);
+ g_free(text);
+
+ /* Version changed to encoder version */
+ text = g_strdup_printf(_("Encoder:"));
+ gtk_label_set_text(GTK_LABEL(VersionLabel),text);
+ g_free(text);
+
+ text = g_strdup_printf("%s",ETFileInfo->mpc_version);
+ gtk_label_set_text(GTK_LABEL(VersionValueLabel),text);
+ g_free(text);
+
+ /* Size */
+ size = Convert_Size(ETFileInfo->size);
+ size1 = Convert_Size(ETCore->ETFileDisplayedList_TotalSize);
+ text = g_strdup_printf("%s (%s)",size,size1);
+ gtk_label_set_text(GTK_LABEL(SizeValueLabel),text);
+ g_free(size);
+ g_free(size1);
+ g_free(text);
+
+ /* Duration */
+ time = Convert_Duration(ETFileInfo->duration);
+ time1 = Convert_Duration(ETCore->ETFileDisplayedList_TotalDuration);
+ text = g_strdup_printf("%s (%s)",time,time1);
+ gtk_label_set_text(GTK_LABEL(DurationValueLabel),text);
+ g_free(time);
+ g_free(time1);
+ g_free(text);
+
+ return TRUE;
+}
diff --git a/src/musepack_header.h b/src/musepack_header.h
new file mode 100755
index 0000000..8d321ee
--- /dev/null
+++ b/src/musepack_header.h
@@ -0,0 +1,42 @@
+/* musepack_header.h - 26 XI 2002 */
+/*
+ * EasyTAG - Tag editor for MP3 and Ogg Vorbis files
+ * Copyright (C) 2000-2003 Jerome Couderc <easytag@gmail.com>
+ * Copyright (C) 2002-2003 Artur Polaczyñski <artii@o2.pl>
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef __MUSEPACK_HEADER_H__
+#define __MUSEPACK_HEADER_H__
+
+
+#include "et_core.h"
+
+/****************
+ * Declarations *
+ ****************/
+
+
+/**************
+ * Prototypes *
+ **************/
+
+gboolean Mpc_Header_Read_File_Info (gchar *filename, ET_File_Info *ETFileInfo);
+gboolean Mpc_Header_Display_File_Info_To_UI (gchar *filename, ET_File_Info *ETFileInfo);
+
+
+#endif /* __MUSEPACK_HEADER_H__ */
diff --git a/src/ogg_header.c b/src/ogg_header.c
new file mode 100755
index 0000000..61d7a76
--- /dev/null
+++ b/src/ogg_header.c
@@ -0,0 +1,293 @@
+/* ogg_header.c - 2003/12/29 */
+/*
+ * EasyTAG - Tag editor for MP3 and Ogg Vorbis files
+ * Copyright (C) 2000-2003 Jerome Couderc <easytag@gmail.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h> // For definition of ENABLE_OGG
+
+#ifdef ENABLE_OGG
+
+#include <gtk/gtk.h>
+#include <glib/gi18n-lib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <vorbis/codec.h>
+#include <vorbis/vorbisfile.h>
+
+#ifdef ENABLE_SPEEX
+#include <speex/speex_header.h>
+#include "vcedit.h"
+#endif
+
+#include "easytag.h"
+#include "ogg_header.h"
+#include "et_core.h"
+#include "charset.h"
+#include "log.h"
+#include "misc.h"
+
+
+/***************
+ * Declaration *
+ ***************/
+
+
+/**************
+ * Prototypes *
+ **************/
+
+
+/*************
+ * Functions *
+ *************/
+
+gboolean Ogg_Header_Read_File_Info (gchar *filename, ET_File_Info *ETFileInfo)
+{
+ FILE *file;
+ OggVorbis_File vf;
+ vorbis_info *vi;
+ gint encoder_version = 0;
+ gint channels = 0;
+ glong rate = 0;
+ glong bitrate_upper = 0;
+ glong bitrate_nominal = 0;
+ glong bitrate_lower = 0;
+ gdouble duration = 0;
+ gulong filesize;
+ gint ret;
+ gchar *filename_utf8;
+
+ if (!filename || !ETFileInfo)
+ return FALSE;
+
+ filename_utf8 = filename_to_display(filename);
+
+ if ( (file=fopen(filename,"rb"))==NULL ) // Warning : it is important to open the file in binary mode! (to get header informations under Win32)
+ {
+ Log_Print(_("ERROR while opening file: '%s' (%s)."),filename_utf8,g_strerror(errno));
+ g_free(filename_utf8);
+ return FALSE;
+ }
+
+ if ( (ret=ov_open(file,&vf,NULL,0)) == 0)
+ {
+ if ( (vi=ov_info(&vf,0)) != NULL )
+ {
+ encoder_version = vi->version; // Vorbis encoder version used to create this bitstream.
+ channels = vi->channels; // Number of channels in bitstream.
+ rate = vi->rate; // (Hz) Sampling rate of the bitstream.
+ bitrate_upper = vi->bitrate_upper; // (b/s) Specifies the upper limit in a VBR bitstream.
+ bitrate_nominal = vi->bitrate_nominal; // (b/s) Specifies the average bitrate for a VBR bitstream.
+ //bitrate_nominal = ov_bitrate(&vf,-1); // (b/s) Specifies the average bitrate for the specified logical bitstream.
+ bitrate_lower = vi->bitrate_nominal; // (b/s) Specifies the lower limit in a VBR bitstream.
+ }else
+ {
+ Log_Print(_("Ogg Vorbis: The specified bitstream does not exist or the "
+ "file has been initialized improperly (file: '%s')."),filename_utf8);
+ }
+
+ duration = ov_time_total(&vf,-1); // (s) Total time.
+ //g_print("play time: %ld s\n",(long)ov_time_total(&vf,-1));
+ //g_print("serialnumber: %ld\n",(long)ov_serialnumber(&vf,-1));
+ //g_print("compressed length: %ld bytes\n",(long)(ov_raw_total(&vf,-1)));
+
+ /***{
+ // Test for displaying comments
+ vorbis_comment *vc = ov_comment(&vf,-1);
+ Log_Print(">>> %s",filename_utf8);
+ Log_Print("Nbr comments : %d",vc->comments);
+ Log_Print("Vendor : %s",vc->vendor);
+ char **ptr = vc->user_comments;
+ while(*ptr){
+ Log_Print("> %s",*ptr);
+ ++ptr;
+ }
+ }***/
+ ov_clear(&vf); // This close also the file
+ }else
+ {
+ // Because not closed by ov_clear()
+ fclose(file);
+
+ // On error...
+ switch (ret)
+ {
+ case OV_EREAD:
+ Log_Print(_("Ogg Vorbis: Read from media returned an error (file: '%s')."),filename_utf8);
+ break;
+ case OV_ENOTVORBIS:
+ Log_Print(_("Ogg Vorbis: Bitstream is not Vorbis data (file: '%s')."),filename_utf8);
+ break;
+ case OV_EVERSION:
+ Log_Print(_("Ogg Vorbis: Vorbis version mismatch (file: '%s')."),filename_utf8);
+ break;
+ case OV_EBADHEADER:
+ Log_Print(_("Ogg Vorbis: Invalid Vorbis bitstream header (file: '%s')."),filename_utf8);
+ break;
+ case OV_EFAULT:
+ Log_Print(_("Ogg Vorbis: Internal logic fault, indicates a bug or heap/stack corruption (file: '%s')."),filename_utf8);
+ break;
+ default:
+ break;
+ }
+ }
+
+ filesize = Get_File_Size(filename);
+
+ ETFileInfo->version = encoder_version;
+ ETFileInfo->bitrate = bitrate_nominal/1000;
+ ETFileInfo->samplerate = rate;
+ ETFileInfo->mode = channels;
+ ETFileInfo->size = filesize;
+ ETFileInfo->duration = duration;
+
+ g_free(filename_utf8);
+ return TRUE;
+}
+
+
+#ifdef ENABLE_SPEEX
+
+gboolean Speex_Header_Read_File_Info (gchar *filename, ET_File_Info *ETFileInfo)
+{
+ FILE *file;
+ vcedit_state *state;
+ SpeexHeader *si;
+ gchar *encoder_version = NULL;
+ gint channels = 0;
+ glong rate = 0;
+ glong bitrate = 0;
+ gdouble duration = 0;
+ gulong filesize;
+ gchar *filename_utf8;
+
+ if (!filename || !ETFileInfo)
+ return FALSE;
+
+ filename_utf8 = filename_to_display(filename);
+
+ if ( (file=fopen(filename,"rb"))==NULL ) // Warning : it is important to open the file in binary mode! (to get header informations under Win32)
+ {
+ Log_Print(_("ERROR while opening file: '%s' (%s)."),filename_utf8,g_strerror(errno));
+ g_free(filename_utf8);
+ return FALSE;
+ }
+
+
+ state = vcedit_new_state(); // Allocate memory for 'state'
+ if ( vcedit_open(state,file) < 0 )
+ {
+ Log_Print(_("ERROR: Failed to open file: '%s' as vorbis (%s)."),filename_utf8,vcedit_error(state));
+ fclose(file);
+ g_free(filename_utf8);
+ vcedit_clear(state);
+ return FALSE;
+ }
+
+ // Get Speex informations
+ if ( (si=state->si) != NULL )
+ {
+ encoder_version = si->speex_version;
+ channels = si->nb_channels; // Number of channels in bitstream.
+ rate = si->rate; // (Hz) Sampling rate of the bitstream.
+ bitrate = si->bitrate; // (b/s) Specifies the bitrate
+
+ duration = 0;//ov_time_total(&vf,-1); // (s) Total time.
+
+ //g_print("play time: %ld s\n",(long)ov_time_total(&vf,-1));
+ //g_print("serialnumber: %ld\n",(long)ov_serialnumber(&vf,-1));
+ //g_print("compressed length: %ld bytes\n",(long)(ov_raw_total(&vf,-1)));
+ }
+
+ filesize = Get_File_Size(filename);
+
+ ETFileInfo->mpc_version = g_strdup(encoder_version);
+ ETFileInfo->bitrate = bitrate/1000;
+ ETFileInfo->samplerate = rate;
+ ETFileInfo->mode = channels;
+ ETFileInfo->size = filesize;
+ //if (bitrate > 0)
+ // ETFileInfo->duration = filesize*8/bitrate/1000; // FIXME : Approximation!! Needs to remove tag size!
+ //else
+ ETFileInfo->duration = duration;
+
+ vcedit_clear(state);
+ fclose(file);
+ g_free(filename_utf8);
+ return TRUE;
+}
+#endif
+
+gboolean Ogg_Header_Display_File_Info_To_UI (gchar *filename, ET_File_Info *ETFileInfo)
+{
+ gchar *text;
+ gchar *time = NULL;
+ gchar *time1 = NULL;
+ gchar *size = NULL;
+ gchar *size1 = NULL;
+
+ /* Encoder version */
+ gtk_label_set_text(GTK_LABEL(VersionLabel),_("Encoder:"));
+ if (!ETFileInfo->mpc_version)
+ {
+ text = g_strdup_printf("%d",ETFileInfo->version);
+ gtk_label_set_text(GTK_LABEL(VersionValueLabel),text);
+ g_free(text);
+ }else
+ {
+ gtk_label_set_text(GTK_LABEL(VersionValueLabel),ETFileInfo->mpc_version);
+ }
+
+ /* Bitrate */
+ text = g_strdup_printf(_("%d kb/s"),ETFileInfo->bitrate);
+ gtk_label_set_text(GTK_LABEL(BitrateValueLabel),text);
+ g_free(text);
+
+ /* Samplerate */
+ text = g_strdup_printf(_("%d Hz"),ETFileInfo->samplerate);
+ gtk_label_set_text(GTK_LABEL(SampleRateValueLabel),text);
+ g_free(text);
+
+ /* Mode */
+ gtk_label_set_text(GTK_LABEL(ModeLabel),_("Channels:"));
+ text = g_strdup_printf("%d",ETFileInfo->mode);
+ gtk_label_set_text(GTK_LABEL(ModeValueLabel),text);
+ g_free(text);
+
+ /* Size */
+ size = Convert_Size(ETFileInfo->size);
+ size1 = Convert_Size(ETCore->ETFileDisplayedList_TotalSize);
+ text = g_strdup_printf("%s (%s)",size,size1);
+ gtk_label_set_text(GTK_LABEL(SizeValueLabel),text);
+ g_free(size);
+ g_free(size1);
+ g_free(text);
+
+ /* Duration */
+ time = Convert_Duration(ETFileInfo->duration);
+ time1 = Convert_Duration(ETCore->ETFileDisplayedList_TotalDuration);
+ text = g_strdup_printf("%s (%s)",time,time1);
+ gtk_label_set_text(GTK_LABEL(DurationValueLabel),text);
+ g_free(time);
+ g_free(time1);
+ g_free(text);
+
+ return TRUE;
+}
+
+#endif /* ENABLE_OGG */
diff --git a/src/ogg_header.h b/src/ogg_header.h
new file mode 100755
index 0000000..467b0b7
--- /dev/null
+++ b/src/ogg_header.h
@@ -0,0 +1,44 @@
+/* ogg_header.h - 2003/12/29 */
+/*
+ * EasyTAG - Tag editor for MP3 and Ogg Vorbis files
+ * Copyright (C) 2000-2003 Jerome Couderc <easytag@gmail.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef __OGG_HEADER_H__
+#define __OGG_HEADER_H__
+
+
+#include <glib.h>
+#include "et_core.h"
+
+/****************
+ * Declarations *
+ ****************/
+
+
+/**************
+ * Prototypes *
+ **************/
+
+gboolean Ogg_Header_Read_File_Info (gchar *filename, ET_File_Info *ETFileInfo);
+gboolean Ogg_Header_Display_File_Info_To_UI (gchar *filename, ET_File_Info *ETFileInfo);
+
+gboolean Speex_Header_Read_File_Info (gchar *filename, ET_File_Info *ETFileInfo);
+
+
+#endif /* __OGG_HEADER_H__ */
diff --git a/src/ogg_tag.c b/src/ogg_tag.c
new file mode 100755
index 0000000..ce13c48
--- /dev/null
+++ b/src/ogg_tag.c
@@ -0,0 +1,836 @@
+/* ogg_tag.c - 2001/11/08 */
+/*
+ * EasyTAG - Tag editor for MP3 and Ogg Vorbis files
+ * Copyright (C) 2001-2003 Jerome Couderc <easytag@gmail.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h> // For definition of ENABLE_OGG
+
+#ifdef ENABLE_OGG
+
+#include <gtk/gtk.h>
+#include <glib/gi18n-lib.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <vorbis/codec.h>
+#include <vorbis/vorbisfile.h>
+#include <unistd.h>
+
+#include "easytag.h"
+#include "ogg_tag.h"
+#include "vcedit.h"
+#include "et_core.h"
+#include "log.h"
+#include "misc.h"
+#include "setting.h"
+#include "charset.h"
+
+#ifdef WIN32
+// for mkstemp
+# include "win32/win32dep.h"
+#endif
+
+
+/***************
+ * Declaration *
+ ***************/
+
+#define MULTIFIELD_SEPARATOR " - "
+
+/* Ogg Vorbis fields names in UTF-8 : http://www.xiph.org/vorbis/doc/v-comment.html
+ *
+ * Field names :
+ *
+ * Below is a proposed, minimal list of standard field names with a description of intended use. No single or group of field names is mandatory; a comment header may contain one, all or none of the names in this list.
+ *
+ * TITLE : Track/Work name
+ * VERSION : The version field may be used to differentiate multiple versions of the same track title in a single collection. (e.g. remix info)
+ * ALBUM : The collection name to which this track belongs
+ * TRACKNUMBER : The track number of this piece if part of a specific larger collection or album
+ * ARTIST : The artist generally considered responsible for the work. In popular music this is usually the performing band or singer. For classical music it would be the composer. For an audio book it would be the author of the original text.
+ * PERFORMER : The artist(s) who performed the work. In classical music this would be the conductor, orchestra, soloists. In an audio book it would be the actor who did the reading. In popular music this is typically the same as the ARTIST and is omitted.
+ * COPYRIGHT : Copyright attribution, e.g., '2001 Nobody's Band' or '1999 Jack Moffitt'
+ * LICENSE : License information, eg, 'All Rights Reserved', 'Any Use Permitted', a URL to a license such as a Creative Commons license ("www.creativecommons.org/blahblah/license.html") or the EFF Open Audio License ('distributed under the terms of the Open Audio License. see http://www.eff.org/IP/Open_licenses/eff_oal.html for details'), etc.
+ * ORGANIZATION : Name of the organization producing the track (i.e. the 'record label')
+ * DESCRIPTION : A short text description of the contents
+ * GENRE : A short text indication of music genre
+ * DATE : Date the track was recorded
+ * LOCATION : Location where track was recorded
+ * CONTACT : Contact information for the creators or distributors of the track. This could be a URL, an email address, the physical address of the producing label.
+ * ISRC : ISRC number for the track; see the ISRC intro page for more information on ISRC numbers.
+ *
+ * The remaining tags are multiples; if they are present more than once, all their occurances are considered significant.
+ *
+ * PUBLISHER : who publishes the disc the track came from
+ * DISCNUMBER : if part of a multi-disc album, put the disc number here
+ * EAN/UPN : there may be a barcode on the CD; it is most likely an EAN or UPN (Universal Product Number).
+ * LABEL : the record label or imprint on the disc
+ * LABELNO : record labels often put the catalog number of the source media somewhere on the packaging. use this tag to record it.
+ * OPUS : the number of the work; eg, Opus 10, BVW 81, K6
+ * SOURCEMEDIA : the recording media the track came from. eg, CD, Cassette, Radio Broadcast, LP, CD Single
+ * TRACKTOTAL :
+ * ENCODED-BY : The person who encoded the Ogg file. May include contact information such as email address and phone number.
+ * ENCODING : Put the settings you used to encode the Ogg file here. This tag is NOT expected to be stored or returned by cddb type databases. It includes information about the quality setting, bit rate, and bitrate management settings used to encode the Ogg. It also is used for information about which encoding software was used to do the encoding.
+ * COMPOSER : composer of the work. eg, Gustav Mahler
+ * ARRANGER : the person who arranged the piece, eg, Ravel
+ * LYRICIST : the person who wrote the lyrics, eg Donizetti
+ * AUTHOR : for text that is spoken, or was originally meant to be spoken, the author, eg JRR Tolkien
+ * CONDUCTOR : conductor of the work; eg Herbert von Karajan. choir directors would also use this tag.
+ * PERFORMER : individual performers singled out for mention; eg, Yoyo Ma (violinist)
+ * ENSEMBLE : the group playing the piece, whether orchestra, singing duo, or rock and roll band.
+ * PART : a division within a work; eg, a movement of a symphony. Some tracks contain several parts. Use a single PART tag for each part contained in a track. ie, PART="Oh sole mio"
+ * PARTNUMBER : The part number goes in here. You can use any format you like, such as Roman numerals, regular numbers, or whatever. The numbers should be entered in such a way that an alphabetical sort on this tag will correctly show the proper ordering of all the oggs that contain the contain the piece of music.
+ * LOCATION : location of recording, or other location of interest
+ * COMMENT : additional comments of any nature.
+ */
+
+
+
+/**************
+ * Prototypes *
+ **************/
+gboolean Ogg_Tag_Write_File (FILE *file_in, gchar *filename_in, vcedit_state *state);
+
+
+/*************
+ * Functions *
+ *************/
+
+/*
+ * Read tag data into an Ogg Vorbis file.
+ * Note:
+ * - if field is found but contains no info (strlen(str)==0), we don't read it
+ */
+gboolean Ogg_Tag_Read_File_Tag (gchar *filename, File_Tag *FileTag)
+{
+ FILE *file;
+ vcedit_state *state;
+ vorbis_comment *vc;
+ gchar *string = NULL;
+ gchar *string1 = NULL;
+ gchar *string2 = NULL;
+ gchar *filename_utf8 = filename_to_display(filename);
+ guint field_num, i;
+
+
+ if (!filename || !FileTag)
+ return FALSE;
+
+ ogg_error_msg = NULL;
+
+ if ( (file=fopen(filename,"rb")) == NULL )
+ {
+ Log_Print(_("ERROR while opening file: '%s' (%s)."),filename_utf8,g_strerror(errno));
+ g_free(filename_utf8);
+ return FALSE;
+ }
+
+ {
+ // Skip the id3v2 tag
+ guchar tmp_id3[4];
+ gulong id3v2size;
+
+ // Check if there is an ID3v2 tag...
+ fseek(file, 0L, SEEK_SET);
+ if (fread(tmp_id3, 1, 4, file) == 4)
+ {
+ // Calculate ID3v2 length
+ if (tmp_id3[0] == 'I' && tmp_id3[1] == 'D' && tmp_id3[2] == '3' && tmp_id3[3] < 0xFF)
+ {
+ // id3v2 tag skipeer $49 44 33 yy yy xx zz zz zz zz [zz size]
+ fseek(file, 2, SEEK_CUR); // Size is 6-9 position
+ if (fread(tmp_id3, 1, 4, file) == 4)
+ {
+ id3v2size = 10 + ( (long)(tmp_id3[3]) | ((long)(tmp_id3[2]) << 7)
+ | ((long)(tmp_id3[1]) << 14) | ((long)(tmp_id3[0]) << 21) );
+ fseek(file, id3v2size, SEEK_SET);
+ Log_Print(_("Warning : The Ogg Vorbis file '%s' contains an ID3v2 tag."),filename_utf8);
+ }else
+ {
+ fseek(file, 0L, SEEK_SET);
+ }
+ }else
+ {
+ fseek(file, 0L, SEEK_SET);
+ }
+ }else
+ {
+ fseek(file, 0L, SEEK_SET);
+ }
+ }
+
+ state = vcedit_new_state(); // Allocate memory for 'state'
+ if ( vcedit_open(state,file) < 0 )
+ {
+ Log_Print(_("ERROR: Failed to open file: '%s' as vorbis (%s)."),filename_utf8,vcedit_error(state));
+ ogg_error_msg = vcedit_error(state);
+ fclose(file);
+ g_free(filename_utf8);
+ vcedit_clear(state);
+ return FALSE;
+ }
+
+
+ /* Get data from tag */
+ vc = vcedit_comments(state);
+ /*{
+ gint i;
+ for (i=0;i<vc->comments;i++)
+ g_print("%s -> Ogg vc:'%s'\n",g_path_get_basename(filename),vc->user_comments[i]);
+ }*/
+
+ /*********
+ * Title *
+ *********/
+ /* Note : don't forget to add any new field to 'Save unsupported fields' */
+ field_num = 0;
+ while ( (string = vorbis_comment_query(vc,"TITLE",field_num++)) != NULL )
+ {
+ string = Try_To_Validate_Utf8_String(string);
+
+ if ( g_utf8_strlen(string, -1) > 0 )
+ {
+ if (FileTag->title==NULL)
+ FileTag->title = g_strdup(string);
+ else
+ FileTag->title = g_strconcat(FileTag->title,MULTIFIELD_SEPARATOR,string,NULL);
+ // If strlen = 0, then no allocated data!
+ }
+
+ g_free(string);
+ }
+
+ /**********
+ * Artist *
+ **********/
+ field_num = 0;
+ while ( (string = vorbis_comment_query(vc,"ARTIST",field_num++)) != NULL )
+ {
+ string = Try_To_Validate_Utf8_String(string);
+
+ if ( g_utf8_strlen(string, -1) > 0 )
+ {
+ if (FileTag->artist==NULL)
+ FileTag->artist = g_strdup(string);
+ else
+ FileTag->artist = g_strconcat(FileTag->artist,MULTIFIELD_SEPARATOR,string,NULL);
+ }
+
+ g_free(string);
+ }
+
+ /*********
+ * Album *
+ *********/
+ field_num = 0;
+ while ( (string = vorbis_comment_query(vc,"ALBUM",field_num++)) != NULL )
+ {
+ string = Try_To_Validate_Utf8_String(string);
+
+ if ( g_utf8_strlen(string, -1) > 0 )
+ {
+ if (FileTag->album==NULL)
+ FileTag->album = g_strdup(string);
+ else
+ FileTag->album = g_strconcat(FileTag->album,MULTIFIELD_SEPARATOR,string,NULL);
+ }
+
+ g_free(string);
+ }
+
+ /*******************************
+ * Disc Number (Part of a Set) *
+ *******************************/
+ if ( (string = vorbis_comment_query(vc,"DISCNUMBER",0)) != NULL && g_utf8_strlen(string, -1) > 0 )
+ {
+ FileTag->disc_number = g_strdup(string);
+ }
+
+ /********
+ * Year *
+ ********/
+ if ( (string = vorbis_comment_query(vc,"DATE",0)) != NULL && g_utf8_strlen(string, -1) > 0 )
+ {
+ FileTag->year = g_strdup(string);
+ }
+
+ /*************************
+ * Track and Total Track *
+ *************************/
+ if ( (string = vorbis_comment_query(vc,"TRACKNUMBER",0)) != NULL && g_utf8_strlen(string, -1) > 0 )
+ {
+ if (NUMBER_TRACK_FORMATED)
+ {
+ // Ckeck if TRACKTOTAL used, else takes it in TRACKNUMBER
+ if ( (string1 = vorbis_comment_query(vc,"TRACKTOTAL",0)) != NULL && g_utf8_strlen(string1, -1) > 0 )
+ {
+ FileTag->track_total = g_strdup_printf("%.*d",NUMBER_TRACK_FORMATED_SPIN_BUTTON,atoi(string1));
+ }else
+ if ( (string1 = g_utf8_strchr(string, -1, '/')) )
+ {
+ FileTag->track_total = g_strdup_printf("%.*d",NUMBER_TRACK_FORMATED_SPIN_BUTTON,atoi(string1+1));
+ *string1 = '\0';
+ }
+ FileTag->track = g_strdup_printf("%.*d",NUMBER_TRACK_FORMATED_SPIN_BUTTON,atoi(string));
+ }else
+ {
+ // Ckeck if TRACKTOTAL used, else takes it in TRACKNUMBER
+ if ( (string1 = vorbis_comment_query(vc,"TRACKTOTAL",0)) != NULL && g_utf8_strlen(string1, -1) > 0 )
+ {
+ FileTag->track_total = g_strdup_printf("%.*d",NUMBER_TRACK_FORMATED_SPIN_BUTTON,atoi(string1));
+ }else
+ if ( (string1 = g_utf8_strchr(string, -1, '/')) )
+ {
+ FileTag->track_total = g_strdup(string1+1);
+ *string1 = '\0';
+ }
+ FileTag->track = g_strdup(string);
+ }
+ }
+
+ /*********
+ * Genre *
+ *********/
+ field_num = 0;
+ while ( (string = vorbis_comment_query(vc,"GENRE",field_num++)) != NULL )
+ {
+ string = Try_To_Validate_Utf8_String(string);
+
+ if ( g_utf8_strlen(string, -1) > 0 )
+ {
+ if (FileTag->genre==NULL)
+ FileTag->genre = g_strdup(string);
+ else
+ FileTag->genre = g_strconcat(FileTag->genre,MULTIFIELD_SEPARATOR,string,NULL);
+ }
+
+ g_free(string);
+ }
+
+ /***********
+ * Comment *
+ ***********/
+ field_num = 0;
+ string1 = NULL; // Cause it may be not updated into the 'while' condition
+ while ( ((string2 = vorbis_comment_query(vc,"DESCRIPTION",field_num)) != NULL ) // New specifications
+ || ((string = vorbis_comment_query(vc,"COMMENT", field_num)) != NULL ) // Old : Winamp format (for EasyTAG 1.99.11 and older)
+ || ((string1 = vorbis_comment_query(vc,"", field_num)) != NULL ) ) // Old : Xmms format (for EasyTAG 1.99.11 and older)
+ {
+ string = Try_To_Validate_Utf8_String(string);
+ string1 = Try_To_Validate_Utf8_String(string1);
+ string2 = Try_To_Validate_Utf8_String(string2);
+
+ if ( string2 && g_utf8_strlen(string2, -1) > 0 ) // Contains comment to new specifications format and we prefer this format (field name defined)
+ {
+ if (FileTag->comment==NULL)
+ FileTag->comment = g_strdup(string2);
+ else
+ FileTag->comment = g_strconcat(FileTag->comment,MULTIFIELD_SEPARATOR,string2,NULL);
+
+ // Frees allocated data
+ if (string && g_utf8_strlen(string, -1) > 0)
+ g_free(string);
+ if (string1 && g_utf8_strlen(string1, -1) > 0)
+ g_free(string1);
+ }else if ( string && g_utf8_strlen(string, -1) > 0 ) // Contains comment to Winamp format and we prefer this format (field name defined)
+ {
+ if (FileTag->comment==NULL)
+ FileTag->comment = g_strdup(string);
+ else
+ FileTag->comment = g_strconcat(FileTag->comment,MULTIFIELD_SEPARATOR,string,NULL);
+
+ // Frees allocated data
+ if (string1 && g_utf8_strlen(string1, -1) > 0)
+ g_free(string1);
+ }else if ( string1 && g_utf8_strlen(string1, -1) > 0 ) // Contains comment to Xmms format only
+ {
+ if (FileTag->comment==NULL)
+ FileTag->comment = g_strdup(string1);
+ else
+ FileTag->comment = g_strconcat(FileTag->comment,MULTIFIELD_SEPARATOR,string1,NULL);
+ }
+
+ g_free(string);
+ g_free(string1);
+ g_free(string2);
+
+ string = NULL;
+ string1 = NULL;
+ field_num++;
+ }
+
+ /************
+ * Composer *
+ ************/
+ field_num = 0;
+ while ( (string = vorbis_comment_query(vc,"COMPOSER",field_num++)) != NULL )
+ {
+ string = Try_To_Validate_Utf8_String(string);
+
+ if ( g_utf8_strlen(string, -1) > 0 )
+ {
+ if (FileTag->composer==NULL)
+ FileTag->composer = g_strdup(string);
+ else
+ FileTag->composer = g_strconcat(FileTag->composer,MULTIFIELD_SEPARATOR,string,NULL);
+ }
+
+ g_free(string);
+ }
+
+ /*******************
+ * Original artist *
+ *******************/
+ field_num = 0;
+ while ( (string = vorbis_comment_query(vc,"PERFORMER",field_num++)) != NULL )
+ {
+ string = Try_To_Validate_Utf8_String(string);
+
+ if ( g_utf8_strlen(string, -1) > 0 )
+ {
+ if (FileTag->orig_artist==NULL)
+ FileTag->orig_artist = g_strdup(string);
+ else
+ FileTag->orig_artist = g_strconcat(FileTag->orig_artist,MULTIFIELD_SEPARATOR,string,NULL);
+ }
+
+ g_free(string);
+ }
+
+ /*************
+ * Copyright *
+ *************/
+ field_num = 0;
+ while ( (string = vorbis_comment_query(vc,"COPYRIGHT",field_num++)) != NULL )
+ {
+ string = Try_To_Validate_Utf8_String(string);
+
+ if ( g_utf8_strlen(string, -1) > 0 )
+ {
+ if (FileTag->copyright==NULL)
+ FileTag->copyright = g_strdup(string);
+ else
+ FileTag->copyright = g_strconcat(FileTag->copyright,MULTIFIELD_SEPARATOR,string,NULL);
+ }
+
+ g_free(string);
+ }
+
+ /*******
+ * URL *
+ *******/
+ field_num = 0;
+ while ( (string = vorbis_comment_query(vc,"LICENSE",field_num++)) != NULL )
+ {
+ string = Try_To_Validate_Utf8_String(string);
+
+ if ( g_utf8_strlen(string, -1) > 0 )
+ {
+ if (FileTag->url==NULL)
+ FileTag->url = g_strdup(string);
+ else
+ FileTag->url = g_strconcat(FileTag->url,MULTIFIELD_SEPARATOR,string,NULL);
+ }
+
+ g_free(string);
+ }
+
+ /**************
+ * Encoded by *
+ **************/
+ field_num = 0;
+ while ( (string = vorbis_comment_query(vc,"ENCODED-BY",field_num++)) != NULL )
+ {
+ string = Try_To_Validate_Utf8_String(string);
+
+ if ( g_utf8_strlen(string, -1) > 0 )
+ {
+ if (FileTag->encoded_by==NULL)
+ FileTag->encoded_by = g_strdup(string);
+ else
+ FileTag->encoded_by = g_strconcat(FileTag->encoded_by,MULTIFIELD_SEPARATOR,string,NULL);
+ }
+
+ g_free(string);
+ }
+
+
+ /***************************
+ * Save unsupported fields *
+ ***************************/
+ for (i=0;i<(guint)vc->comments;i++)
+ {
+ if ( strncasecmp(vc->user_comments[i],"TITLE=", 6) != 0
+ && strncasecmp(vc->user_comments[i],"ARTIST=", 7) != 0
+ && strncasecmp(vc->user_comments[i],"ALBUM=", 6) != 0
+ && strncasecmp(vc->user_comments[i],"DISCNUMBER=", 11) != 0
+ && strncasecmp(vc->user_comments[i],"DATE=", 5) != 0
+ && strncasecmp(vc->user_comments[i],"TRACKNUMBER=",12) != 0
+ && strncasecmp(vc->user_comments[i],"TRACKTOTAL=", 11) != 0
+ && strncasecmp(vc->user_comments[i],"GENRE=", 6) != 0
+ && strncasecmp(vc->user_comments[i],"DESCRIPTION=",12) != 0
+ && strncasecmp(vc->user_comments[i],"COMMENT=", 8) != 0
+ && strncasecmp(vc->user_comments[i],"=", 1) != 0
+ && strncasecmp(vc->user_comments[i],"COMPOSER=", 9) != 0
+ && strncasecmp(vc->user_comments[i],"PERFORMER=", 10) != 0
+ && strncasecmp(vc->user_comments[i],"COPYRIGHT=", 10) != 0
+ && strncasecmp(vc->user_comments[i],"LICENSE=", 8) != 0
+ && strncasecmp(vc->user_comments[i],"ENCODED-BY=", 11) != 0 )
+ {
+ FileTag->other = g_list_append(FileTag->other,
+ Try_To_Validate_Utf8_String(vc->user_comments[i]));
+ }
+ }
+
+ vcedit_clear(state);
+ fclose(file);
+ g_free(filename_utf8);
+
+ return TRUE;
+}
+
+
+
+gboolean Ogg_Tag_Write_File_Tag (ET_File *ETFile)
+{
+ File_Tag *FileTag;
+ gchar *filename;
+ gchar *filename_utf8;
+ gchar *basename_utf8;
+ FILE *file_in;
+ vcedit_state *state;
+ vorbis_comment *vc;
+ gchar *string;
+ GList *list;
+
+ if (!ETFile || !ETFile->FileTag)
+ return FALSE;
+
+ FileTag = (File_Tag *)ETFile->FileTag->data;
+ filename = ((File_Name *)ETFile->FileNameCur->data)->value;
+ filename_utf8 = ((File_Name *)ETFile->FileNameCur->data)->value_utf8;
+ ogg_error_msg = NULL;
+
+ /* Test to know if we can write into the file */
+ if ( (file_in=fopen(filename,"rb"))==NULL )
+ {
+ Log_Print(_("ERROR while opening file: '%s' (%s)."),filename_utf8,g_strerror(errno));
+ return FALSE;
+ }
+
+ {
+ // Skip the id3v2 tag
+ guchar tmp_id3[4];
+ gulong id3v2size;
+
+ // Check if there is an ID3v2 tag...
+ fseek(file_in, 0L, SEEK_SET);
+ if (fread(tmp_id3, 1, 4, file_in) == 4)
+ {
+ // Calculate ID3v2 length
+ if (tmp_id3[0] == 'I' && tmp_id3[1] == 'D' && tmp_id3[2] == '3' && tmp_id3[3] < 0xFF)
+ {
+ // id3v2 tag skipeer $49 44 33 yy yy xx zz zz zz zz [zz size]
+ fseek(file_in, 2, SEEK_CUR); // Size is 6-9 position
+ if (fread(tmp_id3, 1, 4, file_in) == 4)
+ {
+ id3v2size = 10 + ( (long)(tmp_id3[3]) | ((long)(tmp_id3[2]) << 7)
+ | ((long)(tmp_id3[1]) << 14) | ((long)(tmp_id3[0]) << 21) );
+ fseek(file_in, id3v2size, SEEK_SET);
+ }else
+ {
+ fseek(file_in, 0L, SEEK_SET);
+ }
+ }else
+ {
+ fseek(file_in, 0L, SEEK_SET);
+ }
+ }else
+ {
+ fseek(file_in, 0L, SEEK_SET);
+ }
+ }
+
+ state = vcedit_new_state(); // Allocate memory for 'state'
+ if ( vcedit_open(state,file_in) < 0 )
+ {
+ Log_Print(_("ERROR: Failed to open file: '%s' as vorbis (%s)."),filename_utf8,vcedit_error(state));
+ ogg_error_msg = vcedit_error(state);
+ fclose(file_in);
+ vcedit_clear(state);
+ return FALSE;
+ }
+
+ /* Get data from tag */
+ vc = vcedit_comments(state);
+ vorbis_comment_clear(vc);
+ vorbis_comment_init(vc);
+
+ /*********
+ * Title *
+ *********/
+ if ( FileTag->title )
+ {
+ string = g_strconcat("TITLE=",FileTag->title,NULL);
+ vorbis_comment_add(vc,string);
+ g_free(string);
+ }
+
+ /**********
+ * Artist *
+ **********/
+ if ( FileTag->artist )
+ {
+ string = g_strconcat("ARTIST=",FileTag->artist,NULL);
+ vorbis_comment_add(vc,string);
+ g_free(string);
+ }
+
+ /*********
+ * Album *
+ *********/
+ if ( FileTag->album )
+ {
+ string = g_strconcat("ALBUM=",FileTag->album,NULL);
+ vorbis_comment_add(vc,string);
+ g_free(string);
+ }
+
+ /***************
+ * Disc Number *
+ ***************/
+ if ( FileTag->disc_number )
+ {
+ string = g_strconcat("DISCNUMBER=",FileTag->disc_number,NULL);
+ vorbis_comment_add(vc,string);
+ g_free(string);
+ }
+
+ /********
+ * Year *
+ ********/
+ if ( FileTag->year )
+ {
+ string = g_strconcat("DATE=",FileTag->year,NULL);
+ vorbis_comment_add(vc,string);
+ g_free(string);
+ }
+
+ /*************************
+ * Track and Total Track *
+ *************************/
+ if ( FileTag->track )
+ {
+ string = g_strconcat("TRACKNUMBER=",FileTag->track,NULL);
+ vorbis_comment_add(vc,string);
+ g_free(string);
+ }
+ if ( FileTag->track_total /*&& strlen(FileTag->track_total)>0*/ )
+ {
+ string = g_strconcat("TRACKTOTAL=",FileTag->track_total,NULL);
+ vorbis_comment_add(vc,string);
+ g_free(string);
+ }
+
+ /*********
+ * Genre *
+ *********/
+ if ( FileTag->genre )
+ {
+ string = g_strconcat("GENRE=",FileTag->genre,NULL);
+ vorbis_comment_add(vc,string);
+ g_free(string);
+ }
+
+ /***********
+ * Comment *
+ ***********/
+ // We write the comment using the two formats "DESCRIPTION" and "COMMENT" to be compatible with old versions
+ if ( FileTag->comment )
+ {
+ // Format of new specification
+ string = g_strconcat("DESCRIPTION=",FileTag->comment,NULL);
+ vorbis_comment_add(vc,string);
+ g_free(string);
+
+ // Format used in winamp plugin
+ string = g_strconcat("COMMENT=",FileTag->comment,NULL);
+ vorbis_comment_add(vc,string);
+ g_free(string);
+
+ if (OGG_TAG_WRITE_XMMS_COMMENT)
+ {
+ // Format used into xmms-1.2.5
+ string = g_strconcat("=",FileTag->comment,NULL);
+ vorbis_comment_add(vc,string);
+ g_free(string);
+ }
+ }
+
+ /************
+ * Composer *
+ ************/
+ if ( FileTag->composer )
+ {
+ string = g_strconcat("COMPOSER=",FileTag->composer,NULL);
+ vorbis_comment_add(vc,string);
+ g_free(string);
+ }
+
+ /*******************
+ * Original artist *
+ *******************/
+ if ( FileTag->orig_artist )
+ {
+ string = g_strconcat("PERFORMER=",FileTag->orig_artist,NULL);
+ vorbis_comment_add(vc,string);
+ g_free(string);
+ }
+
+ /*************
+ * Copyright *
+ *************/
+ if ( FileTag->copyright )
+ {
+ string = g_strconcat("COPYRIGHT=",FileTag->copyright,NULL);
+ vorbis_comment_add(vc,string);
+ g_free(string);
+ }
+
+ /*******
+ * URL *
+ *******/
+ if ( FileTag->url )
+ {
+ string = g_strconcat("LICENSE=",FileTag->url,NULL);
+ vorbis_comment_add(vc,string);
+ g_free(string);
+ }
+
+ /**************
+ * Encoded by *
+ **************/
+ if ( FileTag->encoded_by )
+ {
+ string = g_strconcat("ENCODED-BY=",FileTag->encoded_by,NULL);
+ vorbis_comment_add(vc,string);
+ g_free(string);
+ }
+
+
+ /**************************
+ * Set unsupported fields *
+ **************************/
+ list = FileTag->other;
+ while (list)
+ {
+ if (list->data)
+ vorbis_comment_add(vc,(gchar *)list->data);
+ list = list->next;
+ }
+
+ /* Write tag, and close also 'file_in' in all cases */
+ if ( Ogg_Tag_Write_File(file_in,filename,state) == FALSE )
+ {
+ ogg_error_msg = vcedit_error(state);
+ Log_Print(_("ERROR: Failed to write comments to file '%s' (%s)."),filename_utf8,ogg_error_msg == NULL ? "" : ogg_error_msg);
+
+ vcedit_clear(state);
+ return FALSE;
+ }else
+ {
+ basename_utf8 = g_path_get_basename(filename_utf8);
+ Log_Print(_("Written tag of '%s'"),basename_utf8);
+
+ vcedit_clear(state);
+ }
+
+ return TRUE;
+}
+
+
+
+/*
+ * Write tag informations to a new temporary file, and rename it the the initial name.
+ */
+gboolean Ogg_Tag_Write_File (FILE *file_in, gchar *filename_in, vcedit_state *state)
+{
+ gchar *filename_out;
+ gint file_mkstemp;
+ FILE *file_out;
+ gboolean return_code = TRUE;
+
+
+ filename_out = g_strdup_printf("%s.XXXXXX",filename_in);
+
+ // Function mkstemp() opens also the file!
+ if ((file_mkstemp = mkstemp(filename_out)) < 0)
+ {
+ fclose(file_in);
+ close(file_mkstemp);
+ g_free(filename_out);
+ return FALSE;
+ }
+ close(file_mkstemp);
+
+ if ( (file_out=fopen(filename_out,"wb")) == NULL )
+ {
+ fclose(file_in);
+ remove(filename_out);
+ g_free(filename_out);
+ return FALSE;
+ }
+
+ if (vcedit_write(state,file_out) < 0)
+ {
+ fclose(file_in);
+ fclose(file_out);
+ remove(filename_out);
+ g_free(filename_out);
+ return FALSE;
+ }
+ fclose(file_in);
+ fclose(file_out);
+
+ // Some platforms fail to rename a file if the new name already
+ // exists, so we need to remove, then rename...
+ if (rename(filename_out,filename_in))
+ {
+ // Can't rename directly
+ if (remove(filename_in))
+ {
+ // Can't remove file
+ remove(filename_out);
+ return_code = FALSE;
+
+ }else if (rename(filename_out,filename_in))
+ {
+ // Can't rename after removing file
+ remove(filename_out);
+ return_code = FALSE;
+ }
+ }
+
+ g_free(filename_out);
+
+ return return_code;
+}
+
+
+#endif /* ENABLE_OGG */
diff --git a/src/ogg_tag.h b/src/ogg_tag.h
new file mode 100755
index 0000000..df4bbb3
--- /dev/null
+++ b/src/ogg_tag.h
@@ -0,0 +1,42 @@
+/* ogg_tag.h - 2001/11/08 */
+/*
+ * EasyTAG - Tag editor for MP3 and Ogg Vorbis files
+ * Copyright (C) 2001-2003 Jerome Couderc <easytag@gmail.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef __OGG_TAG_H__
+#define __OGG_TAG_H__
+
+
+#include <glib.h>
+#include "et_core.h"
+
+/***************
+ * Declaration *
+ ***************/
+gchar *ogg_error_msg;
+
+
+/**************
+ * Prototypes *
+ **************/
+gboolean Ogg_Tag_Read_File_Tag (gchar *filename, File_Tag *FileTag);
+gboolean Ogg_Tag_Write_File_Tag (ET_File *ETFile);
+
+
+#endif /* __OGG_TAG_H__ */
diff --git a/src/picture.c b/src/picture.c
new file mode 100755
index 0000000..8dd7b37
--- /dev/null
+++ b/src/picture.c
@@ -0,0 +1,1174 @@
+/* picture.c - 2004/11/21 */
+/*
+ * EasyTAG - Tag editor for MP3 and Ogg Vorbis files
+ * Copyright (C) 2000-2003 Jerome Couderc <easytag@gmail.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+#include <config.h>
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+#include <gdk/gdk.h>
+#include <glib/gi18n-lib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "picture.h"
+#include "easytag.h"
+#include "log.h"
+#include "misc.h"
+#include "setting.h"
+#include "msgbox.h"
+#include "bar.h"
+#include "charset.h"
+
+#ifdef WIN32
+# include "win32/win32dep.h"
+#endif
+
+
+/****************
+ * Declarations *
+ ****************/
+
+
+/**************
+ * Prototypes *
+ **************/
+
+void Tag_Area_Picture_Drag_Data (GtkWidget *widget, GdkDragContext *dc,
+ gint x, gint y, GtkSelectionData *selection_data,
+ guint info, guint t, gpointer data);
+void Picture_Selection_Changed_cb (GtkTreeSelection *selection, gpointer data);
+void Picture_Load_Filename (gchar *filename, gpointer user_data);
+
+void Picture_Add_Button_Clicked (GObject *object);
+void Picture_Properties_Button_Clicked (GObject *object);
+void Picture_Save_Button_Clicked (GObject *object);
+void Picture_Clear_Button_Clicked (GObject *object);
+
+gint Picture_Format (Picture *pic);
+const gchar *Picture_Format_String (gint format);
+const gchar *Picture_Type_String (gint type);
+gchar *Picture_Info (Picture *pic);
+void PictureEntry_Clear (void);
+void PictureEntry_Update (Picture *pic, gint select);
+
+Picture *Picture_Allocate (void);
+Picture *Picture_Copy_One (const Picture *pic);
+Picture *Picture_Copy (const Picture *pic);
+void Picture_Free (Picture *pic);
+Picture *Picture_Load_File_Data (const gchar *filename);
+gboolean Picture_Save_File_Data (const Picture *pic, const gchar *filename);
+
+gboolean Picture_Entry_View_Button_Pressed (GtkTreeView *treeview, GdkEventButton *event, gpointer data);
+gboolean Picture_Entry_View_Key_Pressed (GtkTreeView *treeview, GdkEvent *event, gpointer data);
+
+
+/*
+ * Note :
+ * -> MP4_TAG :
+ * Just has one picture (PICTURE_TYPE_FRONT_COVER).
+ * The format's don't matter to the MP4 side.
+ *
+ */
+
+/*************
+ * Functions *
+ *************/
+
+void Tag_Area_Picture_Drag_Data (GtkWidget *widget, GdkDragContext *dc,
+ gint x, gint y, GtkSelectionData *selection_data,
+ guint info, guint t, gpointer data)
+{
+ GtkTreeSelection *selection;
+ gchar **uri_list, **uri;
+
+ gtk_drag_finish(dc, TRUE, FALSE, t);
+
+ if (info != TARGET_URI_LIST
+ || !selection_data
+ || !PictureEntryView)
+ return;
+
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(PictureEntryView));
+ gtk_tree_selection_unselect_all(selection);
+
+ uri = uri_list = g_strsplit((const gchar *)selection_data->data, "\r\n", 0);
+ while (*uri && strlen(*uri))
+ {
+ //Picture *pic;
+ gchar *filename;
+
+ filename = g_filename_from_uri(*uri, 0, 0);
+ if (filename)
+ {
+ Picture_Load_Filename(filename,NULL);
+ /*pic = Picture_Load_File_Data(filename);
+ g_free(filename);
+ if (pic)
+ PictureEntry_Update(pic, 1);*/
+ }
+ uri++;
+ }
+ g_strfreev(uri_list);
+}
+
+void Picture_Selection_Changed_cb (GtkTreeSelection *selection, gpointer data)
+{
+ //if (gtk_tree_selection_count_selected_rows(GTK_TREE_SELECTION(selection)) == 1)
+ //{
+ gtk_widget_set_sensitive(GTK_WIDGET(PictureSaveButton), TRUE);
+ gtk_widget_set_sensitive(GTK_WIDGET(PicturePropertiesButton), TRUE);
+ //}else
+ //{
+ // gtk_widget_set_sensitive(GTK_WIDGET(PictureSaveButton), FALSE);
+ // gtk_widget_set_sensitive(GTK_WIDGET(PicturePropertiesButton), FALSE);
+ //}
+}
+
+void Picture_Clear_Button_Clicked (GObject *object)
+{
+ GList *paths, *refs = NULL, *node = NULL;
+ GtkTreeSelection *selection;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ gpointer proxy;
+ gint n = 0;
+
+ if (!PictureEntryView) return;
+
+ model = gtk_tree_view_get_model(GTK_TREE_VIEW(PictureEntryView));
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(PictureEntryView));
+ paths = gtk_tree_selection_get_selected_rows(selection, 0);
+ proxy = g_object_newv(G_TYPE_OBJECT, 0, NULL);
+
+ // List of items to delete
+ for (node = paths; node; node = node->next)
+ {
+ refs = g_list_append(refs, gtk_tree_row_reference_new_proxy(proxy, model, node->data));
+ gtk_tree_path_free(node->data);
+ }
+ g_list_free(paths);
+
+ for (node = refs; node; node = node->next)
+ {
+ GtkTreePath *path = gtk_tree_row_reference_get_path(node->data);
+ Picture *pic;
+ gboolean valid;
+
+ valid = gtk_tree_model_get_iter(model, &iter, path);
+ if (valid)
+ {
+ gtk_tree_model_get(model, &iter, PICTURE_COLUMN_DATA, &pic,-1);
+ Picture_Free(pic);
+
+ gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
+ }
+
+ gtk_tree_row_reference_deleted(proxy, path);
+ gtk_tree_path_free(path);
+ gtk_tree_row_reference_free(node->data);
+ n++;
+ }
+ g_list_free(refs);
+
+ if (!n)
+ // Delete all if no one was selected.
+ PictureEntry_Clear();
+}
+
+/*
+ * - 'filename' : path + filename of picture file
+ */
+void Picture_Load_Filename (gchar *filename, gpointer user_data)
+{
+ Picture *pic;
+ gchar *filename_utf8;
+ gchar *filename_utf8_folded = NULL;
+ gchar *front_folded = NULL;
+ gchar *back_folded = NULL;
+ gchar *cd_folded = NULL;
+ gchar *inside_folded = NULL;
+ //gchar *inlay_folded = NULL;
+
+ // Filename must be passed in filesystem encoding!
+ pic = Picture_Load_File_Data(filename);
+
+ filename_utf8 = filename_to_display(filename);
+
+ if (pic && filename_utf8)
+ {
+ // Behaviour following the tag type...
+ switch (ETCore->ETFileDisplayed->ETFileDescription->TagType)
+ {
+ case MP4_TAG:
+ {
+ pic->type = PICTURE_TYPE_FRONT_COVER;
+ break;
+ }
+
+ // Other tag types
+ default:
+ {
+ // By default, set the filename in the description
+ pic->description = g_path_get_basename(filename_utf8);
+
+ // Try to identify the type of the picture from the file name
+ filename_utf8_folded = g_utf8_casefold(pic->description, -1);
+ front_folded = g_utf8_casefold("Front", -1);
+ back_folded = g_utf8_casefold("Back", -1);
+ cd_folded = g_utf8_casefold("CD", -1);
+ inside_folded = g_utf8_casefold("inside", -1);
+ //inlay_folded = g_utf8_casefold("inlay", -1);
+ if ( strstr(filename_utf8_folded, front_folded) != NULL )
+ pic->type = PICTURE_TYPE_FRONT_COVER;
+ else if ( strstr(filename_utf8_folded, back_folded) != NULL )
+ pic->type = PICTURE_TYPE_BACK_COVER;
+ else if ( strstr(filename_utf8_folded, cd_folded) != NULL )
+ pic->type = PICTURE_TYPE_MEDIA;
+ else if ( strstr(filename_utf8_folded, inside_folded) != NULL )
+ pic->type = PICTURE_TYPE_LEAFLET_PAGE;
+ //else if ( strstr(filename_utf8_folded, inlay_folded) != NULL )
+ // pic->type = PICTURE_TYPE_LEAFLET_PAGE;
+
+ break;
+ }
+ }
+
+ PictureEntry_Update(pic, 1);
+
+ // FIXME: Call Picture_Free(pic) here? It seems PictureEntry_Update makes copies of pic.
+ //Picture_Free(pic);
+ }
+
+ g_free(filename_utf8);
+ g_free(filename_utf8_folded);
+ g_free(front_folded);
+ g_free(back_folded);
+ g_free(cd_folded);
+ g_free(inside_folded);
+ //g_free(inlay_folded);
+}
+
+/*
+ * To add an image in the list -> call a FileSelectionWindow
+ */
+void Picture_Add_Button_Clicked (GObject *object)
+{
+ GtkWidget *FileSelectionWindow;
+ GtkFileFilter *filter;
+ GtkWindow *parent_window = NULL;
+ static gchar *init_dir = NULL;
+
+ if (!PictureEntryView) return;
+
+ parent_window = (GtkWindow *) gtk_widget_get_toplevel(GTK_WIDGET(object));
+ if (!GTK_WIDGET_TOPLEVEL(parent_window))
+ {
+ g_warning("Could not get parent window\n");
+ return;
+ }
+
+
+ FileSelectionWindow = gtk_file_chooser_dialog_new(_("Add pictures"),
+ parent_window,
+ GTK_FILE_CHOOSER_ACTION_OPEN,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ GTK_STOCK_OPEN, GTK_RESPONSE_OK,
+ NULL);
+
+ // Add files filters
+ // "All files" filter
+ filter = gtk_file_filter_new ();
+ gtk_file_filter_set_name(GTK_FILE_FILTER(filter), _("All Files"));
+ gtk_file_filter_add_pattern(GTK_FILE_FILTER(filter), "*");
+ gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(FileSelectionWindow), GTK_FILE_FILTER(filter));
+
+ // "PNG and JPEG" filter
+ filter = gtk_file_filter_new ();
+ gtk_file_filter_set_name(GTK_FILE_FILTER(filter), _("PNG and JPEG"));
+ //gtk_file_filter_add_mime_type(GTK_FILE_FILTER(filter), "image/jpeg");
+ //gtk_file_filter_add_mime_type(GTK_FILE_FILTER(filter), "image/png");
+ gtk_file_filter_add_pattern(GTK_FILE_FILTER(filter), "*.jpeg");
+ gtk_file_filter_add_pattern(GTK_FILE_FILTER(filter), "*.jpg");
+ gtk_file_filter_add_pattern(GTK_FILE_FILTER(filter), "*.png");
+ gtk_file_chooser_add_filter(GTK_FILE_CHOOSER (FileSelectionWindow), GTK_FILE_FILTER(filter));
+ // Make this filter the default
+ gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(FileSelectionWindow), GTK_FILE_FILTER(filter));
+
+ // Set window position
+ if (MESSAGE_BOX_POSITION_NONE)
+ gtk_window_set_position(GTK_WINDOW(FileSelectionWindow),GTK_WIN_POS_NONE);
+ else if (MESSAGE_BOX_POSITION_CENTER)
+ gtk_window_set_position(GTK_WINDOW(FileSelectionWindow),GTK_WIN_POS_CENTER);
+ else if (MESSAGE_BOX_POSITION_MOUSE)
+ gtk_window_set_position(GTK_WINDOW(FileSelectionWindow),GTK_WIN_POS_MOUSE);
+
+ // Behaviour following the tag type...
+ switch (ETCore->ETFileDisplayed->ETFileDescription->TagType)
+ {
+ case MP4_TAG:
+ {
+ // Only one file can be selected
+ gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(FileSelectionWindow), FALSE);
+ break;
+ }
+
+ // Other tag types
+ default:
+ {
+ gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(FileSelectionWindow), TRUE);
+ break;
+ }
+ }
+
+ gtk_dialog_set_default_response(GTK_DIALOG(FileSelectionWindow), GTK_RESPONSE_OK);
+
+ // Starting directory (the same of the current file)
+ if (ETCore->ETFileDisplayed)
+ {
+ gchar *cur_filename_utf8 = ((File_Name *)((GList *)ETCore->ETFileDisplayed->FileNameCur)->data)->value_utf8;
+ init_dir = g_path_get_dirname(cur_filename_utf8);
+ gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(FileSelectionWindow),init_dir);
+ }else
+ // Starting directory (the same than the previous one)
+ if (init_dir)
+ gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(FileSelectionWindow),init_dir);
+
+ if (gtk_dialog_run(GTK_DIALOG(FileSelectionWindow)) == GTK_RESPONSE_OK)
+ {
+ GtkTreeSelection *selection;
+ GSList *list;
+
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(PictureEntryView));
+ gtk_tree_selection_unselect_all(selection);
+
+ list = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(FileSelectionWindow));
+ g_slist_foreach(list, (GFunc) Picture_Load_Filename, 0);
+ g_slist_free(list);
+
+ // Save the directory selected for initialize next time
+ g_free(init_dir);
+ init_dir = gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER(FileSelectionWindow));
+ }
+ gtk_widget_destroy(FileSelectionWindow);
+}
+
+
+void Picture_Properties_Button_Clicked (GObject *object)
+{
+ GtkWidget *ScrollWindowPictureTypes, *PictureTypesWindow;
+ GtkWidget *type, *label, *desc;
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+ GtkTreeSelection *selection;
+ GtkListStore *store;
+ GtkTreeIter type_iter_to_select, iter;
+ GtkTreeModel *model;
+ GtkWindow *parent_window = NULL;
+ GList *selection_list = NULL;
+ guint i;
+ gint selection_nbr, selection_i = 1;
+ gint picture_types[] =
+ {
+ PICTURE_TYPE_OTHER,
+ PICTURE_TYPE_FILE_ICON,
+ PICTURE_TYPE_OTHER_FILE_ICON,
+ PICTURE_TYPE_FRONT_COVER,
+ PICTURE_TYPE_BACK_COVER,
+ PICTURE_TYPE_LEAFLET_PAGE,
+ PICTURE_TYPE_MEDIA,
+ PICTURE_TYPE_LEAD_ARTIST_LEAD_PERFORMER_SOLOIST,
+ PICTURE_TYPE_ARTIST_PERFORMER,
+ PICTURE_TYPE_CONDUCTOR,
+ PICTURE_TYPE_BAND_ORCHESTRA,
+ PICTURE_TYPE_COMPOSER,
+ PICTURE_TYPE_LYRICIST_TEXT_WRITER,
+ PICTURE_TYPE_RECORDING_LOCATION,
+ PICTURE_TYPE_DURING_RECORDING,
+ PICTURE_TYPE_DURING_PERFORMANCE,
+ PICTURE_TYPE_MOVIDE_VIDEO_SCREEN_CAPTURE,
+ PICTURE_TYPE_A_BRIGHT_COLOURED_FISH,
+ PICTURE_TYPE_ILLUSTRATION,
+ PICTURE_TYPE_BAND_ARTIST_LOGOTYPE,
+ PICTURE_TYPE_PUBLISHER_STUDIO_LOGOTYPE
+ };
+
+
+ if (!PictureEntryView) return;
+
+ parent_window = (GtkWindow *) gtk_widget_get_toplevel(GTK_WIDGET(object));
+ if (!GTK_WIDGET_TOPLEVEL(parent_window))
+ {
+ g_warning("Could not get parent window\n");
+ return;
+ }
+
+ model = gtk_tree_view_get_model(GTK_TREE_VIEW(PictureEntryView));
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(PictureEntryView));
+ selection_list = gtk_tree_selection_get_selected_rows(selection, NULL);
+ selection_nbr = gtk_tree_selection_count_selected_rows(GTK_TREE_SELECTION(selection));
+ while (selection_list)
+ {
+ GtkTreePath *path = selection_list->data;
+ Picture *pic = NULL;
+ GtkTreeSelection *selectiontype;
+ gchar *title;
+ GtkTreePath *rowPath;
+ gboolean valid;
+
+ // Get corresponding picture
+ valid = gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, path);
+ if (valid)
+ gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, PICTURE_COLUMN_DATA, &pic, -1);
+
+ title = g_strdup_printf(_("Picture Properties %d/%d"),selection_i++,selection_nbr);
+ PictureTypesWindow = gtk_dialog_new_with_buttons(title,
+ parent_window,
+ GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
+ GTK_STOCK_OK, GTK_RESPONSE_OK,
+ NULL);
+ g_free(title);
+
+ // Set window position
+ if (MESSAGE_BOX_POSITION_NONE)
+ gtk_window_set_position(GTK_WINDOW(PictureTypesWindow),GTK_WIN_POS_NONE);
+ else if (MESSAGE_BOX_POSITION_CENTER)
+ gtk_window_set_position(GTK_WINDOW(PictureTypesWindow),GTK_WIN_POS_CENTER);
+ else if (MESSAGE_BOX_POSITION_MOUSE)
+ gtk_window_set_position(GTK_WINDOW(PictureTypesWindow),GTK_WIN_POS_MOUSE);
+
+ gtk_dialog_set_default_response(GTK_DIALOG(PictureTypesWindow), GTK_RESPONSE_OK);
+
+ ScrollWindowPictureTypes = gtk_scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ScrollWindowPictureTypes),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_AUTOMATIC);
+ store = gtk_list_store_new(PICTURE_TYPE_COLUMN_COUNT, G_TYPE_STRING, G_TYPE_INT);
+ type = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
+ gtk_container_add(GTK_CONTAINER(ScrollWindowPictureTypes), type);
+
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new();
+ gtk_tree_view_column_pack_start(column, renderer, FALSE);
+ gtk_tree_view_column_set_title(column, _("Picture Type"));
+ gtk_tree_view_column_set_attributes(column, renderer,
+ "text", PICTURE_TYPE_COLUMN_TEXT,
+ NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(type), column);
+ gtk_widget_set_size_request(type, 256, 256);
+ gtk_box_pack_start(GTK_BOX(GTK_DIALOG(PictureTypesWindow)->vbox),ScrollWindowPictureTypes,TRUE,TRUE,0);
+
+ // Behaviour following the tag type...
+ switch (ETCore->ETFileDisplayed->ETFileDescription->TagType)
+ {
+ case MP4_TAG:
+ {
+ // Load picture type (only Front Cover!)
+ GtkTreeIter itertype;
+
+ gtk_list_store_append(store, &itertype);
+ gtk_list_store_set(store, &itertype,
+ PICTURE_TYPE_COLUMN_TEXT, _(Picture_Type_String(PICTURE_TYPE_FRONT_COVER)),
+ PICTURE_TYPE_COLUMN_TYPE_CODE, PICTURE_TYPE_FRONT_COVER,
+ -1);
+ // Line to select by default
+ type_iter_to_select = itertype;
+ break;
+ }
+
+ // Other tag types
+ default:
+ {
+ // Load pictures types
+ for (i = 0; i < sizeof(picture_types)/sizeof(picture_types[0]); i++)
+ {
+ GtkTreeIter itertype;
+
+ gtk_list_store_append(store, &itertype);
+ gtk_list_store_set(store, &itertype,
+ PICTURE_TYPE_COLUMN_TEXT, _(Picture_Type_String(picture_types[i])),
+ PICTURE_TYPE_COLUMN_TYPE_CODE, picture_types[i],
+ -1);
+ // Line to select by default
+ if (pic->type == picture_types[i])
+ type_iter_to_select = itertype;
+ }
+ break;
+ }
+ }
+
+ // Select the line by default
+ selectiontype = gtk_tree_view_get_selection(GTK_TREE_VIEW(type));
+ gtk_tree_selection_select_iter(selectiontype, &type_iter_to_select);
+
+ // Set visible the current selected line
+ rowPath = gtk_tree_model_get_path(GTK_TREE_MODEL(store), &type_iter_to_select);
+ gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(type), rowPath, NULL, FALSE, 0, 0);
+ gtk_tree_path_free(rowPath);
+
+ // Description
+ label = gtk_label_new(_("Picture Description:"));
+ gtk_box_pack_start(GTK_BOX(GTK_DIALOG(PictureTypesWindow)->vbox),label,FALSE,FALSE,4);
+
+ // Entry for the description
+ desc = gtk_entry_new();
+ gtk_box_pack_start(GTK_BOX(GTK_DIALOG(PictureTypesWindow)->vbox),desc,FALSE,FALSE,0);
+ if (pic->description)
+ {
+ gchar *tmp = ET_Utf8_Validate_Full_String(pic->description);
+ gtk_entry_set_text(GTK_ENTRY(desc), tmp);
+ g_free(tmp);
+ }
+
+ // Behaviour following the tag type...
+ switch (ETCore->ETFileDisplayed->ETFileDescription->TagType)
+ {
+ case MP4_TAG:
+ {
+ gtk_widget_set_sensitive(GTK_WIDGET(label), FALSE);
+ gtk_widget_set_sensitive(GTK_WIDGET(desc), FALSE);
+ break;
+ }
+
+ // Other tag types
+ default:
+ {
+ break;
+ }
+ }
+
+ gtk_widget_show_all(PictureTypesWindow);
+
+ if (gtk_dialog_run(GTK_DIALOG(PictureTypesWindow)) == GTK_RESPONSE_OK)
+ {
+ GtkTreeModel *modeltype;
+ GtkTreeIter itertype;
+
+ modeltype = gtk_tree_view_get_model(GTK_TREE_VIEW(type));
+ selectiontype = gtk_tree_view_get_selection(GTK_TREE_VIEW(type));
+ if (gtk_tree_selection_get_selected(selectiontype, &modeltype, &itertype))
+ {
+ gchar *buffer, *pic_info;
+ gint t;
+
+ gtk_tree_model_get(modeltype, &itertype,
+ PICTURE_TYPE_COLUMN_TYPE_CODE, &t, -1);
+ pic->type = t;
+
+ buffer = g_strdup(gtk_entry_get_text(GTK_ENTRY(desc)));
+ Strip_String(buffer);
+ if (pic->description)
+ g_free(pic->description);
+ if ( g_utf8_strlen(buffer, -1) > 0 )
+ {
+ pic->description = buffer;
+ }else
+ {
+ pic->description = 0;
+ g_free(buffer);
+ }
+
+ // Update value in the PictureEntryView
+ pic_info = Picture_Info(pic);
+ gtk_list_store_set(GTK_LIST_STORE(model), &iter,
+ PICTURE_COLUMN_TEXT, pic_info,
+ -1);
+ g_free(pic_info);
+ }
+ }
+ gtk_widget_destroy(PictureTypesWindow);
+
+ if (!selection_list->next) break;
+ selection_list = selection_list->next;
+ }
+}
+
+
+void Picture_Save_Button_Clicked (GObject *object)
+{
+ GtkWidget *FileSelectionWindow;
+ GtkFileFilter *filter;
+ GtkWindow *parent_window = NULL;
+ static gchar *init_dir = NULL;
+
+ GtkTreeSelection *selection;
+ GList *selection_list = NULL;
+ GtkTreeModel *model;
+ gint selection_nbr, selection_i = 1;
+
+
+ if (!PictureEntryView) return;
+
+ parent_window = (GtkWindow*) gtk_widget_get_toplevel(GTK_WIDGET(object));
+ if (!GTK_WIDGET_TOPLEVEL(parent_window))
+ {
+ g_warning("Could not get parent window\n");
+ return;
+ }
+
+ model = gtk_tree_view_get_model(GTK_TREE_VIEW(PictureEntryView));
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(PictureEntryView));
+ selection_list = gtk_tree_selection_get_selected_rows(selection, NULL);
+ selection_nbr = gtk_tree_selection_count_selected_rows(GTK_TREE_SELECTION(selection));
+
+ while (selection_list)
+ {
+ GtkTreePath *path = selection_list->data;
+ GtkTreeIter iter;
+ Picture *pic;
+ gchar *title;
+ gboolean valid;
+
+ // Get corresponding picture
+ valid = gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, path);
+ if (valid)
+ gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, PICTURE_COLUMN_DATA, &pic, -1);
+
+ title = g_strdup_printf(_("Save picture %d/%d"),selection_i++,selection_nbr);
+ FileSelectionWindow = gtk_file_chooser_dialog_new(title,
+ parent_window,
+ GTK_FILE_CHOOSER_ACTION_SAVE,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ GTK_STOCK_SAVE, GTK_RESPONSE_OK,
+ NULL);
+ g_free(title);
+
+ // Add files filters
+ // "All files" filter
+ filter = gtk_file_filter_new ();
+ gtk_file_filter_set_name(GTK_FILE_FILTER(filter), _("All Files"));
+ gtk_file_filter_add_pattern(GTK_FILE_FILTER(filter), "*");
+ gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(FileSelectionWindow), GTK_FILE_FILTER(filter));
+
+ // "PNG and JPEG" filter
+ filter = gtk_file_filter_new ();
+ gtk_file_filter_set_name(GTK_FILE_FILTER(filter), _("PNG and JPEG"));
+ gtk_file_filter_add_mime_type(GTK_FILE_FILTER(filter), "image/jpeg");
+ gtk_file_filter_add_mime_type(GTK_FILE_FILTER(filter), "image/png");
+ gtk_file_chooser_add_filter(GTK_FILE_CHOOSER (FileSelectionWindow), GTK_FILE_FILTER(filter));
+ // Make this filter the default
+ gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(FileSelectionWindow), GTK_FILE_FILTER(filter));
+
+ // Set window position
+ if (MESSAGE_BOX_POSITION_NONE)
+ gtk_window_set_position(GTK_WINDOW(FileSelectionWindow),GTK_WIN_POS_NONE);
+ else if (MESSAGE_BOX_POSITION_CENTER)
+ gtk_window_set_position(GTK_WINDOW(FileSelectionWindow),GTK_WIN_POS_CENTER);
+ else if (MESSAGE_BOX_POSITION_MOUSE)
+ gtk_window_set_position(GTK_WINDOW(FileSelectionWindow),GTK_WIN_POS_MOUSE);
+
+ gtk_dialog_set_default_response(GTK_DIALOG(FileSelectionWindow), GTK_RESPONSE_OK);
+
+ // Set the default folder if defined
+ if (init_dir)
+ gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(FileSelectionWindow),init_dir);
+
+ // Suggest a filename to the user
+ if ( pic->description && strlen(pic->description) )
+ {
+ gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(FileSelectionWindow), pic->description); //filename in UTF8
+ }else
+ {
+ gchar *image_name = NULL;
+ switch (Picture_Format(pic))
+ {
+ case PICTURE_FORMAT_JPEG :
+ image_name = g_strdup("image_name.jpg");
+ break;
+ case PICTURE_FORMAT_PNG :
+ image_name = g_strdup("image_name.png");
+ break;
+ case PICTURE_FORMAT_UNKNOWN :
+ image_name = g_strdup("image_name.ext");
+ break;
+ }
+ gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(FileSelectionWindow), image_name); //filename in UTF8
+ g_free(image_name);
+ }
+
+ if (gtk_dialog_run(GTK_DIALOG(FileSelectionWindow)) == GTK_RESPONSE_OK)
+ {
+ FILE *file;
+ gchar *filename, *filename_utf8;
+
+ // Save the directory selected for initialize next time
+ g_free(init_dir);
+ init_dir = gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER(FileSelectionWindow));
+
+ filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(FileSelectionWindow));
+ filename_utf8 = filename_to_display(filename);
+
+ // Warn user if the file already exists, else saves directly
+ if ( (file=fopen(filename_utf8,"r"))!=NULL )
+ {
+ gchar *msg;
+ GtkWidget *msgbox;
+ gint button;
+
+ fclose(file);
+
+ msg = g_strdup_printf(_("The following file already exists :\n'%s'\n"
+ "Do you want to overwrite?"),filename_utf8);
+ msgbox = msg_box_new(_("Save file..."),msg,GTK_STOCK_DIALOG_QUESTION,BUTTON_NO,BUTTON_YES,0);
+ g_free(msg);
+ msg_box_hide_check_button(MSG_BOX(msgbox));
+ button = msg_box_run(MSG_BOX(msgbox));
+ gtk_widget_destroy(msgbox);
+
+ if (button == BUTTON_YES)
+ {
+ Picture_Save_File_Data(pic, filename_utf8);
+ }
+ }else
+ {
+ Picture_Save_File_Data(pic, filename_utf8);
+ }
+ g_free(filename_utf8);
+ }
+ gtk_widget_destroy(FileSelectionWindow);
+
+ if (!selection_list->next) break;
+ selection_list = selection_list->next;
+ }
+}
+
+
+/* FIXME: Possibly use gnome_vfs_get_mime_type_for_buffer. */
+gint Picture_Format (Picture *pic)
+{
+ if (pic->data && pic->size > 2
+ && pic->data[0] == 0xff
+ && pic->data[1] == 0xd8)
+ return PICTURE_FORMAT_JPEG;
+
+ if (pic->data && pic->size > 8
+ && pic->data[0] == 0x89
+ && pic->data[1] == 0x50
+ && pic->data[2] == 0x4e
+ && pic->data[3] == 0x47
+ && pic->data[4] == 0x0d
+ && pic->data[5] == 0x0a
+ && pic->data[6] == 0x1a
+ && pic->data[7] == 0x0a)
+ return PICTURE_FORMAT_PNG;
+
+ return PICTURE_FORMAT_UNKNOWN;
+}
+
+const gchar *Picture_Format_String (gint format)
+{
+ switch (format)
+ {
+ case PICTURE_FORMAT_JPEG:
+ return _("JPEG image");
+ case PICTURE_FORMAT_PNG:
+ return _("PNG image");
+ default:
+ return _("Unknown image");
+ }
+}
+
+const gchar *Picture_Type_String (gint type)
+{
+ switch (type)
+ {
+ case PICTURE_TYPE_OTHER:
+ return _("Other");
+ case PICTURE_TYPE_FILE_ICON:
+ return _("32x32 pixel PNG file icon");
+ case PICTURE_TYPE_OTHER_FILE_ICON:
+ return _("Other file icon");
+ case PICTURE_TYPE_FRONT_COVER:
+ return _("Cover (front)");
+ case PICTURE_TYPE_BACK_COVER:
+ return _("Cover (back)");
+ case PICTURE_TYPE_LEAFLET_PAGE:
+ return _("Leaflet page");
+ case PICTURE_TYPE_MEDIA:
+ return _("Media (e.g. label side of CD)");
+ case PICTURE_TYPE_LEAD_ARTIST_LEAD_PERFORMER_SOLOIST:
+ return _("Lead artist/lead performer/soloist");
+ case PICTURE_TYPE_ARTIST_PERFORMER:
+ return _("Artist/performer");
+ case PICTURE_TYPE_CONDUCTOR:
+ return _("Conductor");
+ case PICTURE_TYPE_BAND_ORCHESTRA:
+ return _("Band/Orchestra");
+ case PICTURE_TYPE_COMPOSER:
+ return _("Composer");
+ case PICTURE_TYPE_LYRICIST_TEXT_WRITER:
+ return _("Lyricist/text writer");
+ case PICTURE_TYPE_RECORDING_LOCATION:
+ return _("Recording location");
+ case PICTURE_TYPE_DURING_RECORDING:
+ return _("During recording");
+ case PICTURE_TYPE_DURING_PERFORMANCE:
+ return _("During performance");
+ case PICTURE_TYPE_MOVIDE_VIDEO_SCREEN_CAPTURE:
+ return _("Movie/video screen capture");
+ case PICTURE_TYPE_A_BRIGHT_COLOURED_FISH:
+ return _("A bright coloured fish");
+ case PICTURE_TYPE_ILLUSTRATION:
+ return _("Illustration");
+ case PICTURE_TYPE_BAND_ARTIST_LOGOTYPE:
+ return _("Band/Artist logotype");
+ case PICTURE_TYPE_PUBLISHER_STUDIO_LOGOTYPE:
+ return _("Publisher/studio logotype");
+ default:
+ return _("Unknown picture type");
+ }
+}
+
+gchar *Picture_Info (Picture *pic)
+{
+ gchar *format, *desc, *type, *r, *size_str;
+ GString *s;
+
+ format = (gchar *)Picture_Format_String(Picture_Format(pic));
+
+ if (pic->description)
+ desc = pic->description;
+ else
+ desc = "";
+
+ type = (gchar *)Picture_Type_String(pic->type);
+ size_str = Convert_Size_1((gfloat)pic->size);
+
+ s = g_string_new(0);
+ // Behaviour following the tag type...
+ switch (ETCore->ETFileDisplayed->ETFileDescription->TagType)
+ {
+ case MP4_TAG:
+ {
+ g_string_sprintf(s, "%s (%s - %dx%d %s)\n%s: %s",
+ format,
+ size_str,
+ pic->width, pic->height, _("pixels"),
+ _("Type"), type);
+ break;
+ }
+
+ // Other tag types
+ default:
+ {
+ g_string_sprintf(s, "%s (%s - %dx%d %s)\n%s: %s\n%s: %s",
+ format,
+ size_str,
+ pic->width, pic->height, _("pixels"),
+ _("Type"), type,
+ _("Description"), desc);
+ break;
+ }
+ }
+ r = ET_Utf8_Validate_Full_String(s->str);
+ g_string_free(s, TRUE); // TRUE to free also 's->str'!
+ g_free(size_str);
+
+ return r;
+}
+
+void PictureEntry_Clear (void)
+{
+ GtkListStore *picture_store;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ Picture *pic;
+
+ model = gtk_tree_view_get_model(GTK_TREE_VIEW(PictureEntryView));
+ if (gtk_tree_model_get_iter_first(model, &iter))
+ {
+ do
+ {
+ gtk_tree_model_get(model, &iter, PICTURE_COLUMN_DATA, &pic,-1);
+ Picture_Free(pic);
+ } while (gtk_tree_model_iter_next(model, &iter));
+ }
+
+ picture_store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(PictureEntryView)));
+ if (picture_store)
+ gtk_list_store_clear(picture_store);
+}
+
+void PictureEntry_Update (Picture *pic, gint select)
+{
+ GdkPixbufLoader *loader = 0;
+
+ if (!pic || !PictureEntryView) return;
+
+ if (!pic->data)
+ {
+ PictureEntry_Clear();
+ return;
+ }
+
+ loader = gdk_pixbuf_loader_new();
+ if (loader)
+ {
+ if (gdk_pixbuf_loader_write(loader, pic->data, pic->size, 0))
+ {
+ GtkTreeSelection *selection;
+ GdkPixbuf *pixbuf;
+
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(PictureEntryView));
+
+ pixbuf = gdk_pixbuf_loader_get_pixbuf(loader);
+ if (pixbuf)
+ {
+ GtkListStore *picture_store;
+ GtkTreeIter iter1;
+ GdkPixbuf *scaled_pixbuf;
+ gint scaled_pixbuf_width;
+ gint scaled_pixbuf_height;
+ gchar *pic_info;
+
+ // Keep aspect ratio of the picture
+ pic->width = gdk_pixbuf_get_width (pixbuf);
+ pic->height = gdk_pixbuf_get_height (pixbuf);
+ if (pic->width > pic->height)
+ {
+ scaled_pixbuf_width = 96;
+ scaled_pixbuf_height = 96 * pic->height / pic->width;
+ }else
+ {
+ scaled_pixbuf_width = 96 * pic->width / pic->height;
+ scaled_pixbuf_height = 96;
+ }
+
+ scaled_pixbuf = gdk_pixbuf_scale_simple(pixbuf,
+ scaled_pixbuf_width, scaled_pixbuf_height,
+ //GDK_INTERP_NEAREST); // Lower quality but better speed
+ GDK_INTERP_BILINEAR);
+ gdk_pixbuf_unref(pixbuf);
+
+ picture_store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(PictureEntryView)));
+ gtk_list_store_append(picture_store, &iter1);
+ pic_info = Picture_Info(pic);
+ gtk_list_store_set(picture_store, &iter1,
+ PICTURE_COLUMN_PIC, scaled_pixbuf,
+ PICTURE_COLUMN_TEXT, pic_info,
+ PICTURE_COLUMN_DATA, Picture_Copy_One(pic),
+ -1);
+ g_free(pic_info);
+
+ if (select)
+ gtk_tree_selection_select_iter(selection, &iter1);
+ gdk_pixbuf_unref(scaled_pixbuf);
+ }else
+ {
+ GtkWidget *msgbox = NULL;
+ gchar *msg = NULL;
+
+ msg = g_strdup(_("Can't display the picture, as not enough data "
+ "has been read to determine how to create the image buffer."));
+ Log_Print("%s",msg);
+ msgbox = msg_box_new(_("Loading Picture File..."),msg,
+ GTK_STOCK_DIALOG_ERROR,BUTTON_YES,0);
+ msg_box_hide_check_button(MSG_BOX(msgbox));
+ g_free(msg);
+ msg_box_run(MSG_BOX(msgbox));
+ gtk_widget_destroy(msgbox);
+ }
+ }
+ }
+ gdk_pixbuf_loader_close(loader, 0);
+
+ // Do also for next picture
+ if (pic->next)
+ PictureEntry_Update(pic->next, select);
+
+ return;
+}
+
+
+Picture *Picture_Allocate (void)
+{
+ Picture *pic = g_malloc0(sizeof(Picture));
+ return pic;
+}
+
+Picture *Picture_Copy_One (const Picture *pic)
+{
+ Picture *pic2;
+
+ if (!pic)
+ return 0;
+ pic2 = Picture_Allocate();
+ pic2->type = pic->type;
+ pic2->width = pic->width;
+ pic2->height = pic->height;
+ if (pic->description)
+ pic2->description = g_strdup(pic->description);
+ if (pic->data)
+ {
+ pic2->size = pic->size;
+ pic2->data = g_malloc(pic2->size);
+ memcpy(pic2->data, pic->data, pic->size);
+ }
+ return pic2;
+}
+
+Picture *Picture_Copy (const Picture *pic)
+{
+ Picture *pic2 = Picture_Copy_One(pic);
+ if (pic->next)
+ pic2->next = Picture_Copy(pic->next);
+ return pic2;
+}
+
+void Picture_Free (Picture *pic)
+{
+ if (!pic)
+ return;
+ if (pic->next)
+ Picture_Free(pic->next);
+ if (pic->description)
+ g_free(pic->description);
+ if (pic->data)
+ g_free(pic->data);
+ g_free(pic);
+}
+
+
+/*
+ * Load the picture represented by the 'filename' (must be passed in
+ * file system encoding, not UTF-8)
+ */
+Picture *Picture_Load_File_Data (const gchar *filename)
+{
+ Picture *pic;
+ gchar *buffer = 0;
+ size_t size = 0;
+ struct stat st;
+
+ if (lstat(filename, &st)==-1)
+ return (Picture *)NULL;
+
+ size = st.st_size;
+ buffer = g_malloc(size);
+
+ FILE *fd = fopen(filename, "rb");
+ if (!fd)
+ {
+ gchar *msg;
+ gchar *filename_utf8;
+ GtkWidget *msgbox;
+
+ /* Picture file not opened */
+ filename_utf8 = filename_to_display(filename);
+ msg = g_strdup_printf(_("Can't open file :\n'%s'!\n(%s)"),
+ filename_utf8,g_strerror(errno));
+ msgbox = msg_box_new(_("Error..."),msg,GTK_STOCK_DIALOG_ERROR,BUTTON_OK,0);
+ g_free(msg);
+ msg_box_hide_check_button(MSG_BOX(msgbox));
+ msg_box_run(MSG_BOX(msgbox));
+ gtk_widget_destroy(msgbox);
+
+ Statusbar_Message(_("Picture file not loaded..."),TRUE);
+ g_free(filename_utf8);
+ return FALSE;
+ }
+
+ if (fread(buffer, size, 1, fd) != 1)
+ goto fail;
+
+ fclose(fd);
+
+ pic = Picture_Allocate();
+ pic->size = size;
+ pic->data = (guchar *)buffer;
+ return pic;
+
+ fail:
+ if (buffer)
+ g_free(buffer);
+ fclose(fd);
+ return (Picture *)NULL;
+}
+
+/*
+ * Save picture data to a file (jpeg, png)
+ */
+gboolean Picture_Save_File_Data (const Picture *pic, const gchar *filename)
+{
+ gint fd;
+
+ fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0666);
+ if (fd == -1)
+ return FALSE;
+
+ if (write(fd, pic->data, pic->size) != pic->size)
+ {
+ close(fd);
+ return FALSE;
+ }
+
+ close(fd);
+ return TRUE;
+}
+
+/*
+ * If double clicking the PictureEntryView :
+ * - over a selected row : opens properties window
+ * - over an empty area : open the adding window
+ */
+gboolean Picture_Entry_View_Button_Pressed (GtkTreeView *treeview, GdkEventButton *event, gpointer data)
+{
+ if (event->type==GDK_2BUTTON_PRESS && event->button==1)
+ {
+ GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(PictureEntryView));
+
+ if (gtk_tree_selection_count_selected_rows(GTK_TREE_SELECTION(selection)) != 0)
+ Picture_Properties_Button_Clicked(G_OBJECT(PicturePropertiesButton));
+ else
+ Picture_Add_Button_Clicked(G_OBJECT(PictureAddButton));
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+/*
+ * Key press into picture entry
+ * - Delete = delete selected picture files
+ */
+gboolean Picture_Entry_View_Key_Pressed (GtkTreeView *treeview, GdkEvent *event, gpointer data)
+{
+ GdkEventKey *kevent;
+
+ kevent = (GdkEventKey *)event;
+ if (event && event->type==GDK_KEY_PRESS)
+ {
+ switch(kevent->keyval)
+ {
+ case GDK_Delete:
+ Picture_Clear_Button_Clicked(NULL);
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
diff --git a/src/picture.h b/src/picture.h
new file mode 100755
index 0000000..eda3af4
--- /dev/null
+++ b/src/picture.h
@@ -0,0 +1,125 @@
+/* picture.h - 2004/11/21 */
+/*
+ * EasyTAG - Tag editor for MP3 and Ogg Vorbis files
+ * Copyright (C) 2000-2003 Jerome Couderc <easytag@gmail.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef __PICTURE_H__
+#define __PICTURE_H__
+
+#include "et_core.h"
+
+
+/***************
+ * Declaration *
+ ***************/
+
+/* Defined in et_core.h
+typedef struct _Picture Picture;
+struct _Picture
+{
+ gint type;
+ gchar *description;
+ gint width; // Original width of the picture
+ gint height; // Original height of the picture
+ gulong size; // Picture size in bits
+ guchar *data;
+ Picture *next;
+};*/
+
+enum // Picture types
+{
+ PICTURE_TYPE_OTHER,
+ PICTURE_TYPE_FILE_ICON,
+ PICTURE_TYPE_OTHER_FILE_ICON,
+ PICTURE_TYPE_FRONT_COVER,
+ PICTURE_TYPE_BACK_COVER,
+ PICTURE_TYPE_LEAFLET_PAGE,
+ PICTURE_TYPE_MEDIA,
+ PICTURE_TYPE_LEAD_ARTIST_LEAD_PERFORMER_SOLOIST,
+ PICTURE_TYPE_ARTIST_PERFORMER,
+ PICTURE_TYPE_CONDUCTOR,
+ PICTURE_TYPE_BAND_ORCHESTRA,
+ PICTURE_TYPE_COMPOSER,
+ PICTURE_TYPE_LYRICIST_TEXT_WRITER,
+ PICTURE_TYPE_RECORDING_LOCATION,
+ PICTURE_TYPE_DURING_RECORDING,
+ PICTURE_TYPE_DURING_PERFORMANCE,
+ PICTURE_TYPE_MOVIDE_VIDEO_SCREEN_CAPTURE,
+ PICTURE_TYPE_A_BRIGHT_COLOURED_FISH,
+ PICTURE_TYPE_ILLUSTRATION,
+ PICTURE_TYPE_BAND_ARTIST_LOGOTYPE,
+ PICTURE_TYPE_PUBLISHER_STUDIO_LOGOTYPE
+};
+
+enum
+{
+ PICTURE_FORMAT_JPEG,
+ PICTURE_FORMAT_PNG,
+ PICTURE_FORMAT_UNKNOWN
+};
+
+enum // Columns for PictureEntryView
+{
+ PICTURE_COLUMN_PIC, // Column 0
+ PICTURE_COLUMN_TEXT,
+ PICTURE_COLUMN_DATA,
+ PICTURE_COLUMN_COUNT
+};
+
+enum // Columns for list in properties window
+{
+ PICTURE_TYPE_COLUMN_TEXT, // Column 0
+ PICTURE_TYPE_COLUMN_TYPE_CODE,
+ PICTURE_TYPE_COLUMN_COUNT
+};
+
+enum
+{
+ TARGET_URI_LIST
+};
+
+
+/**************
+ * Prototypes *
+ **************/
+
+void Tag_Area_Picture_Drag_Data (GtkWidget *widget, GdkDragContext *dc,
+ gint x, gint y, GtkSelectionData *selection_data,
+ guint info, guint t, gpointer data);
+void Picture_Selection_Changed_cb (GtkTreeSelection *selection, gpointer data);
+
+void Picture_Add_Button_Clicked (GObject *object);
+void Picture_Properties_Button_Clicked (GObject *object);
+void Picture_Save_Button_Clicked (GObject *object);
+void Picture_Clear_Button_Clicked (GObject *object);
+
+void PictureEntry_Clear (void);
+void PictureEntry_Update (Picture *pic, gint select);
+
+Picture *Picture_Allocate (void);
+Picture *Picture_Copy_One (const Picture *pic);
+Picture *Picture_Copy (const Picture *pic);
+void Picture_Free (Picture *pic);
+gint Picture_Format (Picture *pic);
+
+gboolean Picture_Entry_View_Button_Pressed (GtkTreeView *treeview, GdkEventButton *event, gpointer data);
+gboolean Picture_Entry_View_Key_Pressed (GtkTreeView *treeview, GdkEvent *event, gpointer data);
+
+
+#endif /* __PICTURE_H__ */
diff --git a/src/prefs.c b/src/prefs.c
new file mode 100755
index 0000000..5b649e6
--- /dev/null
+++ b/src/prefs.c
@@ -0,0 +1,1857 @@
+/* prefs.c - 2000/05/06 */
+/*
+ * EasyTAG - Tag editor for MP3 and Ogg Vorbis files
+ * Copyright (C) 2000-2003 Jerome Couderc <easytag@gmail.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+
+#include <gtk/gtk.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <gdk/gdkkeysyms.h>
+#include <gdk/gdk.h>
+#include <glib/gi18n-lib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "prefs.h"
+#include "setting.h"
+#include "msgbox.h"
+#include "bar.h"
+#include "misc.h"
+#include "scan.h"
+#include "easytag.h"
+#include "browser.h"
+#include "cddb.h"
+#include "charset.h"
+
+//#ifdef WIN32
+//# include "win32/win32dep.h"
+//#endif
+
+
+/**************
+ * Prototypes *
+ **************/
+/* Options window */
+void OptionsWindow_Quit (void);
+void OptionsWindow_Apply_Button (void);
+void OptionsWindow_Save_Button (void);
+void OptionsWindow_Cancel_Button (void);
+gboolean OptionsWindow_Key_Press (GtkWidget *window, GdkEvent *event);
+gint Check_Config (void);
+
+void Set_Default_Comment_Check_Button_Toggled (void);
+void Number_Track_Formated_Toggled (void);
+void Number_Track_Formated_Spin_Button_Changed (GtkObject *Label, GtkObject *SpinButton);
+void Change_Id3_Settings_Toggled (void);
+void File_Writing_Id3v2_Write_Tag_Toggled(void);
+void Use_Non_Standard_Id3_Reading_Character_Set_Toggled (void);
+void Scanner_Convert_Check_Button_Toggled_1 (GtkObject *object_rec, GtkObject *object_emi);
+void Cddb_Use_Proxy_Toggled (void);
+
+void DefaultPathToMp3_Combo_Add_String (void);
+void CddbLocalPath_Combo_Add_String (void);
+
+
+
+/*************
+ * Functions *
+ *************/
+void Init_OptionsWindow (void)
+{
+ OptionsWindow = (GtkWidget *)NULL;
+}
+
+/*
+ * The window for options
+ */
+void Open_OptionsWindow (void)
+{
+ GtkWidget *OptionsVBox;
+ GtkWidget *ButtonBox;
+ GtkWidget *Button;
+ GtkWidget *Label;
+ GtkWidget *Frame;
+ GtkWidget *Table;
+ GtkWidget *VBox, *vbox;
+ GtkWidget *HBox, *hbox, *id3v1v2hbox;
+ GtkWidget *Separator;
+ GtkWidget *EventBox;
+ GtkTooltips *Tips;
+ gchar temp[MAX_STRING_LEN];
+ gchar *path_utf8;
+ gchar *program_path;
+
+ /* Check if already opened */
+ if (OptionsWindow)
+ {
+ gdk_window_show(OptionsWindow->window);
+ return;
+ }
+
+ /* The window */
+ OptionsWindow = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ /* Config */
+ gtk_window_set_position(GTK_WINDOW(OptionsWindow),GTK_WIN_POS_CENTER);
+ gtk_window_set_transient_for(GTK_WINDOW(OptionsWindow),GTK_WINDOW(MainWindow));
+ gtk_container_set_border_width(GTK_CONTAINER(OptionsWindow), 5);
+ gtk_window_set_default_size(GTK_WINDOW(OptionsWindow),OPTIONS_WINDOW_WIDTH,OPTIONS_WINDOW_HEIGHT);
+ /* Title */
+ gtk_window_set_title(GTK_WINDOW(OptionsWindow),_("Preferences..."));
+
+ /* Signals connection */
+ g_signal_connect(G_OBJECT(OptionsWindow),"destroy", G_CALLBACK(OptionsWindow_Quit),NULL);
+ g_signal_connect(G_OBJECT(OptionsWindow),"delete_event",G_CALLBACK(OptionsWindow_Quit),NULL);
+ g_signal_connect(G_OBJECT(OptionsWindow),"key_press_event",
+ G_CALLBACK(OptionsWindow_Key_Press),NULL);
+
+ Tips = gtk_tooltips_new();
+
+ /* Options */
+ /* The vbox */
+ OptionsVBox = gtk_vbox_new(FALSE,0);
+ gtk_box_set_spacing (GTK_BOX(OptionsVBox),5);
+ gtk_container_add(GTK_CONTAINER(OptionsWindow),OptionsVBox);
+
+ /* Options NoteBook */
+ OptionsNoteBook = gtk_notebook_new();
+ gtk_notebook_popup_enable(GTK_NOTEBOOK(OptionsNoteBook));
+ gtk_notebook_set_scrollable(GTK_NOTEBOOK(OptionsNoteBook),TRUE);
+ gtk_box_pack_start(GTK_BOX(OptionsVBox),OptionsNoteBook,TRUE,TRUE,0);
+
+
+
+ /*
+ * Browser
+ */
+ Label = gtk_label_new(_("Browser"));
+ Frame = gtk_frame_new(_("Browser"));
+ gtk_notebook_append_page(GTK_NOTEBOOK(OptionsNoteBook),Frame,Label);
+ gtk_container_set_border_width(GTK_CONTAINER(Frame), 5);
+
+ VBox = gtk_vbox_new(FALSE,4);
+ gtk_container_add(GTK_CONTAINER(Frame),VBox);
+ gtk_container_set_border_width(GTK_CONTAINER(VBox), 4);
+
+
+ /* File Browser frame */
+ Frame = gtk_frame_new(_("File Browser"));
+ gtk_box_pack_start(GTK_BOX(VBox),Frame,FALSE,FALSE,0);
+ vbox = gtk_vbox_new(FALSE,0);
+ gtk_container_add(GTK_CONTAINER(Frame),vbox);
+ gtk_container_set_border_width(GTK_CONTAINER(vbox), 2);
+
+ /* Default directory */
+ HBox = gtk_hbox_new(FALSE,2);
+ gtk_box_pack_start(GTK_BOX(vbox),HBox,FALSE,FALSE,0);
+
+ // Label
+ Label = gtk_label_new(_("Default directory :"));
+ gtk_box_pack_start(GTK_BOX(HBox),Label,FALSE,FALSE,0);
+
+ // Combo
+ if (DefaultPathModel != NULL)
+ gtk_list_store_clear(DefaultPathModel);
+ else
+ DefaultPathModel = gtk_list_store_new(MISC_COMBO_COUNT, G_TYPE_STRING);
+
+ DefaultPathToMp3 = gtk_combo_box_entry_new_with_model(GTK_TREE_MODEL(DefaultPathModel), MISC_COMBO_TEXT);
+ gtk_box_pack_start(GTK_BOX(HBox),DefaultPathToMp3,TRUE,TRUE,0);
+ gtk_widget_set_size_request(DefaultPathToMp3, 400, -1);
+ gtk_tooltips_set_tip(Tips,GTK_BIN(DefaultPathToMp3)->child,_("Specify the directory where "
+ "your files are located. This path will be loaded when EasyTAG starts without parameter."),NULL);
+ g_signal_connect(G_OBJECT(GTK_ENTRY(GTK_BIN(DefaultPathToMp3)->child)),"activate",G_CALLBACK(DefaultPathToMp3_Combo_Add_String),NULL);
+ //g_signal_connect(G_OBJECT(GTK_ENTRY(GTK_BIN(DefaultPathToMp3)->child)),"focus_out_event",G_CALLBACK(DefaultPathToMp3_Combo_Add_String),NULL);
+
+ // History list
+ Load_Default_Path_To_MP3_List(DefaultPathModel, MISC_COMBO_TEXT);
+ // If default path hasn't been added already, add it now..
+ path_utf8 = filename_to_display(DEFAULT_PATH_TO_MP3);
+ Add_String_To_Combo_List(DefaultPathModel, path_utf8);
+ if (path_utf8)
+ gtk_entry_set_text(GTK_ENTRY(GTK_BIN(DefaultPathToMp3)->child), path_utf8);
+ g_free(path_utf8);
+
+ // Button browse
+ Button = Create_Button_With_Pixmap(BUTTON_BROWSE);
+ gtk_box_pack_start(GTK_BOX(HBox),Button,FALSE,FALSE,0);
+ g_signal_connect_swapped(G_OBJECT(Button),"clicked",
+ G_CALLBACK(File_Selection_Window_For_Directory),G_OBJECT(GTK_BIN(DefaultPathToMp3)->child));
+
+ /* Load directory on startup */
+ LoadOnStartup = gtk_check_button_new_with_label(_("Load on startup the default directory or the directory passed as argument"));
+ gtk_box_pack_start(GTK_BOX(vbox),LoadOnStartup,FALSE,FALSE,0);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(LoadOnStartup),LOAD_ON_STARTUP);
+ gtk_tooltips_set_tip(Tips,LoadOnStartup,_("Automatically search files, when EasyTAG starts, "
+ "into the default directory. Note that this path may be overriden by the parameter "
+ "passed to easytag (easytag /path_to/mp3_files)."),NULL);
+
+ /* Browse subdirectories */
+ BrowseSubdir = gtk_check_button_new_with_label(_("Search subdirectories"));
+ gtk_box_pack_start(GTK_BOX(vbox),BrowseSubdir,FALSE,FALSE,0);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(BrowseSubdir),BROWSE_SUBDIR);
+ gtk_tooltips_set_tip(Tips,BrowseSubdir,_("Search subdirectories for files when reading "
+ "a directory into the tree."),NULL);
+
+ /* Open the node to show subdirectories */
+ OpenSelectedBrowserNode = gtk_check_button_new_with_label(_("Show subdirectories when selecting "
+ "a directory"));
+ gtk_box_pack_start(GTK_BOX(vbox),OpenSelectedBrowserNode,FALSE,FALSE,0);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(OpenSelectedBrowserNode),OPEN_SELECTED_BROWSER_NODE);
+ gtk_tooltips_set_tip(Tips,OpenSelectedBrowserNode,_("This expands the selected node into the file "
+ "browser to display the sub-directories."),NULL);
+
+ /* Browse hidden directories */
+ BrowseHiddendir = gtk_check_button_new_with_label(_("Search hidden directories"));
+#ifndef WIN32 /* Always true and not user modifiable on win32 */
+ gtk_box_pack_start(GTK_BOX(vbox),BrowseHiddendir,FALSE,FALSE,0);
+#endif
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(BrowseHiddendir),BROWSE_HIDDEN_DIR);
+ gtk_tooltips_set_tip(Tips,BrowseHiddendir,_("Search hidden directories for files "
+ "(directories starting by a '.')."),NULL);
+
+
+
+ /*
+ * Misc
+ */
+ Label = gtk_label_new (_("Misc"));
+ Frame = gtk_frame_new (_("Misc"));
+ gtk_notebook_append_page (GTK_NOTEBOOK(OptionsNoteBook),Frame,Label);
+ gtk_container_set_border_width(GTK_CONTAINER(Frame), 5);
+
+ VBox = gtk_vbox_new(FALSE,4);
+ gtk_container_add(GTK_CONTAINER(Frame),VBox);
+ gtk_container_set_border_width(GTK_CONTAINER(VBox), 4);
+
+
+ /* User interface */
+ Frame = gtk_frame_new (_("User Interface"));
+ gtk_box_pack_start(GTK_BOX(VBox),Frame,FALSE,FALSE,0);
+ vbox = gtk_vbox_new(FALSE,2);
+ gtk_container_add(GTK_CONTAINER(Frame),vbox);
+ gtk_container_set_border_width(GTK_CONTAINER(vbox), 2);
+
+ // Show header infos
+ ShowHeaderInfos = gtk_check_button_new_with_label(_("Show header informations of file"));
+ gtk_box_pack_start(GTK_BOX(vbox),ShowHeaderInfos,FALSE,FALSE,0);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ShowHeaderInfos),SHOW_HEADER_INFO);
+ gtk_tooltips_set_tip(Tips,ShowHeaderInfos,_("If activated, informations about the file as "
+ "the bitrate, the time, the size, will be displayed under the filename entry."),NULL);
+
+ // Display color mode for changed files in list
+ hbox = gtk_hbox_new(FALSE,2);
+ gtk_box_pack_start(GTK_BOX(vbox),hbox,FALSE,FALSE,0);
+ gtk_container_set_border_width(GTK_CONTAINER(hbox), 2);
+ Label = gtk_label_new(_("Display changed files in list using :"));
+ gtk_box_pack_start(GTK_BOX(hbox),Label,FALSE,FALSE,0);
+
+ ChangedFilesDisplayedToRed = gtk_radio_button_new_with_label(NULL,_("Red color"));
+ gtk_box_pack_start(GTK_BOX(hbox),ChangedFilesDisplayedToRed,FALSE,FALSE,4);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ChangedFilesDisplayedToRed),CHANGED_FILES_DISPLAYED_TO_RED);
+
+ // Set "new" Gtk+-2.0ish black/bold style for changed items
+ ChangedFilesDisplayedToBold = gtk_radio_button_new_with_label(
+ gtk_radio_button_get_group(GTK_RADIO_BUTTON(ChangedFilesDisplayedToRed)),_("Bold style"));
+ gtk_box_pack_start(GTK_BOX(hbox),ChangedFilesDisplayedToBold,FALSE,FALSE,2);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ChangedFilesDisplayedToBold),CHANGED_FILES_DISPLAYED_TO_BOLD);
+
+
+ /* Sorting List Options */
+ Frame = gtk_frame_new (_("Sorting List Options"));
+ gtk_box_pack_start(GTK_BOX(VBox),Frame,FALSE,FALSE,0);
+ vbox = gtk_vbox_new(FALSE,2);
+ gtk_container_add(GTK_CONTAINER(Frame),vbox);
+ gtk_container_set_border_width(GTK_CONTAINER(vbox), 2);
+
+ hbox = gtk_hbox_new(FALSE,2);
+ gtk_box_pack_start(GTK_BOX(vbox),hbox,FALSE,FALSE,0);
+ gtk_container_set_border_width(GTK_CONTAINER(hbox), 2);
+ /* Sorting method */
+ Label = gtk_label_new(_("Sort the file list by :"));
+ gtk_box_pack_start(GTK_BOX(hbox),Label,FALSE,FALSE,0);
+
+ EventBox = gtk_event_box_new();
+ SortingFileCombo = gtk_combo_box_new_text();
+ gtk_container_add(GTK_CONTAINER(EventBox),SortingFileCombo);
+ gtk_box_pack_start(GTK_BOX(hbox),EventBox,FALSE,FALSE,2);
+ gtk_widget_set_size_request(GTK_WIDGET(SortingFileCombo), 260, -1);
+ gtk_combo_box_set_wrap_width(GTK_COMBO_BOX(SortingFileCombo),2); // Two columns
+
+ // Items of option menu
+ gtk_combo_box_append_text(GTK_COMBO_BOX(SortingFileCombo), _("Ascending file name"));
+ gtk_combo_box_append_text(GTK_COMBO_BOX(SortingFileCombo), _("Descending file name"));
+ gtk_combo_box_append_text(GTK_COMBO_BOX(SortingFileCombo), _("Ascending track number"));
+ gtk_combo_box_append_text(GTK_COMBO_BOX(SortingFileCombo), _("Descending track number"));
+ gtk_combo_box_append_text(GTK_COMBO_BOX(SortingFileCombo), _("Ascending creation date"));
+ gtk_combo_box_append_text(GTK_COMBO_BOX(SortingFileCombo), _("Descending creation date"));
+ gtk_combo_box_append_text(GTK_COMBO_BOX(SortingFileCombo), _("Ascending title"));
+ gtk_combo_box_append_text(GTK_COMBO_BOX(SortingFileCombo), _("Descending title"));
+ gtk_combo_box_append_text(GTK_COMBO_BOX(SortingFileCombo), _("Ascending artist"));
+ gtk_combo_box_append_text(GTK_COMBO_BOX(SortingFileCombo), _("Descending artist"));
+ gtk_combo_box_append_text(GTK_COMBO_BOX(SortingFileCombo), _("Ascending album"));
+ gtk_combo_box_append_text(GTK_COMBO_BOX(SortingFileCombo), _("Descending album"));
+ gtk_combo_box_append_text(GTK_COMBO_BOX(SortingFileCombo), _("Ascending year"));
+ gtk_combo_box_append_text(GTK_COMBO_BOX(SortingFileCombo), _("Descending year"));
+ gtk_combo_box_append_text(GTK_COMBO_BOX(SortingFileCombo), _("Ascending genre"));
+ gtk_combo_box_append_text(GTK_COMBO_BOX(SortingFileCombo), _("Descending genre"));
+ gtk_combo_box_append_text(GTK_COMBO_BOX(SortingFileCombo), _("Ascending comment"));
+ gtk_combo_box_append_text(GTK_COMBO_BOX(SortingFileCombo), _("Descending comment"));
+
+ gtk_combo_box_set_active(GTK_COMBO_BOX(SortingFileCombo), SORTING_FILE_MODE);
+ gtk_tooltips_set_tip(Tips,EventBox,_("Select the type of file sorting "
+ "when loading a directory."),NULL);
+
+ SortingFileCaseSensitive = gtk_check_button_new_with_label(_("Case sensitive"));
+#ifndef WIN32 /* Always true and not user modifiable on win32, as strncasecmp() doesn't work correctly with g_utf8_collate_key() */
+ gtk_box_pack_start(GTK_BOX(hbox),SortingFileCaseSensitive,FALSE,FALSE,0);
+#endif
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(SortingFileCaseSensitive),
+ SORTING_FILE_CASE_SENSITIVE);
+ gtk_tooltips_set_tip(Tips,SortingFileCaseSensitive,_("If activated, the "
+ "sorting of the list will be dependent on the case."),NULL);
+
+ /* Message Dialog Position */
+ Frame = gtk_frame_new (_("Message Dialog Position"));
+ gtk_box_pack_start(GTK_BOX(VBox),Frame,FALSE,FALSE,0);
+ Table = gtk_table_new(2,2,FALSE);
+ gtk_container_add(GTK_CONTAINER(Frame),Table);
+ //gtk_table_set_row_spacings(GTK_TABLE(Table),2);
+ gtk_table_set_col_spacings(GTK_TABLE(Table),4);
+
+
+ MessageBoxPositionNone = gtk_radio_button_new_with_label(NULL,_("No particular position"));
+ gtk_table_attach(GTK_TABLE(Table),MessageBoxPositionNone,0,1,0,1,GTK_FILL,GTK_FILL,0,0);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(MessageBoxPositionNone),
+ MESSAGE_BOX_POSITION_NONE);
+ gtk_tooltips_set_tip(Tips,MessageBoxPositionNone,_("Let the Window Manager "
+ "to place the windows."),NULL);
+
+ MessageBoxPositionCenterOnParent = gtk_radio_button_new_with_label(
+ gtk_radio_button_get_group(GTK_RADIO_BUTTON(MessageBoxPositionNone)),
+ _("Center of the main window"));
+ gtk_table_attach(GTK_TABLE(Table),MessageBoxPositionCenterOnParent,1,2,0,1,GTK_FILL,GTK_FILL,0,0);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(MessageBoxPositionCenterOnParent),
+ MESSAGE_BOX_POSITION_CENTER_ON_PARENT);
+ gtk_tooltips_set_tip(Tips,MessageBoxPositionCenterOnParent,_("Windows should "
+ "be placed in the center of the main window."),NULL);
+
+ MessageBoxPositionCenter = gtk_radio_button_new_with_label(
+ gtk_radio_button_get_group(GTK_RADIO_BUTTON(MessageBoxPositionNone)),
+ _("Center of the screen"));
+ gtk_table_attach(GTK_TABLE(Table),MessageBoxPositionCenter,0,1,1,2,GTK_FILL,GTK_FILL,0,0);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(MessageBoxPositionCenter),
+ MESSAGE_BOX_POSITION_CENTER);
+ gtk_tooltips_set_tip(Tips,MessageBoxPositionCenter,_("Windows should be placed "
+ "in the center of the screen."),NULL);
+
+ MessageBoxPositionMouse = gtk_radio_button_new_with_label(
+ gtk_radio_button_get_group(GTK_RADIO_BUTTON(MessageBoxPositionNone)),
+ _("Mouse position"));
+ gtk_table_attach(GTK_TABLE(Table),MessageBoxPositionMouse,1,2,1,2,GTK_FILL,GTK_FILL,0,0);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(MessageBoxPositionMouse),
+ MESSAGE_BOX_POSITION_MOUSE);
+ gtk_tooltips_set_tip(Tips,MessageBoxPositionMouse,_("Windows should be placed "
+ "at the current mouse position."),NULL);
+
+ /* File Player */
+ Frame = gtk_frame_new (_("File Audio Player"));
+ gtk_box_pack_start(GTK_BOX(VBox),Frame,FALSE,FALSE,0);
+
+ // Player name with params
+ if (FilePlayerModel == NULL)
+ FilePlayerModel = gtk_list_store_new(MISC_COMBO_COUNT, G_TYPE_STRING);
+ else
+ gtk_list_store_clear(FilePlayerModel);
+
+ hbox = gtk_hbox_new(FALSE,2);
+ gtk_container_add(GTK_CONTAINER(Frame),hbox);
+ gtk_container_set_border_width(GTK_CONTAINER(hbox), 4);
+ Label = gtk_label_new (_("Player to run :"));
+ gtk_box_pack_start(GTK_BOX(hbox),Label,FALSE,FALSE,0);
+ FilePlayerCombo = gtk_combo_box_entry_new_with_model(GTK_TREE_MODEL(FilePlayerModel), MISC_COMBO_TEXT);
+ gtk_widget_set_size_request(GTK_WIDGET(FilePlayerCombo), 300, -1);
+ gtk_box_pack_start(GTK_BOX(hbox),FilePlayerCombo,FALSE,FALSE,0);
+ gtk_tooltips_set_tip(Tips,GTK_BIN(FilePlayerCombo)->child,_("Enter the program used to "
+ "play the files. Some arguments can be passed for the program (as 'xmms -p') before "
+ "to receive files as other arguments."),NULL);
+ // History List
+ Load_Audio_File_Player_List(FilePlayerModel, MISC_COMBO_TEXT);
+ Add_String_To_Combo_List(FilePlayerModel, AUDIO_FILE_PLAYER);
+ // Don't load the parameter if XMMS not found, else user can't save the preference
+ if ( (program_path=Check_If_Executable_Exists(AUDIO_FILE_PLAYER)))
+ gtk_entry_set_text(GTK_ENTRY(GTK_BIN(FilePlayerCombo)->child), AUDIO_FILE_PLAYER);
+ g_free(program_path);
+
+ // Button browse
+ Button = Create_Button_With_Pixmap(BUTTON_BROWSE);
+ gtk_box_pack_start(GTK_BOX(hbox),Button,FALSE,FALSE,0);
+ g_signal_connect_swapped(G_OBJECT(Button),"clicked",
+ G_CALLBACK(File_Selection_Window_For_File), G_OBJECT(GTK_BIN(FilePlayerCombo)->child));
+
+
+
+ /*
+ * File Settings
+ */
+ Label = gtk_label_new (_("File Settings"));
+ Frame = gtk_frame_new (_("File Settings"));
+ gtk_notebook_append_page (GTK_NOTEBOOK(OptionsNoteBook),Frame,Label);
+ gtk_container_set_border_width(GTK_CONTAINER(Frame),5);
+
+ VBox = gtk_vbox_new(FALSE,2);
+ gtk_container_add(GTK_CONTAINER(Frame),VBox);
+ gtk_container_set_border_width(GTK_CONTAINER(VBox),4);
+
+
+ /* File (name) Options */
+ Frame = gtk_frame_new (_("File Options"));
+ gtk_box_pack_start(GTK_BOX(VBox),Frame,FALSE,FALSE,0);
+ vbox = gtk_vbox_new(FALSE,2);
+ gtk_container_add(GTK_CONTAINER(Frame),vbox);
+ gtk_container_set_border_width(GTK_CONTAINER(vbox), 2);
+
+ ReplaceIllegalCharactersInFilename = gtk_check_button_new_with_label(_("Replace illegal characters in filename (for Windows and CD-Rom)"));
+ gtk_box_pack_start(GTK_BOX(vbox),ReplaceIllegalCharactersInFilename,FALSE,FALSE,0);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ReplaceIllegalCharactersInFilename),REPLACE_ILLEGAL_CHARACTERS_IN_FILENAME);
+ gtk_tooltips_set_tip(Tips,ReplaceIllegalCharactersInFilename,_("Convert illegal characters for "
+ "FAT32/16 and ISO9660 + Joliet filesystems ('\\', ':', ';', '*', '?', '\"', '<', '>', '|') "
+ "of the filename to avoid problem when renaming the file. This is usefull when renaming the "
+ "file from the tag with the scanner."),NULL);
+
+ hbox = gtk_hbox_new(FALSE,2);
+ gtk_box_pack_start(GTK_BOX(vbox),hbox,FALSE,FALSE,0);
+ gtk_container_set_border_width(GTK_CONTAINER(hbox), 2);
+ /* Extension case (lower/upper?) */
+ Label = gtk_label_new(_("Convert filename extension to :"));
+ gtk_box_pack_start(GTK_BOX(hbox),Label,FALSE,FALSE,0);
+
+ FilenameExtensionLowerCase = gtk_radio_button_new_with_label(NULL,_("Lower Case"));
+ gtk_box_pack_start(GTK_BOX(hbox),FilenameExtensionLowerCase,FALSE,FALSE,2);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(FilenameExtensionLowerCase),FILENAME_EXTENSION_LOWER_CASE);
+ gtk_tooltips_set_tip(Tips,FilenameExtensionLowerCase,_("For example, the extension will be converted to '.mp3'"),NULL);
+
+ FilenameExtensionUpperCase = gtk_radio_button_new_with_label(
+ gtk_radio_button_get_group(GTK_RADIO_BUTTON(FilenameExtensionLowerCase)),_("Upper Case"));
+ gtk_box_pack_start(GTK_BOX(hbox),FilenameExtensionUpperCase,FALSE,FALSE,2);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(FilenameExtensionUpperCase),FILENAME_EXTENSION_UPPER_CASE);
+ gtk_tooltips_set_tip(Tips,FilenameExtensionUpperCase,_("For example, the extension will be converted to '.MP3'"),NULL);
+
+ FilenameExtensionNoChange = gtk_radio_button_new_with_label(
+ gtk_radio_button_get_group(GTK_RADIO_BUTTON(FilenameExtensionLowerCase)),_("No Change"));
+ gtk_box_pack_start(GTK_BOX(hbox),FilenameExtensionNoChange,FALSE,FALSE,2);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(FilenameExtensionNoChange),FILENAME_EXTENSION_NO_CHANGE);
+ gtk_tooltips_set_tip(Tips,FilenameExtensionNoChange,_("The extension will not be converted"),NULL);
+
+ /* Preserve modification time */
+ PreserveModificationTime = gtk_check_button_new_with_label(_("Preserve modification time of the file"));
+ gtk_box_pack_start(GTK_BOX(vbox),PreserveModificationTime,FALSE,FALSE,0);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(PreserveModificationTime),PRESERVE_MODIFICATION_TIME);
+ gtk_tooltips_set_tip(Tips,PreserveModificationTime,_("Preserve the modification time "
+ "(in file properties) when saving the file."),NULL);
+
+
+ /* Character Set for File Name */
+ Frame = gtk_frame_new (_("Character Set for File Name"));
+ gtk_box_pack_start(GTK_BOX(VBox),Frame,FALSE,FALSE,0);
+ vbox = gtk_vbox_new(FALSE,2);
+ gtk_container_add(GTK_CONTAINER(Frame),vbox);
+ gtk_container_set_border_width(GTK_CONTAINER(vbox), 2);
+
+ /****hbox = gtk_hbox_new(FALSE,2);
+ gtk_box_pack_start(GTK_BOX(vbox),hbox,FALSE,FALSE,0);
+ gtk_container_set_border_width(GTK_CONTAINER(hbox), 2);***/
+
+ Table = gtk_table_new(4,2,FALSE);
+ gtk_box_pack_start(GTK_BOX(vbox),Table,FALSE,FALSE,0);
+ //gtk_table_set_row_spacings(GTK_TABLE(Table),2);
+ gtk_table_set_col_spacings(GTK_TABLE(Table),2);
+
+ /* Rules for character set */
+ Label = gtk_label_new(_("Rules to apply if some characters can't be converted to "
+ "the system character encoding when writing filename:"));
+ gtk_table_attach(GTK_TABLE(Table),Label,0,2,0,1,GTK_FILL,GTK_FILL,0,0);
+ gtk_misc_set_alignment(GTK_MISC(Label),0,0.5);
+
+ Label = gtk_label_new(" ");
+ gtk_table_attach(GTK_TABLE(Table),Label,0,1,1,2,GTK_FILL,GTK_FILL,0,0);
+
+ FilenameCharacterSetOther = gtk_radio_button_new_with_label(NULL,_("Try an other "
+ "character encoding"));
+ gtk_table_attach(GTK_TABLE(Table),FilenameCharacterSetOther,1,2,1,2,GTK_FILL,GTK_FILL,0,0);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(FilenameCharacterSetOther),FILENAME_CHARACTER_SET_OTHER);
+ gtk_tooltips_set_tip(Tips,FilenameCharacterSetOther,_("With this option, it will "
+ "try the conversion to the encoding associated to your locale (for example : "
+ "ISO-8859-1 for 'fr', KOI8-R for 'ru', ISO-8859-2 for 'ro'). If it fails, it "
+ "will try the character encoding ISO-8859-1."),NULL);
+
+ FilenameCharacterSetApproximate = gtk_radio_button_new_with_label(
+ gtk_radio_button_get_group(GTK_RADIO_BUTTON(FilenameCharacterSetOther)),
+ _("Force using the system character encoding and activate the transliteration"));
+ gtk_table_attach(GTK_TABLE(Table),FilenameCharacterSetApproximate,1,2,2,3,GTK_FILL,GTK_FILL,0,0);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(FilenameCharacterSetApproximate),FILENAME_CHARACTER_SET_APPROXIMATE);
+ gtk_tooltips_set_tip(Tips,FilenameCharacterSetApproximate,_("With this option, when "
+ "a character cannot be represented in the target character set, it can be "
+ "approximated through one or several similarly looking characters."),NULL);
+
+ FilenameCharacterSetDiscard = gtk_radio_button_new_with_label(
+ gtk_radio_button_get_group(GTK_RADIO_BUTTON(FilenameCharacterSetOther)),
+ _("Force using the system character encoding and silently discard some characters"));
+ gtk_table_attach(GTK_TABLE(Table),FilenameCharacterSetDiscard,1,2,3,4,GTK_FILL,GTK_FILL,0,0);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(FilenameCharacterSetDiscard),FILENAME_CHARACTER_SET_DISCARD);
+ gtk_tooltips_set_tip(Tips,FilenameCharacterSetDiscard,_("With this option, when "
+ "a characters cannot be represented in the target character set, it will "
+ "be silently discarded."),NULL);
+
+
+
+ /*
+ * Tag Settings
+ */
+ Label = gtk_label_new (_("Tag Settings"));
+ Frame = gtk_frame_new (_("Tag Settings"));
+ gtk_notebook_append_page (GTK_NOTEBOOK(OptionsNoteBook),Frame,Label);
+ gtk_container_set_border_width(GTK_CONTAINER(Frame),5);
+
+ VBox = gtk_vbox_new(FALSE,2);
+ gtk_container_add(GTK_CONTAINER(Frame),VBox);
+ gtk_container_set_border_width(GTK_CONTAINER(VBox),4);
+
+ /* Tag Options */
+ Frame = gtk_frame_new (_("Tag Options"));
+ gtk_box_pack_start(GTK_BOX(VBox),Frame,FALSE,FALSE,0);
+ vbox = gtk_vbox_new(FALSE,2);
+ gtk_container_add(GTK_CONTAINER(Frame),vbox);
+ gtk_container_set_border_width(GTK_CONTAINER(vbox), 2);
+
+ DateAutoCompletion = gtk_check_button_new_with_label(_("Auto completion of date if not complete"));
+ gtk_box_pack_start(GTK_BOX(vbox),DateAutoCompletion,FALSE,FALSE,0);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(DateAutoCompletion),DATE_AUTO_COMPLETION);
+ gtk_tooltips_set_tip(Tips,DateAutoCompletion,_("Try to complete the year field if you enter "
+ "only the last numerals of the date (for instance, if the current year is 2005: "
+ "5 => 2005, 4 => 2004, 6 => 1996, 95 => 1995, ...)."),NULL);
+
+ hbox = gtk_hbox_new(FALSE,0);
+ gtk_box_pack_start(GTK_BOX(vbox),hbox,FALSE,FALSE,0);
+
+ NumberTrackFormated = gtk_check_button_new_with_label(_("Write the track field with the following number of digits :"));
+ gtk_box_pack_start(GTK_BOX(hbox),NumberTrackFormated,FALSE,FALSE,0);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(NumberTrackFormated),NUMBER_TRACK_FORMATED);
+ gtk_tooltips_set_tip(Tips,NumberTrackFormated,_("If activated, the track field is written using "
+ "the number '0' as padding to obtain a number with 'n' digits (Ex. with two digits : '05', "
+ "'09', '10',...). Else it keeps the 'raw' track value."),NULL);
+
+ NumberTrackFormatedSpinButton = gtk_spin_button_new((GtkAdjustment *)gtk_adjustment_new(2.0,2.0,6.0,1.0,1.0,1.0),1.0,0);
+ gtk_box_pack_start(GTK_BOX(hbox),NumberTrackFormatedSpinButton,FALSE,FALSE,0);
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(NumberTrackFormatedSpinButton),(gfloat)NUMBER_TRACK_FORMATED_SPIN_BUTTON);
+ g_signal_connect(G_OBJECT(NumberTrackFormated),"toggled",G_CALLBACK(Number_Track_Formated_Toggled),NULL);
+ g_signal_emit_by_name(G_OBJECT(NumberTrackFormated),"toggled");
+
+ Label = gtk_label_new(""); // Label to show the example
+ gtk_box_pack_start(GTK_BOX(hbox),Label,FALSE,FALSE,4);
+ g_signal_connect_swapped(G_OBJECT(NumberTrackFormatedSpinButton),"changed",G_CALLBACK(Number_Track_Formated_Spin_Button_Changed),G_OBJECT(Label));
+ g_signal_emit_by_name(G_OBJECT(NumberTrackFormatedSpinButton),"changed",NULL);
+
+ OggTagWriteXmmsComment = gtk_check_button_new_with_label(_("Ogg Vorbis Files : Write also the comment to the XMMS format"));
+ gtk_box_pack_start(GTK_BOX(vbox),OggTagWriteXmmsComment,FALSE,FALSE,0);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(OggTagWriteXmmsComment),OGG_TAG_WRITE_XMMS_COMMENT);
+ gtk_tooltips_set_tip(Tips,OggTagWriteXmmsComment,_("XMMS doesn't make use of the right way to "
+ "identify a comment in Ogg Vorbis files as other apps do. In fact, this field is usually labeled "
+ "with 'comment=', whereas XMMS uses only `='. Please, uncheck this option if you don't want "
+ "other apps to complain about an unknown field. Comments won't be shown in XMMS, though."),NULL);
+
+ // Separator line
+ Separator = gtk_hseparator_new();
+ gtk_box_pack_start(GTK_BOX(vbox),Separator,FALSE,FALSE,0);
+
+ /* Tag field focus */
+ Table = gtk_table_new(2,3,FALSE);
+ gtk_box_pack_start(GTK_BOX(vbox),Table,FALSE,FALSE,0);
+ //gtk_table_set_row_spacings(GTK_TABLE(Table),2);
+ gtk_table_set_col_spacings(GTK_TABLE(Table),2);
+
+ Label = gtk_label_new(_("Tag field focus when switching files in list with "
+ "shortcuts Page Up/Page Down:"));
+ gtk_table_attach(GTK_TABLE(Table),Label,0,2,0,1,GTK_FILL,GTK_FILL,0,0);
+ gtk_misc_set_alignment(GTK_MISC(Label),0,0.5);
+
+ Label = gtk_label_new(" ");
+ gtk_table_attach(GTK_TABLE(Table),Label,0,1,1,2,GTK_FILL,GTK_FILL,0,0);
+
+ SetFocusToSameTagField = gtk_radio_button_new_with_label(NULL,
+ _("Keep focus to the same tag field"));
+ gtk_table_attach(GTK_TABLE(Table),SetFocusToSameTagField,1,2,1,2,GTK_FILL,GTK_FILL,0,0);
+ //gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(SetFocusToSameTagField),SET_FOCUS_TO_SAME_TAG_FIELD);
+
+ SetFocusToFirstTagField = gtk_radio_button_new_with_label(
+ gtk_radio_button_get_group(GTK_RADIO_BUTTON(SetFocusToSameTagField)),
+ _("Return focus to the first tag field (ie 'Title' field)"));
+ gtk_table_attach(GTK_TABLE(Table),SetFocusToFirstTagField,1,2,2,3,GTK_FILL,GTK_FILL,0,0);
+ //gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(SetFocusToFirstTagField),SET_FOCUS_TO_FIRST_TAG_FIELD);
+
+
+ /*
+ * ID3 Tag Settings
+ */
+ Label = gtk_label_new (_("ID3 Tag Settings"));
+ Frame = gtk_frame_new (_("ID3 Tag Settings"));
+#ifdef ENABLE_MP3
+ gtk_notebook_append_page (GTK_NOTEBOOK(OptionsNoteBook),Frame,Label);
+#endif
+ gtk_container_set_border_width(GTK_CONTAINER(Frame),5);
+
+ VBox = gtk_vbox_new(FALSE,2);
+ gtk_container_add(GTK_CONTAINER(Frame),VBox);
+ gtk_container_set_border_width(GTK_CONTAINER(VBox),4);
+
+
+ /* Tag Rules frame */
+ Frame = gtk_frame_new (_("ID3 Tag Rules"));
+ gtk_box_pack_start(GTK_BOX(VBox),Frame,FALSE,FALSE,0);
+ vbox = gtk_vbox_new(FALSE,2);
+ gtk_container_add(GTK_CONTAINER(Frame),vbox);
+ gtk_container_set_border_width(GTK_CONTAINER(vbox),2);
+
+ Table = gtk_table_new(3,2,FALSE);
+ gtk_box_pack_start(GTK_BOX(vbox),Table,FALSE,FALSE,0);
+ gtk_table_set_row_spacings(GTK_TABLE(Table),2);
+ gtk_table_set_col_spacings(GTK_TABLE(Table),2);
+
+ /* Write ID3 tags in FLAC files */
+ WriteId3TagsInFlacFiles = gtk_check_button_new_with_label(_("Write ID3 tags in FLAC files (in addition to FLAC tag)"));
+ gtk_table_attach(GTK_TABLE(Table),WriteId3TagsInFlacFiles,0,1,0,1,GTK_FILL,GTK_FILL,0,0);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(WriteId3TagsInFlacFiles),WRITE_ID3_TAGS_IN_FLAC_FILE);
+ gtk_tooltips_set_tip(Tips,WriteId3TagsInFlacFiles,_("If activated, ID3 tags will be "
+ "also added in the FLAC file (according the two rules above, plus the FLAC tag). "
+ "Else ID3 tags will be stripped."),NULL);
+
+ /* Strip tag when fields (managed by EasyTAG) are empty */
+ StripTagWhenEmptyFields = gtk_check_button_new_with_label(_("Strip tags if all fields are set to blank"));
+ gtk_table_attach(GTK_TABLE(Table),StripTagWhenEmptyFields,0,1,1,2,GTK_FILL,GTK_FILL,0,0);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(StripTagWhenEmptyFields),STRIP_TAG_WHEN_EMPTY_FIELDS);
+ gtk_tooltips_set_tip(Tips,StripTagWhenEmptyFields,_("As ID3v2 tags may contain other data than "
+ "Title, Artist, Album, Year, Track, Genre or Comment (as an attached picture, lyrics, ...), "
+ "this option allows you to strip the whole tag when these seven standard data fields have "
+ "been set to blank."),NULL);
+
+ /* Convert old ID3v2 tag version */
+ ConvertOldId3v2TagVersion = gtk_check_button_new_with_label(_("Automatically convert old ID3v2 tag versions"));
+ gtk_table_attach(GTK_TABLE(Table),ConvertOldId3v2TagVersion,0,1,2,3,GTK_FILL,GTK_FILL,0,0);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ConvertOldId3v2TagVersion),CONVERT_OLD_ID3V2_TAG_VERSION);
+ gtk_tooltips_set_tip(Tips,ConvertOldId3v2TagVersion,_("If activated, an old ID3v2 tag version (as "
+ "ID3v2.2) will be updated to the ID3v2.3 version."),NULL);
+
+ /* Use CRC32 */
+ FileWritingId3v2UseCrc32 = gtk_check_button_new_with_label(_("Use CRC32"));
+ gtk_table_attach(GTK_TABLE(Table),FileWritingId3v2UseCrc32,1,2,0,1,GTK_FILL,GTK_FILL,0,0);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(FileWritingId3v2UseCrc32),FILE_WRITING_ID3V2_USE_CRC32);
+ gtk_tooltips_set_tip(Tips,FileWritingId3v2UseCrc32,_("Set CRC32 in the ID3v2 tags"),NULL);
+
+ /* Use Compression */
+ FileWritingId3v2UseCompression = gtk_check_button_new_with_label(_("Use Compression"));
+ gtk_table_attach(GTK_TABLE(Table),FileWritingId3v2UseCompression,1,2,1,2,GTK_FILL,GTK_FILL,0,0);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(FileWritingId3v2UseCompression),FILE_WRITING_ID3V2_USE_COMPRESSION);
+ gtk_tooltips_set_tip(Tips,FileWritingId3v2UseCompression,_("Set Compression in the ID3v2 tags"),NULL);
+
+ /* Character Set for writing ID3 tag */
+ Frame = gtk_frame_new (_("Character Set for writing ID3 tags"));
+ gtk_box_pack_start(GTK_BOX(VBox),Frame,FALSE,FALSE,0);
+ id3v1v2hbox = gtk_hbox_new(FALSE,3);
+ gtk_container_add(GTK_CONTAINER(Frame),id3v1v2hbox);
+ gtk_container_set_border_width(GTK_CONTAINER(id3v1v2hbox), 2);
+
+ // ID3v2 tags
+ Frame = gtk_frame_new (_("ID3v2 tags"));
+ gtk_box_pack_start(GTK_BOX(id3v1v2hbox),Frame,FALSE,FALSE,2);
+
+ vbox = gtk_vbox_new(FALSE,2);
+ gtk_container_add(GTK_CONTAINER(Frame),vbox);
+ gtk_container_set_border_width(GTK_CONTAINER(vbox),2);
+
+ Table = gtk_table_new(8,6,FALSE);
+ gtk_box_pack_start(GTK_BOX(vbox),Table,FALSE,FALSE,0);
+ gtk_table_set_row_spacings(GTK_TABLE(Table),2);
+ gtk_table_set_col_spacings(GTK_TABLE(Table),2);
+
+ /* Write ID3v2 tag */
+ FileWritingId3v2WriteTag = gtk_check_button_new_with_label(_("Write ID3v2 tag"));
+ gtk_table_attach(GTK_TABLE(Table),FileWritingId3v2WriteTag,0,5,0,1,GTK_FILL,GTK_FILL,0,0);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(FileWritingId3v2WriteTag),FILE_WRITING_ID3V2_WRITE_TAG);
+ gtk_tooltips_set_tip(Tips,FileWritingId3v2WriteTag,_("If activated, an ID3v2.4 tag will be added or "
+ "updated at the beginning of the MP3 files. Else it will be stripped."),NULL);
+ g_signal_connect_after(G_OBJECT(FileWritingId3v2WriteTag),"toggled",
+ G_CALLBACK(Change_Id3_Settings_Toggled),NULL);
+
+#ifdef ENABLE_ID3LIB
+ /* ID3v2 tag version */
+ LabelId3v2Version = gtk_label_new(_("Version:"));
+ gtk_table_attach(GTK_TABLE(Table),LabelId3v2Version,0,2,1,2,GTK_FILL,GTK_FILL,0,0);
+ gtk_misc_set_alignment(GTK_MISC(LabelId3v2Version),0,0.5);
+
+ EventBox = gtk_event_box_new();
+ FileWritingId3v2VersionCombo = gtk_combo_box_new_text();
+ gtk_container_add(GTK_CONTAINER(EventBox),FileWritingId3v2VersionCombo);
+ gtk_tooltips_set_tip(Tips,EventBox,_("Select the ID3v2 tag version to write:\n"
+ " - ID3v2.3 is written using id3lib,\n"
+ " - ID3v2.4 is written using libid3tag (recommended)."),NULL);
+ gtk_combo_box_append_text(GTK_COMBO_BOX(FileWritingId3v2VersionCombo), "ID3v2.4");
+ gtk_combo_box_append_text(GTK_COMBO_BOX(FileWritingId3v2VersionCombo), "ID3v2.3");
+ gtk_combo_box_set_active(GTK_COMBO_BOX(FileWritingId3v2VersionCombo),
+ FILE_WRITING_ID3V2_VERSION_4 ? 0 : 1);
+ gtk_table_attach(GTK_TABLE(Table),EventBox,2,4,1,2,GTK_FILL,GTK_FILL,0,0);
+ g_signal_connect_after(G_OBJECT(FileWritingId3v2VersionCombo),"changed",
+ G_CALLBACK(Change_Id3_Settings_Toggled),NULL);
+#endif
+
+
+ /* Charset */
+ LabelId3v2Charset = gtk_label_new(_("Charset:"));
+ gtk_table_attach(GTK_TABLE(Table),LabelId3v2Charset,0,5,2,3,GTK_FILL,GTK_FILL,0,0);
+ gtk_misc_set_alignment(GTK_MISC(LabelId3v2Charset),0,0.5);
+
+ Label = gtk_label_new(" ");
+ gtk_table_attach(GTK_TABLE(Table),Label,0,1,3,4,GTK_FILL,GTK_FILL,0,0);
+
+ // Unicode
+ FileWritingId3v2UseUnicodeCharacterSet = gtk_radio_button_new_with_label(NULL, _("Unicode "));
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(FileWritingId3v2UseUnicodeCharacterSet),
+ FILE_WRITING_ID3V2_USE_UNICODE_CHARACTER_SET);
+ gtk_table_attach(GTK_TABLE(Table),FileWritingId3v2UseUnicodeCharacterSet,1,2,3,4,GTK_FILL,GTK_FILL,0,0);
+
+ EventBox = gtk_event_box_new();
+ FileWritingId3v2UnicodeCharacterSetCombo = gtk_combo_box_new_text();
+ gtk_container_add(GTK_CONTAINER(EventBox),FileWritingId3v2UnicodeCharacterSetCombo);
+ gtk_tooltips_set_tip(Tips,EventBox,_("Unicode type to use"),NULL);
+ gtk_combo_box_append_text(GTK_COMBO_BOX(FileWritingId3v2UnicodeCharacterSetCombo), "UTF-8");
+ gtk_combo_box_append_text(GTK_COMBO_BOX(FileWritingId3v2UnicodeCharacterSetCombo), "UTF-16");
+ if ( FILE_WRITING_ID3V2_UNICODE_CHARACTER_SET == NULL )
+ gtk_combo_box_set_active(GTK_COMBO_BOX(FileWritingId3v2UnicodeCharacterSetCombo), 0);
+ else
+ gtk_combo_box_set_active(GTK_COMBO_BOX(FileWritingId3v2UnicodeCharacterSetCombo),
+ strcmp(FILE_WRITING_ID3V2_UNICODE_CHARACTER_SET, "UTF-8") ? 1 : 0);
+ gtk_table_attach(GTK_TABLE(Table),EventBox,2,4,3,4,GTK_FILL,GTK_FILL,0,0);
+ g_signal_connect_after(G_OBJECT(FileWritingId3v2UseUnicodeCharacterSet),"toggled",
+ G_CALLBACK(Change_Id3_Settings_Toggled),NULL);
+
+ // Non-unicode
+ FileWritingId3v2UseNoUnicodeCharacterSet = gtk_radio_button_new_with_label(
+ gtk_radio_button_get_group(GTK_RADIO_BUTTON(FileWritingId3v2UseUnicodeCharacterSet)),
+ _("Other"));
+ gtk_table_attach(GTK_TABLE(Table),FileWritingId3v2UseNoUnicodeCharacterSet,1,2,4,5,GTK_FILL,GTK_FILL,0,0);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(FileWritingId3v2UseNoUnicodeCharacterSet),
+ !FILE_WRITING_ID3V2_USE_UNICODE_CHARACTER_SET);
+
+ EventBox = gtk_event_box_new();
+ FileWritingId3v2NoUnicodeCharacterSetCombo = gtk_combo_box_new_text();
+ gtk_container_add(GTK_CONTAINER(EventBox),FileWritingId3v2NoUnicodeCharacterSetCombo);
+ gtk_tooltips_set_tip(Tips,EventBox,_("Character set used to write the tag "
+ "data in the file."),NULL);
+
+ Charset_Populate_Combobox(GTK_COMBO_BOX(FileWritingId3v2NoUnicodeCharacterSetCombo),
+ FILE_WRITING_ID3V2_NO_UNICODE_CHARACTER_SET);
+ gtk_table_attach(GTK_TABLE(Table),EventBox,2,5,4,5,GTK_FILL,GTK_FILL,0,0);
+ g_signal_connect_after(G_OBJECT(FileWritingId3v2UseNoUnicodeCharacterSet),"toggled",
+ G_CALLBACK(Change_Id3_Settings_Toggled),NULL);
+
+ // ID3v2 Additional iconv() options
+ LabelAdditionalId3v2IconvOptions = gtk_label_new(_("Additional settings for iconv():"));
+ gtk_table_attach(GTK_TABLE(Table),LabelAdditionalId3v2IconvOptions,2,5,5,6,GTK_FILL,GTK_FILL,0,0);
+ gtk_misc_set_alignment(GTK_MISC(LabelAdditionalId3v2IconvOptions),0,0.5);
+
+ FileWritingId3v2IconvOptionsNo = gtk_radio_button_new_with_label(NULL, _("No"));
+ gtk_table_attach(GTK_TABLE(Table),FileWritingId3v2IconvOptionsNo,2,3,6,7,GTK_FILL,GTK_FILL,0,0);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(FileWritingId3v2IconvOptionsNo),FILE_WRITING_ID3V2_ICONV_OPTIONS_NO);
+ FileWritingId3v2IconvOptionsTranslit = gtk_radio_button_new_with_label(
+ gtk_radio_button_get_group(GTK_RADIO_BUTTON(FileWritingId3v2IconvOptionsNo)),
+ _("//TRANSLIT"));
+ gtk_table_attach(GTK_TABLE(Table),FileWritingId3v2IconvOptionsTranslit,3,4,6,7,GTK_FILL,GTK_FILL,0,0);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(FileWritingId3v2IconvOptionsTranslit),FILE_WRITING_ID3V2_ICONV_OPTIONS_TRANSLIT);
+ gtk_tooltips_set_tip(Tips,FileWritingId3v2IconvOptionsTranslit,_("With this option, when "
+ "a character cannot be represented in the target character set, it can be "
+ "approximated through one or several similarly looking characters."),NULL);
+
+ FileWritingId3v2IconvOptionsIgnore = gtk_radio_button_new_with_label(
+ gtk_radio_button_get_group(GTK_RADIO_BUTTON(FileWritingId3v2IconvOptionsNo)),
+ _("//IGNORE"));
+ gtk_table_attach(GTK_TABLE(Table),FileWritingId3v2IconvOptionsIgnore,4,5,6,7,GTK_FILL,GTK_FILL,0,0);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(FileWritingId3v2IconvOptionsIgnore),FILE_WRITING_ID3V2_ICONV_OPTIONS_IGNORE);
+ gtk_tooltips_set_tip(Tips,FileWritingId3v2IconvOptionsIgnore,_("With this option, when "
+ "a characters cannot be represented in the target character set, it will "
+ "be silently discarded."),NULL);
+
+ // ID3v1 tags
+ Frame = gtk_frame_new (_("ID3v1 tags"));
+ gtk_box_pack_start(GTK_BOX(id3v1v2hbox),Frame,FALSE,FALSE,2);
+
+ vbox = gtk_vbox_new(FALSE,2);
+ gtk_container_add(GTK_CONTAINER(Frame),vbox);
+ gtk_container_set_border_width(GTK_CONTAINER(vbox),2);
+
+ Table = gtk_table_new(6,5,FALSE);
+ gtk_box_pack_start(GTK_BOX(vbox),Table,FALSE,FALSE,0);
+ gtk_table_set_row_spacings(GTK_TABLE(Table),2);
+ gtk_table_set_col_spacings(GTK_TABLE(Table),2);
+
+
+ /* Write ID3v1 tag */
+ FileWritingId3v1WriteTag = gtk_check_button_new_with_label(_("Write ID3v1.x tag"));
+ gtk_table_attach(GTK_TABLE(Table),FileWritingId3v1WriteTag,0,4,0,1,GTK_FILL,GTK_FILL,0,0);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(FileWritingId3v1WriteTag),FILE_WRITING_ID3V1_WRITE_TAG);
+ gtk_tooltips_set_tip(Tips,FileWritingId3v1WriteTag,_("If activated, an ID3v1 tag will be added or "
+ "updated at the end of the MP3 files. Else it will be stripped."),NULL);
+ g_signal_connect_after(G_OBJECT(FileWritingId3v1WriteTag),"toggled",
+ G_CALLBACK(Change_Id3_Settings_Toggled),NULL);
+
+ /* Id3V1 writing character set */
+ LabelId3v1Charset = gtk_label_new(_("Charset:"));
+ gtk_table_attach(GTK_TABLE(Table),LabelId3v1Charset,0,4,1,2,GTK_FILL,GTK_FILL,0,0);
+ gtk_misc_set_alignment(GTK_MISC(LabelId3v1Charset),0,0.5);
+
+ Label = gtk_label_new(" ");
+ gtk_table_attach(GTK_TABLE(Table),Label,0,1,2,3,GTK_FILL,GTK_FILL,0,0);
+
+ EventBox = gtk_event_box_new();
+ FileWritingId3v1CharacterSetCombo = gtk_combo_box_new_text();
+ gtk_container_add(GTK_CONTAINER(EventBox),FileWritingId3v1CharacterSetCombo);
+ gtk_table_attach(GTK_TABLE(Table),EventBox,1,4,2,3,GTK_FILL,GTK_FILL,0,0);
+ gtk_tooltips_set_tip(Tips,EventBox,_("Character set used to write ID3v1 tag data "
+ "in the file."),NULL);
+ Charset_Populate_Combobox(GTK_COMBO_BOX(FileWritingId3v1CharacterSetCombo), FILE_WRITING_ID3V1_CHARACTER_SET);
+
+ /* ID3V1 Additional iconv() options*/
+ LabelAdditionalId3v1IconvOptions = gtk_label_new(_("Additional settings for iconv():"));
+ gtk_table_attach(GTK_TABLE(Table),LabelAdditionalId3v1IconvOptions,1,4,3,4,GTK_FILL,GTK_FILL,0,0);
+ gtk_misc_set_alignment(GTK_MISC(LabelAdditionalId3v1IconvOptions),0,0.5);
+
+ FileWritingId3v1IconvOptionsNo = gtk_radio_button_new_with_label(NULL,
+ _("No"));
+ gtk_table_attach(GTK_TABLE(Table),FileWritingId3v1IconvOptionsNo,1,2,4,5,GTK_FILL,GTK_FILL,0,0);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(FileWritingId3v1IconvOptionsNo),FILE_WRITING_ID3V1_ICONV_OPTIONS_NO);
+ FileWritingId3v1IconvOptionsTranslit = gtk_radio_button_new_with_label(
+ gtk_radio_button_get_group(GTK_RADIO_BUTTON(FileWritingId3v1IconvOptionsNo)),
+ _("//TRANSLIT"));
+ gtk_table_attach(GTK_TABLE(Table),FileWritingId3v1IconvOptionsTranslit,2,3,4,5,GTK_FILL,GTK_FILL,0,0);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(FileWritingId3v1IconvOptionsTranslit),FILE_WRITING_ID3V1_ICONV_OPTIONS_TRANSLIT);
+ gtk_tooltips_set_tip(Tips,FileWritingId3v1IconvOptionsTranslit,_("With this option, when "
+ "a character cannot be represented in the target character set, it can be "
+ "approximated through one or several similarly looking characters."),NULL);
+
+ FileWritingId3v1IconvOptionsIgnore = gtk_radio_button_new_with_label(
+ gtk_radio_button_get_group(GTK_RADIO_BUTTON(FileWritingId3v1IconvOptionsNo)),
+ _("//IGNORE"));
+ gtk_table_attach(GTK_TABLE(Table),FileWritingId3v1IconvOptionsIgnore,3,4,4,5,GTK_FILL,GTK_FILL,0,0);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(FileWritingId3v1IconvOptionsIgnore),FILE_WRITING_ID3V1_ICONV_OPTIONS_IGNORE);
+ gtk_tooltips_set_tip(Tips,FileWritingId3v1IconvOptionsIgnore,_("With this option, when "
+ "a characters cannot be represented in the target character set, it will "
+ "be silently discarded."),NULL);
+
+ /* Character Set for reading tag */
+ Frame = gtk_frame_new (_("Character Set for reading ID3 tags"));
+ gtk_box_pack_start(GTK_BOX(VBox),Frame,FALSE,FALSE,0);
+ vbox = gtk_vbox_new(FALSE,2);
+ gtk_container_add(GTK_CONTAINER(Frame),vbox);
+ gtk_container_set_border_width(GTK_CONTAINER(vbox), 2);
+
+ Table = gtk_table_new(4,2,FALSE);
+ gtk_box_pack_start(GTK_BOX(vbox),Table,FALSE,FALSE,0);
+ //gtk_table_set_row_spacings(GTK_TABLE(Table),2);
+ gtk_table_set_col_spacings(GTK_TABLE(Table),2);
+
+ // "File Reading Charset" Check Button + Combo
+ UseNonStandardId3ReadingCharacterSet = gtk_check_button_new_with_label(_(
+ "Non-standart:"));
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(UseNonStandardId3ReadingCharacterSet),
+ USE_NON_STANDARD_ID3_READING_CHARACTER_SET);
+ gtk_table_attach(GTK_TABLE(Table),UseNonStandardId3ReadingCharacterSet,0,1,0,1,GTK_FILL,GTK_FILL,0,0);
+ gtk_tooltips_set_tip(Tips,UseNonStandardId3ReadingCharacterSet,
+ _("This character set will be used when reading the tag data, to convert "
+ "each string found in an ISO-8859-1 field in the tag (for ID3v2 or/and ID3v1 tag).\n"
+ "\n"
+ "For example :\n"
+ " - in previous versions of EasyTAG, you can save UTF-8 strings in an ISO-8859-1 "
+ "field. This is not correct! To convert these tags to Unicode: activate this option "
+ "and select UTF-8. You must also activate above the option 'Try to save tags to "
+ "ISO-8859-1. If it isn't possible then use UNICODE (recommended)' or 'Always save "
+ "tags to UNICODE character set'.\n"
+ " - If unicode was not used, Russian people can select the character set "
+ "'Windows-1251' to load tags written under Windows. And 'KOI8-R' to load tags "
+ "written under Unix systems."),NULL);
+
+ EventBox = gtk_event_box_new();
+ FileReadingId3v1v2CharacterSetCombo = gtk_combo_box_new_text();
+ gtk_container_add(GTK_CONTAINER(EventBox),FileReadingId3v1v2CharacterSetCombo);
+ gtk_table_attach(GTK_TABLE(Table),EventBox,2,3,0,1,GTK_FILL,GTK_FILL,0,0);
+
+ gtk_tooltips_set_tip(Tips,EventBox,_("Character set used to read tag data "
+ "in the file."),NULL);
+
+ Charset_Populate_Combobox(GTK_COMBO_BOX(FileReadingId3v1v2CharacterSetCombo),
+ FILE_READING_ID3V1V2_CHARACTER_SET);
+ g_signal_connect_after(G_OBJECT(UseNonStandardId3ReadingCharacterSet),"toggled",
+ G_CALLBACK(Use_Non_Standard_Id3_Reading_Character_Set_Toggled),NULL);
+
+ Use_Non_Standard_Id3_Reading_Character_Set_Toggled();
+ Change_Id3_Settings_Toggled();
+
+
+ /*
+ * Scanner
+ */
+ Label = gtk_label_new (_("Scanner"));
+ Frame = gtk_frame_new (_("Scanner"));
+ gtk_notebook_append_page (GTK_NOTEBOOK(OptionsNoteBook),Frame,Label);
+ gtk_container_set_border_width(GTK_CONTAINER(Frame), 5);
+
+ /* Save the number of the page. Asked in Scanner window */
+ OptionsNoteBook_Scanner_Page_Num = gtk_notebook_page_num(GTK_NOTEBOOK(OptionsNoteBook),Frame);
+
+ VBox = gtk_vbox_new(FALSE,2);
+ gtk_container_add(GTK_CONTAINER(Frame),VBox);
+ gtk_container_set_border_width(GTK_CONTAINER(VBox), 4);
+
+ /* Character conversion for the 'Fill Tag' scanner (=> FTS...) */
+ Frame = gtk_frame_new (_("Fill Tag Scanner - Character Conversion"));
+ gtk_box_pack_start(GTK_BOX(VBox),Frame,FALSE,FALSE,0);
+ vbox = gtk_vbox_new(FALSE,2);
+ gtk_container_add(GTK_CONTAINER(Frame),vbox);
+ gtk_container_set_border_width(GTK_CONTAINER(vbox), 2);
+
+ FTSConvertUnderscoreAndP20IntoSpace = gtk_check_button_new_with_label(_("Convert underscore "
+ "character '_' and string '%20' to space ' '"));
+ FTSConvertSpaceIntoUnderscore = gtk_check_button_new_with_label(_("Convert space ' ' to underscore '_'"));
+ gtk_box_pack_start(GTK_BOX(vbox),FTSConvertUnderscoreAndP20IntoSpace,FALSE,FALSE,0);
+ gtk_box_pack_start(GTK_BOX(vbox),FTSConvertSpaceIntoUnderscore, FALSE,FALSE,0);
+ g_signal_connect_swapped(G_OBJECT(FTSConvertUnderscoreAndP20IntoSpace),"toggled",
+ G_CALLBACK(Scanner_Convert_Check_Button_Toggled_1),G_OBJECT(FTSConvertSpaceIntoUnderscore));
+ g_signal_connect_swapped(G_OBJECT(FTSConvertSpaceIntoUnderscore),"toggled",
+ G_CALLBACK(Scanner_Convert_Check_Button_Toggled_1),G_OBJECT(FTSConvertUnderscoreAndP20IntoSpace));
+
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(FTSConvertUnderscoreAndP20IntoSpace),
+ FTS_CONVERT_UNDERSCORE_AND_P20_INTO_SPACE);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(FTSConvertSpaceIntoUnderscore),
+ FTS_CONVERT_SPACE_INTO_UNDERSCORE);
+ gtk_tooltips_set_tip(Tips,FTSConvertUnderscoreAndP20IntoSpace,_("If activated, this conversion "
+ "will be used when applying a mask from the scanner for tags."),NULL);
+ gtk_tooltips_set_tip(Tips,FTSConvertSpaceIntoUnderscore,_("If activated, this conversion "
+ "will be used when applying a mask from the scanner for tags."),NULL);
+
+ /* Character conversion for the 'Rename File' scanner (=> RFS...) */
+ Frame = gtk_frame_new (_("Rename File Scanner - Character Conversion"));
+ gtk_box_pack_start(GTK_BOX(VBox),Frame,FALSE,FALSE,0);
+ vbox = gtk_vbox_new(FALSE,2);
+ gtk_container_add(GTK_CONTAINER(Frame),vbox);
+ gtk_container_set_border_width(GTK_CONTAINER(vbox),2);
+
+ RFSConvertUnderscoreAndP20IntoSpace = gtk_check_button_new_with_label(_("Convert underscore "
+ "character '_' and string '%20' to space ' '"));
+ RFSConvertSpaceIntoUnderscore = gtk_check_button_new_with_label(_("Convert space ' ' to underscore '_'"));
+ gtk_box_pack_start(GTK_BOX(vbox),RFSConvertUnderscoreAndP20IntoSpace,FALSE,FALSE,0);
+ gtk_box_pack_start(GTK_BOX(vbox),RFSConvertSpaceIntoUnderscore, FALSE,FALSE,0);
+ g_signal_connect_swapped(G_OBJECT(RFSConvertUnderscoreAndP20IntoSpace),"toggled",
+ G_CALLBACK(Scanner_Convert_Check_Button_Toggled_1),G_OBJECT(RFSConvertSpaceIntoUnderscore));
+ g_signal_connect_swapped(G_OBJECT(RFSConvertSpaceIntoUnderscore),"toggled",
+ G_CALLBACK(Scanner_Convert_Check_Button_Toggled_1),G_OBJECT(RFSConvertUnderscoreAndP20IntoSpace));
+
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(RFSConvertUnderscoreAndP20IntoSpace),
+ RFS_CONVERT_UNDERSCORE_AND_P20_INTO_SPACE);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(RFSConvertSpaceIntoUnderscore),
+ RFS_CONVERT_SPACE_INTO_UNDERSCORE);
+ gtk_tooltips_set_tip(Tips,RFSConvertUnderscoreAndP20IntoSpace,_("If activated, this conversion "
+ "will be used when applying a mask from the scanner for filenames."),NULL);
+ gtk_tooltips_set_tip(Tips,RFSConvertSpaceIntoUnderscore,_("If activated, this conversion "
+ "will be used when applying a mask from the scanner for filenames."),NULL);
+
+ /* Character conversion for the 'Process Fields' scanner (=> PFS...) */
+ Frame = gtk_frame_new (_("Process Fields Scanner - Character Conversion"));
+ gtk_box_pack_start(GTK_BOX(VBox),Frame,FALSE,FALSE,0);
+ vbox = gtk_vbox_new(FALSE,2);
+ gtk_container_add(GTK_CONTAINER(Frame),vbox);
+ gtk_container_set_border_width(GTK_CONTAINER(vbox),2);
+
+ // Don't convert some words like to, feat. first letter uppercase.
+ PFSDontUpperSomeWords = gtk_check_button_new_with_label(_("Don't uppercase "
+ "first letter of words for some prepositions and articles."));
+ gtk_box_pack_start(GTK_BOX(vbox),PFSDontUpperSomeWords, FALSE, FALSE, 0);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(PFSDontUpperSomeWords), PFS_DONT_UPPER_SOME_WORDS);
+ gtk_tooltips_set_tip(Tips, PFSDontUpperSomeWords, _("Don't convert first "
+ "letter of the words like prepositions, articles and words like feat., "
+ "when using the scanner 'First letter uppercase of each word' (for "
+ "example, you will obtain 'Text in an Entry' instead of 'Text In An Entry')."), NULL);
+
+ /* Properties of the scanner window */
+ Frame = gtk_frame_new (_("Scanner Window"));
+ gtk_box_pack_start(GTK_BOX(VBox),Frame,FALSE,FALSE,0);
+ vbox = gtk_vbox_new(FALSE,2);
+ gtk_container_add(GTK_CONTAINER(Frame),vbox);
+ gtk_container_set_border_width(GTK_CONTAINER(vbox), 2);
+
+ OpenScannerWindowOnStartup = gtk_check_button_new_with_label(_("Open the Scanner Window on startup"));
+ gtk_box_pack_start(GTK_BOX(vbox),OpenScannerWindowOnStartup,FALSE,FALSE,0);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(OpenScannerWindowOnStartup),OPEN_SCANNER_WINDOW_ON_STARTUP);
+ gtk_tooltips_set_tip(Tips,OpenScannerWindowOnStartup,_("Activate this option to open automatically "
+ "the scanner window when EasyTAG starts."),NULL);
+
+ ScannerWindowOnTop = gtk_check_button_new_with_label(_("Scanner window always on top"));
+ gtk_box_pack_start(GTK_BOX(vbox),ScannerWindowOnTop,FALSE,FALSE,0);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ScannerWindowOnTop),SCANNER_WINDOW_ON_TOP);
+ gtk_tooltips_set_tip(Tips,ScannerWindowOnTop,_("If activated, the window which contains the masks "
+ "will stay always over the main window."),NULL);
+
+
+ /* Other options */
+ Frame = gtk_frame_new (_("Fields"));
+ gtk_box_pack_start(GTK_BOX(VBox),Frame,FALSE,FALSE,0);
+ vbox = gtk_vbox_new(FALSE,2);
+ gtk_container_add(GTK_CONTAINER(Frame),vbox);
+ gtk_container_set_border_width(GTK_CONTAINER(vbox), 2);
+
+ // Overwrite text into tag fields
+ OverwriteTagField = gtk_check_button_new_with_label(_("Overwrite fields when scanning tag"));
+ gtk_box_pack_start(GTK_BOX(vbox),OverwriteTagField,FALSE,FALSE,0);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(OverwriteTagField),OVERWRITE_TAG_FIELD);
+ gtk_tooltips_set_tip(Tips,OverwriteTagField,_("If activated, the scanner will replace existing text "
+ "in fields by the new one. If deactivated, only blank fields of the tag will be filled."),NULL);
+
+ // Set a default comment text or CRC-32 checksum
+ if (!DefaultCommentModel)
+ DefaultCommentModel = gtk_list_store_new(MISC_COMBO_COUNT, G_TYPE_STRING);
+ else
+ gtk_list_store_clear(DefaultCommentModel);
+
+ hbox = gtk_hbox_new(FALSE,2);
+ gtk_box_pack_start(GTK_BOX(vbox),hbox,FALSE,FALSE,0);
+ SetDefaultComment = gtk_check_button_new_with_label(_("Set this text as default comment :"));
+ gtk_box_pack_start(GTK_BOX(hbox),SetDefaultComment,FALSE,FALSE,0);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(SetDefaultComment),SET_DEFAULT_COMMENT);
+ gtk_tooltips_set_tip(Tips,SetDefaultComment,_("Activate this option if you want to put the "
+ "following string into the comment field when using the 'Fill Tag' scanner."),NULL);
+ DefaultComment = gtk_combo_box_entry_new_with_model(GTK_TREE_MODEL(DefaultCommentModel), MISC_COMBO_TEXT);
+ gtk_box_pack_start(GTK_BOX(hbox),DefaultComment,FALSE,FALSE,0);
+ gtk_widget_set_size_request(GTK_WIDGET(DefaultComment), 250, -1);
+ g_signal_connect(G_OBJECT(SetDefaultComment),"toggled",
+ G_CALLBACK(Set_Default_Comment_Check_Button_Toggled),NULL);
+ if (DEFAULT_COMMENT==NULL)
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(SetDefaultComment),FALSE);
+ Set_Default_Comment_Check_Button_Toggled();
+ /* History list */
+ Load_Default_Tag_Comment_Text_List(DefaultCommentModel, MISC_COMBO_TEXT);
+ Add_String_To_Combo_List(DefaultCommentModel, DEFAULT_COMMENT);
+ if (DEFAULT_COMMENT)
+ gtk_entry_set_text(GTK_ENTRY(GTK_BIN(DefaultComment)->child), DEFAULT_COMMENT);
+
+ // CRC32 comment
+ Crc32Comment = gtk_check_button_new_with_label(_("Use CRC32 as the default "
+ "comment (for files with ID3 tags only)."));
+ gtk_box_pack_start(GTK_BOX(vbox),Crc32Comment,FALSE,FALSE,0);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(Crc32Comment),SET_CRC32_COMMENT);
+ gtk_tooltips_set_tip(Tips,Crc32Comment,_("Calculates the CRC-32 value of the file "
+ "and writes it into the comment field when using the 'Fill Tag' scanner."),NULL);
+ g_signal_connect_swapped(G_OBJECT(SetDefaultComment), "toggled",
+ G_CALLBACK(Scanner_Convert_Check_Button_Toggled_1),G_OBJECT(Crc32Comment));
+ g_signal_connect_swapped(G_OBJECT(Crc32Comment), "toggled",
+ G_CALLBACK(Scanner_Convert_Check_Button_Toggled_1),G_OBJECT(SetDefaultComment));
+
+
+ /*
+ * CDDB
+ */
+ Label = gtk_label_new (_("CD Data Base"));
+ Frame = gtk_frame_new (_("CD Data Base"));
+ gtk_notebook_append_page (GTK_NOTEBOOK(OptionsNoteBook),Frame,Label);
+ gtk_container_set_border_width(GTK_CONTAINER(Frame), 5);
+
+ VBox = gtk_vbox_new(FALSE,2);
+ gtk_container_add(GTK_CONTAINER(Frame),VBox);
+ gtk_container_set_border_width(GTK_CONTAINER(VBox), 4);
+
+ // CDDB Server Settings (Automatic Search)
+ Frame = gtk_frame_new (_("Server Settings for Automatic Search"));
+ gtk_box_pack_start(GTK_BOX(VBox),Frame,FALSE,FALSE, 0);
+ vbox = gtk_vbox_new(FALSE,2);
+ gtk_container_add(GTK_CONTAINER(Frame),vbox);
+ gtk_container_set_border_width(GTK_CONTAINER(vbox), 4);
+
+ // 1rst automatic search server
+ hbox = gtk_hbox_new(FALSE,2);
+ gtk_container_add(GTK_CONTAINER(vbox),hbox);
+ Label = gtk_label_new(_("Name :"));
+ gtk_box_pack_start(GTK_BOX(hbox),Label,FALSE,FALSE,2);
+ CddbServerNameAutomaticSearch = gtk_combo_box_entry_new_text();
+ gtk_box_pack_start(GTK_BOX(hbox),CddbServerNameAutomaticSearch,FALSE,FALSE,0);
+ gtk_combo_box_append_text(GTK_COMBO_BOX(CddbServerNameAutomaticSearch), "freedb.freedb.org");
+ gtk_combo_box_append_text(GTK_COMBO_BOX(CddbServerNameAutomaticSearch), "www.gnudb.org");
+ gtk_combo_box_append_text(GTK_COMBO_BOX(CddbServerNameAutomaticSearch), "at.freedb.org");
+ gtk_combo_box_append_text(GTK_COMBO_BOX(CddbServerNameAutomaticSearch), "au.freedb.org");
+ gtk_combo_box_append_text(GTK_COMBO_BOX(CddbServerNameAutomaticSearch), "ca.freedb.org");
+ //gtk_combo_box_append_text(GTK_COMBO_BOX(CddbServerNameAutomaticSearch), "ca2.freedb.org");
+ //gtk_combo_box_append_text(GTK_COMBO_BOX(CddbServerNameAutomaticSearch), "de.freedb.org");
+ gtk_combo_box_append_text(GTK_COMBO_BOX(CddbServerNameAutomaticSearch), "es.freedb.org");
+ gtk_combo_box_append_text(GTK_COMBO_BOX(CddbServerNameAutomaticSearch), "fi.freedb.org");
+ gtk_combo_box_append_text(GTK_COMBO_BOX(CddbServerNameAutomaticSearch), "ru.freedb.org");
+ gtk_combo_box_append_text(GTK_COMBO_BOX(CddbServerNameAutomaticSearch), "uk.freedb.org");
+ gtk_combo_box_append_text(GTK_COMBO_BOX(CddbServerNameAutomaticSearch), "us.freedb.org");
+ if (CDDB_SERVER_NAME_AUTOMATIC_SEARCH)
+ gtk_entry_set_text(GTK_ENTRY(GTK_BIN(CddbServerNameAutomaticSearch)->child),CDDB_SERVER_NAME_AUTOMATIC_SEARCH);
+
+ Label = gtk_label_new (_("Port :"));
+ gtk_box_pack_start(GTK_BOX(hbox),Label,FALSE,FALSE,2);
+ CddbServerPortAutomaticSearch = gtk_entry_new();
+ gtk_widget_set_size_request(GTK_WIDGET(CddbServerPortAutomaticSearch), 45, -1);
+ gtk_entry_set_max_length(GTK_ENTRY(CddbServerPortAutomaticSearch),5);
+ gtk_box_pack_start(GTK_BOX(hbox),CddbServerPortAutomaticSearch,FALSE,FALSE,0);
+ sprintf(temp,"%i",CDDB_SERVER_PORT_AUTOMATIC_SEARCH);
+ gtk_entry_set_text(GTK_ENTRY(CddbServerPortAutomaticSearch),temp);
+ g_signal_connect(G_OBJECT(CddbServerPortAutomaticSearch),"insert_text",G_CALLBACK(Insert_Only_Digit),NULL);
+
+ Label = gtk_label_new (_("CGI Path :"));
+ gtk_box_pack_start(GTK_BOX(hbox),Label,FALSE,FALSE,2);
+ CddbServerCgiPathAutomaticSearch = gtk_entry_new();
+ gtk_box_pack_start(GTK_BOX(hbox),CddbServerCgiPathAutomaticSearch,FALSE,FALSE,0);
+ if (CDDB_SERVER_CGI_PATH_AUTOMATIC_SEARCH)
+ gtk_entry_set_text(GTK_ENTRY(CddbServerCgiPathAutomaticSearch) ,CDDB_SERVER_CGI_PATH_AUTOMATIC_SEARCH);
+
+ // 2sd automatic search server
+ hbox = gtk_hbox_new(FALSE,2);
+ gtk_container_add(GTK_CONTAINER(vbox),hbox);
+ Label = gtk_label_new(_("Name :"));
+ gtk_box_pack_start(GTK_BOX(hbox),Label,FALSE,FALSE,2);
+ CddbServerNameAutomaticSearch2 = gtk_combo_box_entry_new_text();
+ gtk_box_pack_start(GTK_BOX(hbox),CddbServerNameAutomaticSearch2,FALSE,FALSE,0);
+ gtk_combo_box_append_text(GTK_COMBO_BOX(CddbServerNameAutomaticSearch2), "freedb.musicbrainz.org");
+ if (CDDB_SERVER_NAME_AUTOMATIC_SEARCH2)
+ gtk_entry_set_text(GTK_ENTRY(GTK_BIN(CddbServerNameAutomaticSearch2)->child),CDDB_SERVER_NAME_AUTOMATIC_SEARCH2);
+
+ Label = gtk_label_new (_("Port :"));
+ gtk_box_pack_start(GTK_BOX(hbox),Label,FALSE,FALSE,2);
+ CddbServerPortAutomaticSearch2 = gtk_entry_new();
+ gtk_widget_set_size_request(GTK_WIDGET(CddbServerPortAutomaticSearch2), 45, -1);
+ gtk_entry_set_max_length(GTK_ENTRY(CddbServerPortAutomaticSearch2),5);
+ gtk_box_pack_start(GTK_BOX(hbox),CddbServerPortAutomaticSearch2,FALSE,FALSE,0);
+ sprintf(temp,"%i",CDDB_SERVER_PORT_AUTOMATIC_SEARCH2);
+ gtk_entry_set_text(GTK_ENTRY(CddbServerPortAutomaticSearch2),temp);
+ g_signal_connect(G_OBJECT(CddbServerPortAutomaticSearch2),"insert_text",G_CALLBACK(Insert_Only_Digit),NULL);
+
+ Label = gtk_label_new (_("CGI Path :"));
+ gtk_box_pack_start(GTK_BOX(hbox),Label,FALSE,FALSE,2);
+ CddbServerCgiPathAutomaticSearch2 = gtk_entry_new();
+ gtk_box_pack_start(GTK_BOX(hbox),CddbServerCgiPathAutomaticSearch2,FALSE,FALSE,0);
+ if (CDDB_SERVER_CGI_PATH_AUTOMATIC_SEARCH2)
+ gtk_entry_set_text(GTK_ENTRY(CddbServerCgiPathAutomaticSearch2) ,CDDB_SERVER_CGI_PATH_AUTOMATIC_SEARCH2);
+
+ // CDDB Server Settings (Manual Search)
+ Frame = gtk_frame_new (_("Server Settings for Manual Search"));
+ gtk_box_pack_start(GTK_BOX(VBox),Frame,FALSE,FALSE,0);
+ vbox = gtk_vbox_new(FALSE,2);
+ gtk_container_add(GTK_CONTAINER(Frame),vbox);
+ gtk_container_set_border_width(GTK_CONTAINER(vbox), 4);
+
+ hbox = gtk_hbox_new(FALSE,2);
+ gtk_container_add(GTK_CONTAINER(vbox),hbox);
+ Label = gtk_label_new(_("Name :"));
+ gtk_box_pack_start(GTK_BOX(hbox),Label,FALSE,FALSE,2);
+ CddbServerNameManualSearch = gtk_combo_box_entry_new_text();
+ gtk_box_pack_start(GTK_BOX(hbox),CddbServerNameManualSearch,FALSE,FALSE,0);
+ gtk_combo_box_append_text(GTK_COMBO_BOX(CddbServerNameManualSearch), "www.freedb.org");
+ gtk_combo_box_append_text(GTK_COMBO_BOX(CddbServerNameManualSearch), "www.gnudb.org");
+ if (CDDB_SERVER_NAME_MANUAL_SEARCH)
+ gtk_entry_set_text(GTK_ENTRY(GTK_BIN(CddbServerNameManualSearch)->child),CDDB_SERVER_NAME_MANUAL_SEARCH);
+
+ Label = gtk_label_new (_("Port :"));
+ gtk_box_pack_start(GTK_BOX(hbox),Label,FALSE,FALSE,2);
+ CddbServerPortManualSearch = gtk_entry_new();
+ gtk_widget_set_size_request(GTK_WIDGET(CddbServerPortManualSearch), 45, -1);
+ gtk_entry_set_max_length(GTK_ENTRY(CddbServerPortManualSearch),5);
+ gtk_box_pack_start(GTK_BOX(hbox),CddbServerPortManualSearch,FALSE,FALSE,0);
+ sprintf(temp,"%i",CDDB_SERVER_PORT_MANUAL_SEARCH);
+ gtk_entry_set_text(GTK_ENTRY(CddbServerPortManualSearch),temp);
+ g_signal_connect(G_OBJECT(CddbServerPortManualSearch),"insert_text",G_CALLBACK(Insert_Only_Digit),NULL);
+
+ Label = gtk_label_new (_("CGI Path :"));
+ gtk_box_pack_start(GTK_BOX(hbox),Label,FALSE,FALSE,2);
+ CddbServerCgiPathManualSearch = gtk_entry_new();
+ gtk_box_pack_start(GTK_BOX(hbox),CddbServerCgiPathManualSearch,FALSE,FALSE,0);
+ if (CDDB_SERVER_CGI_PATH_MANUAL_SEARCH)
+ gtk_entry_set_text(GTK_ENTRY(CddbServerCgiPathManualSearch) ,CDDB_SERVER_CGI_PATH_MANUAL_SEARCH);
+
+ // Local access for CDDB (Automatic Search)
+ Frame = gtk_frame_new (_("Local CD Data Base"));
+ gtk_box_pack_start(GTK_BOX(VBox),Frame,FALSE,FALSE,0);
+ vbox = gtk_vbox_new(FALSE,2);
+ gtk_container_add(GTK_CONTAINER(Frame),vbox);
+ gtk_container_set_border_width(GTK_CONTAINER(vbox), 4);
+
+ hbox = gtk_hbox_new(FALSE,2);
+ gtk_container_add(GTK_CONTAINER(vbox),hbox);
+ Label = gtk_label_new(_("Path :"));
+ gtk_box_pack_start(GTK_BOX(hbox),Label,FALSE,FALSE,2);
+
+ if (CddbLocalPathModel != NULL)
+ gtk_list_store_clear(CddbLocalPathModel);
+ else
+ CddbLocalPathModel = gtk_list_store_new(MISC_COMBO_COUNT, G_TYPE_STRING);
+
+ CddbLocalPath = gtk_combo_box_entry_new_with_model(GTK_TREE_MODEL(CddbLocalPathModel), MISC_COMBO_TEXT);
+ gtk_box_pack_start(GTK_BOX(hbox),CddbLocalPath,FALSE,FALSE,0);
+ gtk_widget_set_size_request(GTK_WIDGET(CddbLocalPath), 450, -1);
+ gtk_tooltips_set_tip(Tips,GTK_BIN(CddbLocalPath)->child,_("Specify the directory "
+ "where are located the local cd data base. The local cd data base contains the eleven following "
+ "directories 'blues', 'classical', 'country', 'data', 'folk', 'jazz', 'newage', 'reggae', "
+ "'rock', 'soundtrack' and 'misc'."),NULL);
+ g_signal_connect(G_OBJECT(GTK_ENTRY(GTK_BIN(CddbLocalPath)->child)),"activate",G_CALLBACK(CddbLocalPath_Combo_Add_String),NULL);
+ //g_signal_connect(G_OBJECT(GTK_ENTRY(GTK_BIN(CddbLocalPath)->child)),"focus_out_event",G_CALLBACK(CddbLocalPath_Combo_Add_String),NULL);
+
+ // History list
+ Load_Cddb_Local_Path_List(CddbLocalPathModel, MISC_COMBO_TEXT);
+
+ // If default path hasn't been added already, add it now..
+ if (CDDB_LOCAL_PATH)
+ {
+ path_utf8 = filename_to_display(CDDB_LOCAL_PATH);
+ Add_String_To_Combo_List(CddbLocalPathModel, path_utf8);
+ if (path_utf8)
+ gtk_entry_set_text(GTK_ENTRY(GTK_BIN(CddbLocalPath)->child), path_utf8);
+ g_free(path_utf8);
+ }
+
+ Button = Create_Button_With_Pixmap(BUTTON_BROWSE);
+ gtk_box_pack_start(GTK_BOX(hbox),Button,FALSE,FALSE,0);
+ g_signal_connect_swapped(G_OBJECT(Button),"clicked",
+ G_CALLBACK(File_Selection_Window_For_Directory),G_OBJECT(GTK_BIN(CddbLocalPath)->child));
+
+ // CDDB Proxy Settings
+ Frame = gtk_frame_new (_("Proxy Settings"));
+ gtk_box_pack_start(GTK_BOX(VBox),Frame,FALSE,FALSE,0);
+
+ Table = gtk_table_new(3,5,FALSE);
+ gtk_container_add(GTK_CONTAINER(Frame),Table);
+ gtk_table_set_row_spacings(GTK_TABLE(Table),2);
+ gtk_table_set_col_spacings(GTK_TABLE(Table),4);
+ gtk_container_set_border_width(GTK_CONTAINER(Table), 2);
+
+ CddbUseProxy = gtk_check_button_new_with_label(_("Use a proxy"));
+ gtk_table_attach(GTK_TABLE(Table),CddbUseProxy,0,5,0,1,GTK_FILL,GTK_FILL,0,0);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(CddbUseProxy),CDDB_USE_PROXY);
+ gtk_tooltips_set_tip(Tips,CddbUseProxy,_("Set active the settings of the proxy server."),NULL);
+
+ Label = gtk_label_new(" ");
+ gtk_table_attach(GTK_TABLE(Table),Label,0,1,1,2,GTK_FILL,GTK_FILL,0,0);
+
+ Label = gtk_label_new(_("Host Name :"));
+ gtk_table_attach(GTK_TABLE(Table),Label,1,2,1,2,GTK_FILL,GTK_FILL,0,0);
+ gtk_misc_set_alignment(GTK_MISC(Label),1,0.5);
+ CddbProxyName = gtk_entry_new();
+ gtk_table_attach(GTK_TABLE(Table),CddbProxyName,2,3,1,2,GTK_FILL,GTK_FILL,0,0);
+ if (CDDB_PROXY_NAME)
+ gtk_entry_set_text(GTK_ENTRY(CddbProxyName),CDDB_PROXY_NAME);
+ gtk_tooltips_set_tip(Tips,CddbProxyName,_("Name of the proxy server."),NULL);
+ Label = gtk_label_new (_("Port :"));
+ gtk_table_attach(GTK_TABLE(Table),Label,3,4,1,2,GTK_FILL,GTK_FILL,0,0);
+ gtk_misc_set_alignment(GTK_MISC(Label),1,0.5);
+ CddbProxyPort = gtk_entry_new();
+ gtk_widget_set_size_request(GTK_WIDGET(CddbProxyPort), 45, -1);
+ gtk_entry_set_max_length(GTK_ENTRY(CddbProxyPort),5);
+ gtk_table_attach(GTK_TABLE(Table),CddbProxyPort,4,5,1,2,GTK_FILL,GTK_FILL,0,0);
+ gtk_tooltips_set_tip(Tips,CddbProxyPort,_("Port of the proxy server."),NULL);
+ sprintf(temp,"%i",CDDB_PROXY_PORT);
+ gtk_entry_set_text(GTK_ENTRY(CddbProxyPort),temp);
+ g_signal_connect(G_OBJECT(CddbProxyPort),"insert_text",G_CALLBACK(Insert_Only_Digit),NULL);
+ g_signal_connect(G_OBJECT(CddbUseProxy),"toggled",G_CALLBACK(Cddb_Use_Proxy_Toggled),NULL);
+ Label = gtk_label_new(_("User Name :"));
+ gtk_table_attach(GTK_TABLE(Table),Label,1,2,2,3,GTK_FILL,GTK_FILL,0,0);
+ gtk_misc_set_alignment(GTK_MISC(Label),1,0.5);
+ CddbProxyUserName = gtk_entry_new();
+ if (CDDB_PROXY_USER_NAME)
+ gtk_entry_set_text(GTK_ENTRY(CddbProxyUserName),CDDB_PROXY_USER_NAME);
+ gtk_table_attach(GTK_TABLE(Table),CddbProxyUserName,2,3,2,3,GTK_FILL,GTK_FILL,0,0);
+ gtk_tooltips_set_tip(Tips,CddbProxyUserName,_("Name of user for the the proxy server."),NULL);
+ Label = gtk_label_new(_("User Password :"));
+ gtk_table_attach(GTK_TABLE(Table),Label,3,4,2,3,GTK_FILL,GTK_FILL,0,0);
+ gtk_misc_set_alignment(GTK_MISC(Label),1,0.5);
+ CddbProxyUserPassword = gtk_entry_new();
+ if (CDDB_PROXY_USER_PASSWORD)
+ gtk_entry_set_text(GTK_ENTRY(CddbProxyUserPassword),CDDB_PROXY_USER_PASSWORD);
+ gtk_table_attach(GTK_TABLE(Table),CddbProxyUserPassword,4,5,2,3,GTK_FILL,GTK_FILL,0,0);
+ gtk_entry_set_visibility(GTK_ENTRY(CddbProxyUserPassword),FALSE);
+ gtk_tooltips_set_tip(Tips,CddbProxyUserPassword,_("Password of user for the the proxy server."),NULL);
+ Cddb_Use_Proxy_Toggled();
+
+
+ // Track Name list (CDDB results)
+ Frame = gtk_frame_new (_("Track Name List"));
+ gtk_box_pack_start(GTK_BOX(VBox),Frame,FALSE,FALSE,0);
+
+ vbox = gtk_vbox_new(FALSE,2);
+ gtk_container_add(GTK_CONTAINER(Frame),vbox);
+ gtk_container_set_border_width(GTK_CONTAINER(vbox), 2);
+
+ CddbFollowFile = gtk_check_button_new_with_label(_("Select corresponding audio "
+ "file (according position or DLM if activated below)"));
+ gtk_box_pack_start(GTK_BOX(vbox),CddbFollowFile,FALSE,FALSE,0);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(CddbFollowFile),CDDB_FOLLOW_FILE);
+ gtk_tooltips_set_tip(Tips,CddbFollowFile,_("If activated, when selecting a "
+ "line in the list of tracks name, the corresponding audio file in the "
+ "main list will be also selected."),NULL);
+
+ // Check box to use DLM (also used in the cddb window)
+ CddbUseDLM = gtk_check_button_new_with_label(_("Use the Levenshtein algorithm "
+ "(DLM) to match lines (using title) with audio files (using filename)"));
+ gtk_box_pack_start(GTK_BOX(vbox),CddbUseDLM,FALSE,FALSE,0);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(CddbUseDLM),CDDB_USE_DLM);
+ gtk_tooltips_set_tip(Tips,CddbUseDLM,_("When activating this option, the "
+ "Levenshtein algorithm (DLM : Damerau-Levenshtein Metric) will be used "
+ "to match the CDDB title against every file name in the current folder, "
+ "and to select the best match. This will be used when selecting the "
+ "corresponding audio file, or applying cddb results, instead of using "
+ "directly the position order."),NULL);
+
+
+ /*
+ * Confirmation
+ */
+ Label = gtk_label_new (_("Confirmation"));
+ Frame = gtk_frame_new (_("Confirmation"));
+ gtk_notebook_append_page (GTK_NOTEBOOK(OptionsNoteBook),Frame,Label);
+ gtk_container_set_border_width(GTK_CONTAINER(Frame), 5);
+
+ VBox = gtk_vbox_new(FALSE,2);
+ gtk_container_add(GTK_CONTAINER(Frame),VBox);
+ gtk_container_set_border_width(GTK_CONTAINER(VBox), 2);
+
+ ConfirmBeforeExit = gtk_check_button_new_with_label(_("Confirm exit from program"));
+ gtk_box_pack_start(GTK_BOX(VBox),ConfirmBeforeExit,FALSE,FALSE,0);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ConfirmBeforeExit),CONFIRM_BEFORE_EXIT);
+ gtk_tooltips_set_tip(Tips,ConfirmBeforeExit,_("If activated, opens a dialog box to ask "
+ "confirmation before exiting the program."),NULL);
+
+ ConfirmWriteTag = gtk_check_button_new_with_label(_("Confirm writing of file tag"));
+ gtk_box_pack_start(GTK_BOX(VBox),ConfirmWriteTag,FALSE,FALSE,0);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ConfirmWriteTag),CONFIRM_WRITE_TAG);
+
+ ConfirmRenameFile = gtk_check_button_new_with_label(_("Confirm renaming of file"));
+ gtk_box_pack_start(GTK_BOX(VBox),ConfirmRenameFile,FALSE,FALSE,0);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ConfirmRenameFile),CONFIRM_RENAME_FILE);
+
+ ConfirmDeleteFile = gtk_check_button_new_with_label(_("Confirm deleting of file"));
+ gtk_box_pack_start(GTK_BOX(VBox),ConfirmDeleteFile,FALSE,FALSE,0);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ConfirmDeleteFile),CONFIRM_DELETE_FILE);
+
+ ConfirmWritePlayList = gtk_check_button_new_with_label(_("Confirm writing of playlist"));
+ gtk_box_pack_start(GTK_BOX(VBox),ConfirmWritePlayList,FALSE,FALSE,0);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ConfirmWritePlayList),CONFIRM_WRITE_PLAYLIST);
+
+
+
+ /*
+ * Buttons box of Option Window
+ */
+ ButtonBox = gtk_hbutton_box_new ();
+ gtk_box_pack_start(GTK_BOX(OptionsVBox), ButtonBox, FALSE, FALSE, 4);
+
+ gtk_button_box_set_layout (GTK_BUTTON_BOX (ButtonBox), GTK_BUTTONBOX_END);
+ gtk_box_set_spacing (GTK_BOX(ButtonBox), 15);
+
+
+ /* Apply Button */
+ Button = Create_Button_With_Pixmap(BUTTON_APPLY);
+ // Disable temporarily the apply button
+ ////gtk_container_add(GTK_CONTAINER(ButtonBox),Button);
+ g_signal_connect(G_OBJECT(Button),"clicked",G_CALLBACK(OptionsWindow_Apply_Button),NULL);
+ GTK_WIDGET_SET_FLAGS(Button, GTK_CAN_DEFAULT);
+ gtk_tooltips_set_tip(Tips,Button,_("Apply changes (but don't save) and close this window"),NULL);
+
+
+ /* Cancel Button */
+ Button = Create_Button_With_Pixmap(BUTTON_CANCEL);
+ gtk_container_add(GTK_CONTAINER(ButtonBox), Button);
+ g_signal_connect(G_OBJECT(Button),"clicked", G_CALLBACK(OptionsWindow_Cancel_Button),NULL);
+ GTK_WIDGET_SET_FLAGS(Button, GTK_CAN_DEFAULT);
+ gtk_widget_grab_default (Button);
+ gtk_tooltips_set_tip(Tips,Button,_("Close this window without saving"),NULL);
+
+
+ /* Save Button */
+ Button = Create_Button_With_Pixmap(BUTTON_SAVE);
+ gtk_container_add(GTK_CONTAINER(ButtonBox), Button);
+ g_signal_connect(G_OBJECT(Button),"clicked", G_CALLBACK(OptionsWindow_Save_Button),NULL);
+ GTK_WIDGET_SET_FLAGS(Button, GTK_CAN_DEFAULT);
+ gtk_tooltips_set_tip(Tips,Button,_("Save changes and close this window"),NULL);
+
+ /* Show all in the options window */
+ gtk_widget_show_all(OptionsWindow);
+
+ /* Load the default page */
+ gtk_notebook_set_current_page(GTK_NOTEBOOK(OptionsNoteBook), OPTIONS_NOTEBOOK_PAGE);
+}
+
+
+void Set_Default_Comment_Check_Button_Toggled (void)
+{
+ gtk_widget_set_sensitive(DefaultComment,GTK_TOGGLE_BUTTON(SetDefaultComment)->active);
+}
+
+void Number_Track_Formated_Toggled (void)
+{
+ gtk_widget_set_sensitive(NumberTrackFormatedSpinButton,GTK_TOGGLE_BUTTON(NumberTrackFormated)->active);
+ // To update the example...
+ g_signal_emit_by_name(G_OBJECT(NumberTrackFormatedSpinButton),"changed",NULL);
+}
+
+void Number_Track_Formated_Spin_Button_Changed (GtkObject *Label, GtkObject *SpinButton)
+{
+ gchar *tmp;
+ gint val;
+
+ if (GTK_TOGGLE_BUTTON(NumberTrackFormated)->active)
+ val = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(SpinButton));
+ else
+ val = 1;
+
+ // For translators : be aware to NOT translate '%.*d' in this string
+ tmp = g_strdup_printf(_("(Example : %.*d_-_Track_name_1.mp3)"),val,1);
+
+ gtk_label_set_text(GTK_LABEL(Label),tmp);
+ g_free(tmp);
+}
+
+void Use_Non_Standard_Id3_Reading_Character_Set_Toggled (void)
+{
+ gtk_widget_set_sensitive(FileReadingId3v1v2CharacterSetCombo,
+ GTK_TOGGLE_BUTTON(UseNonStandardId3ReadingCharacterSet)->active);
+}
+void Change_Id3_Settings_Toggled (void)
+{
+ int active;
+
+ if ( !FileWritingId3v2UseUnicodeCharacterSet
+ || !FileWritingId3v2UseNoUnicodeCharacterSet
+ || !FileWritingId3v2WriteTag
+#ifdef ENABLE_ID3LIB
+ || !FileWritingId3v2VersionCombo
+ || !LabelId3v2Version
+#endif
+ || !FileWritingId3v1WriteTag
+ || !LabelId3v2Charset
+ || !FileWritingId3v2UseUnicodeCharacterSet
+ || !FileWritingId3v2UseNoUnicodeCharacterSet
+ || !FileWritingId3v2UnicodeCharacterSetCombo
+ || !FileWritingId3v2NoUnicodeCharacterSetCombo
+ || !LabelAdditionalId3v2IconvOptions
+ || !FileWritingId3v2IconvOptionsNo
+ || !FileWritingId3v2IconvOptionsTranslit
+ || !FileWritingId3v2IconvOptionsIgnore
+ || !FileWritingId3v2UseCrc32
+ || !FileWritingId3v2UseCompression
+ || !ConvertOldId3v2TagVersion
+ || !LabelId3v1Charset
+ || !FileWritingId3v1CharacterSetCombo
+ || !LabelAdditionalId3v1IconvOptions
+ || !FileWritingId3v1IconvOptionsNo
+ || !FileWritingId3v1IconvOptionsTranslit
+ || !FileWritingId3v1IconvOptionsIgnore
+ )
+ return;
+
+ active = (GTK_TOGGLE_BUTTON(FileWritingId3v2UseUnicodeCharacterSet)->active != 0);
+
+ if (GTK_TOGGLE_BUTTON(FileWritingId3v2WriteTag)->active)
+ {
+ gtk_widget_set_sensitive(LabelId3v2Charset, 1);
+
+#ifdef ENABLE_ID3LIB
+ gtk_widget_set_sensitive(LabelId3v2Version, 1);
+ gtk_widget_set_sensitive(FileWritingId3v2VersionCombo, 1);
+ if (gtk_combo_box_get_active(GTK_COMBO_BOX(FileWritingId3v2VersionCombo)) == 1) {
+ gtk_combo_box_set_active(GTK_COMBO_BOX(FileWritingId3v2UnicodeCharacterSetCombo), 1);
+ gtk_widget_set_sensitive(FileWritingId3v2UnicodeCharacterSetCombo, 0);
+ }else
+ gtk_widget_set_sensitive(FileWritingId3v2UnicodeCharacterSetCombo, active);
+#else
+ gtk_widget_set_sensitive(FileWritingId3v2UnicodeCharacterSetCombo, active);
+#endif
+ gtk_widget_set_sensitive(FileWritingId3v2UseUnicodeCharacterSet, 1);
+ gtk_widget_set_sensitive(FileWritingId3v2UseNoUnicodeCharacterSet, 1);
+ gtk_widget_set_sensitive(FileWritingId3v2NoUnicodeCharacterSetCombo, !active);
+ gtk_widget_set_sensitive(LabelAdditionalId3v2IconvOptions, !active);
+ gtk_widget_set_sensitive(FileWritingId3v2IconvOptionsNo, !active);
+ gtk_widget_set_sensitive(FileWritingId3v2IconvOptionsTranslit, !active);
+ gtk_widget_set_sensitive(FileWritingId3v2IconvOptionsIgnore, !active);
+ gtk_widget_set_sensitive(FileWritingId3v2UseCrc32, 1);
+ gtk_widget_set_sensitive(FileWritingId3v2UseCompression, 1);
+ gtk_widget_set_sensitive(ConvertOldId3v2TagVersion, 1);
+
+ }else
+ {
+ gtk_widget_set_sensitive(LabelId3v2Charset, 0);
+#ifdef ENABLE_ID3LIB
+ gtk_widget_set_sensitive(LabelId3v2Version, 0);
+ gtk_widget_set_sensitive(FileWritingId3v2VersionCombo, 0);
+#endif
+ gtk_widget_set_sensitive(FileWritingId3v2UseUnicodeCharacterSet, 0);
+ gtk_widget_set_sensitive(FileWritingId3v2UseNoUnicodeCharacterSet, 0);
+ gtk_widget_set_sensitive(FileWritingId3v2UnicodeCharacterSetCombo, 0);
+ gtk_widget_set_sensitive(FileWritingId3v2NoUnicodeCharacterSetCombo, 0);
+ gtk_widget_set_sensitive(LabelAdditionalId3v2IconvOptions, 0);
+ gtk_widget_set_sensitive(FileWritingId3v2IconvOptionsNo, 0);
+ gtk_widget_set_sensitive(FileWritingId3v2IconvOptionsTranslit, 0);
+ gtk_widget_set_sensitive(FileWritingId3v2IconvOptionsIgnore, 0);
+ gtk_widget_set_sensitive(FileWritingId3v2UseCrc32, 0);
+ gtk_widget_set_sensitive(FileWritingId3v2UseCompression, 0);
+ gtk_widget_set_sensitive(ConvertOldId3v2TagVersion, 0);
+ }
+
+ active = GTK_TOGGLE_BUTTON(FileWritingId3v1WriteTag)->active;
+
+ gtk_widget_set_sensitive(LabelId3v1Charset, active);
+ gtk_widget_set_sensitive(FileWritingId3v1CharacterSetCombo, active);
+ gtk_widget_set_sensitive(LabelAdditionalId3v1IconvOptions, active);
+ gtk_widget_set_sensitive(FileWritingId3v1IconvOptionsNo, active);
+ gtk_widget_set_sensitive(FileWritingId3v1IconvOptionsTranslit, active);
+ gtk_widget_set_sensitive(FileWritingId3v1IconvOptionsIgnore, active);
+}
+
+void Cddb_Use_Proxy_Toggled (void)
+{
+ gtk_widget_set_sensitive(CddbProxyName,GTK_TOGGLE_BUTTON(CddbUseProxy)->active);
+ gtk_widget_set_sensitive(CddbProxyPort,GTK_TOGGLE_BUTTON(CddbUseProxy)->active);
+ gtk_widget_set_sensitive(CddbProxyUserName,GTK_TOGGLE_BUTTON(CddbUseProxy)->active);
+ gtk_widget_set_sensitive(CddbProxyUserPassword,GTK_TOGGLE_BUTTON(CddbUseProxy)->active);
+}
+
+/* Callback from Open_OptionsWindow */
+gboolean OptionsWindow_Key_Press (GtkWidget *window, GdkEvent *event)
+{
+ GdkEventKey *kevent;
+
+ if (event && event->type == GDK_KEY_PRESS)
+ {
+ kevent = (GdkEventKey *)event;
+ switch(kevent->keyval)
+ {
+ case GDK_Escape:
+ {
+ OptionsWindow_Quit();
+ break;
+ }
+ }
+ }
+ return FALSE;
+}
+
+/* Callback from Open_OptionsWindow */
+void OptionsWindow_Apply_Button(void)
+{
+ if (!Check_Config()) return;
+
+#ifndef WIN32
+ /* FIXME : make gtk crash on win32 */
+ Add_String_To_Combo_List(DefaultPathModel, (gchar*) gtk_entry_get_text(GTK_ENTRY(GTK_BIN(DefaultPathToMp3)->child)));
+ Add_String_To_Combo_List(FilePlayerModel, (gchar*) gtk_entry_get_text(GTK_ENTRY(GTK_BIN(FilePlayerCombo)->child)));
+ Add_String_To_Combo_List(DefaultCommentModel, (gchar*) gtk_entry_get_text(GTK_ENTRY(GTK_BIN(DefaultComment)->child)));
+ Add_String_To_Combo_List(CddbLocalPathModel, (gchar*) gtk_entry_get_text(GTK_ENTRY(GTK_BIN(CddbLocalPath)->child)));
+#endif
+
+ Apply_Changes_Of_Preferences_Window();
+
+ OptionsWindow_Quit();
+ Statusbar_Message(_("Changes applied"),TRUE);
+}
+
+/* Callback from Open_OptionsWindow */
+void OptionsWindow_Save_Button(void)
+{
+ if (!Check_Config()) return;
+
+#ifndef WIN32
+ /* FIXME : make gtk crash on win32 */
+ Add_String_To_Combo_List(DefaultPathModel, (gchar*) gtk_entry_get_text(GTK_ENTRY(GTK_BIN(DefaultPathToMp3)->child)));
+ Add_String_To_Combo_List(FilePlayerModel, (gchar*) gtk_entry_get_text(GTK_ENTRY(GTK_BIN(FilePlayerCombo)->child)));
+ Add_String_To_Combo_List(DefaultCommentModel, (gchar*) gtk_entry_get_text(GTK_ENTRY(GTK_BIN(DefaultComment)->child)));
+ Add_String_To_Combo_List(CddbLocalPathModel, (gchar*) gtk_entry_get_text(GTK_ENTRY(GTK_BIN(CddbLocalPath)->child)));
+#endif
+
+ Save_Changes_Of_Preferences_Window();
+
+ OptionsWindow_Quit();
+ Statusbar_Message(_("Configuration saved"),TRUE);
+}
+
+/* Callback from Open_OptionsWindow */
+void OptionsWindow_Cancel_Button(void)
+{
+ OptionsWindow_Quit();
+ Statusbar_Message(_("Configuration unchanged"),TRUE);
+}
+
+/* Callback from Open_OptionsWindow */
+void OptionsWindow_Quit(void)
+{
+ if (OptionsWindow)
+ {
+ /* Save combobox history lists before exit */
+ Save_Default_Path_To_MP3_List (DefaultPathModel, MISC_COMBO_TEXT);
+ Save_Default_Tag_Comment_Text_List(DefaultCommentModel, MISC_COMBO_TEXT);
+ Save_Audio_File_Player_List (FilePlayerModel, MISC_COMBO_TEXT);
+ Save_Cddb_Local_Path_List (CddbLocalPathModel, MISC_COMBO_TEXT);
+
+ OptionsWindow_Apply_Changes();
+
+ /* Now quit */
+ gtk_widget_destroy(OptionsWindow);
+ OptionsWindow = (GtkWidget *)NULL;
+ gtk_widget_set_sensitive(MainWindow, TRUE);
+ }
+}
+
+/*
+ * For the configuration file...
+ */
+void OptionsWindow_Apply_Changes (void)
+{
+ if (OptionsWindow)
+ {
+ //gint x, y;
+ gint width, height;
+
+ if ( OptionsWindow->window!=NULL && gdk_window_is_visible(OptionsWindow->window)
+ && gdk_window_get_state(OptionsWindow->window)!=GDK_WINDOW_STATE_MAXIMIZED )
+ {
+ // Position and Origin of the preferences window
+ //gdk_window_get_root_origin(OptionsWindow->window,&x,&y);
+ //OPTIONS_WINDOW_X = x;
+ //OPTIONS_WINDOW_Y = y;
+ gdk_window_get_size(OptionsWindow->window,&width,&height);
+ OPTIONS_WINDOW_WIDTH = width;
+ OPTIONS_WINDOW_HEIGHT = height;
+ }
+
+ /* Get the last visible notebook page */
+ OPTIONS_NOTEBOOK_PAGE = gtk_notebook_get_current_page(GTK_NOTEBOOK(OptionsNoteBook));
+ }
+}
+
+
+
+/*
+ * Check_Config: Check if config informations are correct
+ * dsd: Check this... going from utf8 to raw is dodgy stuff
+ *
+ * Problem noted : if a character is escaped (like : 'C\351line DION') in
+ * gtk_file_chooser it will converted to UTF-8. So after, there
+ * is a problem to convert it in the right system encoding to be
+ * passed to stat(), and it can find the directory.
+ * exemple :
+ * - initial file on system : C\351line DION - D'eux (1995)
+ * - converted to UTF-8 (path_utf8) : Céline DION - D'eux (1995)
+ * - try to convert to system encoding (path_real) : ?????
+ */
+gint Check_DefaultPathToMp3 (void)
+{
+ gchar *path_utf8;
+ gchar *path_real;
+ struct stat stbuf;
+
+ path_utf8 = g_strdup(gtk_entry_get_text(GTK_ENTRY(GTK_BIN(DefaultPathToMp3)->child)));
+ if (!path_utf8 || g_utf8_strlen(path_utf8, -1) < 1)
+ {
+ g_free(path_utf8);
+ return 1;
+ }
+
+ path_real = filename_from_display(path_utf8);
+
+#ifdef WIN32
+ /* On win32 : stat("c:\path\to\dir") succeed, while stat("c:\path\to\dir\") fails */
+ ET_Win32_Path_Remove_Trailing_Backslash(path_real);
+#endif
+
+ if ( stat(path_real,&stbuf)==0 && S_ISDIR(stbuf.st_mode) )
+ {
+ g_free(path_real);
+ g_free(path_utf8);
+ return 1; /* Path is good */
+ }else
+ {
+ gchar *msg = g_strdup_printf(_(" The selected path for 'Default path to "
+ "files' isn't valid!\n'%s'\n(%s) "),path_utf8,
+ (stat(path_real,&stbuf)==0)?_("Not a directory"):g_strerror(errno) );
+ GtkWidget *msgbox = msg_box_new(_("Error..."),msg,GTK_STOCK_DIALOG_ERROR,BUTTON_OK,0);
+ msg_box_hide_check_button(MSG_BOX(msgbox));
+ gtk_window_set_transient_for(GTK_WINDOW(msgbox),GTK_WINDOW(OptionsWindow));
+ msg_box_run(MSG_BOX(msgbox));
+ gtk_widget_destroy(msgbox);
+ g_free(msg);
+ g_free(path_real);
+ g_free(path_utf8);
+ return 0;
+ }
+}
+
+/*
+ * The character set conversion is used for ID3 tag. UTF-8 is used to display.
+ * - reading_character is converted to UTF-8
+ * - writing_character is converted from UTF-8
+ */
+/*****************
+gint Check_CharacterSetTranslation (void)
+{
+ gchar *temp;
+ gchar *reading_character;
+ gchar *writing_character;
+
+ temp = Get_Active_Combo_Box_Item(GTK_COMBO_BOX(FileReadingCharacterSetCombo));
+ reading_character = Charset_Get_Name_From_Title(temp);
+ g_free(temp);
+
+ temp = Get_Active_Combo_Box_Item(GTK_COMBO_BOX(FileWritingCharacterSetCombo));
+ writing_character = Charset_Get_Name_From_Title(temp);
+ g_free(temp);
+
+ // Check conversion when reading file
+ if ( GTK_TOGGLE_BUTTON(UseNonStandardId3ReadingCharacterSet)->active
+ && (test_conversion_charset(reading_character,"UTF-8")!=TRUE) )
+ {
+ gchar *msg = g_strdup_printf(_("The character set translation from '%s'\n"
+ "to '%s' isn't supported!"),reading_character,"UTF-8");
+ GtkWidget *msgbox = msg_box_new(_("Error..."),msg,GTK_STOCK_DIALOG_ERROR,BUTTON_OK,0);
+ msg_box_hide_check_button(MSG_BOX(msgbox));
+ gtk_window_set_transient_for(GTK_WINDOW(msgbox),GTK_WINDOW(OptionsWindow));
+ msg_box_run(MSG_BOX(msgbox));
+ gtk_widget_destroy(msgbox);
+ g_free(msg);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(UseNonStandardId3ReadingCharacterSet),FALSE);
+ return 0;
+ }
+ // Check conversion when writing file
+ if ( GTK_TOGGLE_BUTTON(UseNonStandardId3WritingCharacterSet)->active
+ && (test_conversion_charset("UTF-8",writing_character)!=TRUE) )
+ {
+ gchar *msg = g_strdup_printf(_("The character set translation from '%s'\n"
+ "to '%s' isn't supported!"),"UTF-8",writing_character);
+ GtkWidget *msgbox = msg_box_new(_("Error..."),msg,GTK_STOCK_DIALOG_ERROR,BUTTON_OK,0);
+ msg_box_hide_check_button(MSG_BOX(msgbox));
+ gtk_window_set_transient_for(GTK_WINDOW(msgbox),GTK_WINDOW(OptionsWindow));
+ msg_box_run(MSG_BOX(msgbox));
+ gtk_widget_destroy(msgbox);
+ g_free(msg);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(UseNonStandardId3WritingCharacterSet),FALSE);
+ return 0;
+ }
+
+ return 1;
+}
+*************/
+
+gint Check_DefaultComment (void)
+{
+ const gchar *file;
+
+ file = gtk_entry_get_text(GTK_ENTRY(GTK_BIN(DefaultComment)->child));
+ if (!file || g_utf8_strlen(file, -1) < 1)
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(SetDefaultComment),FALSE);
+
+ return 1; /* A blank entry is ignored */
+}
+
+/*
+ * Check if player binary is found
+ */
+gint Check_FilePlayerCombo (void)
+{
+ gchar *program_path = NULL;
+ gchar *program_path_validated = NULL;
+
+#ifdef WIN32
+ return 1; /* FIXME see Check_If_Executable_Exists */
+ /* Note : Check_If_Executable_Exists crashes when player is 'winamp.exe' with g_find_program_in_path */
+#endif
+
+ // The program typed
+ program_path = g_strdup(gtk_entry_get_text(GTK_ENTRY(GTK_BIN(FilePlayerCombo)->child)));
+ g_strstrip(program_path);
+ // The program file validated
+ if (program_path && strlen(program_path)>0)
+ program_path_validated = Check_If_Executable_Exists(program_path);
+
+ if ( program_path && strlen(program_path)>0 && !program_path_validated ) // A file is typed but it is invalid!
+ {
+ gchar *msg = g_strdup_printf(_("The audio file player '%s' can't be found!"),
+ gtk_entry_get_text(GTK_ENTRY(GTK_BIN(FilePlayerCombo)->child)));
+ GtkWidget *msgbox = msg_box_new(_("Error..."),msg,GTK_STOCK_DIALOG_ERROR,BUTTON_OK,0);
+ msg_box_hide_check_button(MSG_BOX(msgbox));
+ gtk_window_set_transient_for(GTK_WINDOW(msgbox),GTK_WINDOW(OptionsWindow));
+ msg_box_run(MSG_BOX(msgbox));
+ gtk_widget_destroy(msgbox);
+ g_free(msg);
+
+ g_free(program_path);
+ return 0;
+ } else
+ {
+ g_free(program_path);
+ g_free(program_path_validated);
+ return 1;
+ }
+}
+
+gint Check_Config (void)
+{
+ if ( Check_DefaultPathToMp3()
+ //&& Check_CharacterSetTranslation()
+ && Check_DefaultComment()
+ && Check_FilePlayerCombo() )
+ return 1; /* No problem detected */
+ else
+ return 0; /* Oups! */
+}
+
+
+
+/*
+ * Manage Check buttons into Scanner tab: conversion group
+ * This reproduces "something" like the behaviour of radio buttons with check buttons
+ */
+void Scanner_Convert_Check_Button_Toggled_1 (GtkObject *object_rec, GtkObject *object_emi)
+{
+ if (!object_rec || !object_emi) return;
+
+ if (GTK_TOGGLE_BUTTON(object_emi)->active == TRUE)
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(object_rec),!GTK_TOGGLE_BUTTON(object_emi)->active);
+
+}
+
+
+void DefaultPathToMp3_Combo_Add_String (void)
+{
+ const gchar *path;
+
+ path = gtk_entry_get_text(GTK_ENTRY(GTK_BIN(DefaultPathToMp3)->child));
+ Add_String_To_Combo_List(GTK_LIST_STORE(DefaultPathModel), (gchar *)path);
+}
+
+void CddbLocalPath_Combo_Add_String (void)
+{
+ const gchar *path;
+
+ path = gtk_entry_get_text(GTK_ENTRY(GTK_BIN(CddbLocalPath)->child));
+ Add_String_To_Combo_List(GTK_LIST_STORE(CddbLocalPath), (gchar *)path);
+}
+
+
diff --git a/src/prefs.h b/src/prefs.h
new file mode 100755
index 0000000..79532a4
--- /dev/null
+++ b/src/prefs.h
@@ -0,0 +1,182 @@
+/* prefs.h - 2000/05/06 */
+/*
+ * EasyTAG - Tag editor for MP3 and Ogg Vorbis files
+ * Copyright (C) 2000-2003 Jerome Couderc <easytag@gmail.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef __PREFS_H__
+#define __PREFS_H__
+
+
+/***************
+ * Declaration *
+ ***************/
+
+GtkWidget *OptionsWindow;
+GtkWidget *OptionsNoteBook;
+gint OptionsNoteBook_Scanner_Page_Num; /* Contains the number of the page "Scanner Option" */
+
+
+/* Widgets included in config */
+/* Common */
+GtkWidget *LoadOnStartup;
+GtkWidget *DefaultPathToMp3;
+GtkWidget *BrowserLineStyleOptionMenu;
+GtkWidget *BrowserExpanderStyleOptionMenu;
+GtkWidget *BrowseSubdir;
+GtkWidget *BrowseHiddendir;
+GtkWidget *OpenSelectedBrowserNode;
+
+GtkListStore *DefaultPathModel;
+
+/* User interface */
+GtkWidget *ShowHeaderInfos;
+GtkWidget *ChangedFilesDisplayedToRed;
+GtkWidget *ChangedFilesDisplayedToBold;
+
+/* Misc */
+GtkWidget *SortingFileCombo;
+GtkWidget *SortingFileCaseSensitive;
+
+GtkWidget *MessageBoxPositionNone;
+GtkWidget *MessageBoxPositionCenter;
+GtkWidget *MessageBoxPositionMouse;
+GtkWidget *MessageBoxPositionCenterOnParent;
+
+GtkWidget *FilePlayerCombo;
+GtkListStore *FilePlayerModel;
+
+/* File Settings */
+GtkWidget *ReplaceIllegalCharactersInFilename;
+GtkWidget *FilenameExtensionNoChange;
+GtkWidget *FilenameExtensionLowerCase;
+GtkWidget *FilenameExtensionUpperCase;
+GtkWidget *PreserveModificationTime;
+
+GtkWidget *FilenameCharacterSetOther;
+GtkWidget *FilenameCharacterSetApproximate;
+GtkWidget *FilenameCharacterSetDiscard;
+
+/* Tag Settings */
+GtkWidget *FileWritingId3v2WriteTag;
+GtkWidget *FileWritingId3v1WriteTag;
+GtkWidget *WriteId3TagsInFlacFiles;
+GtkWidget *FileWritingId3v2UseCrc32;
+GtkWidget *FileWritingId3v2UseCompression;
+GtkWidget *StripTagWhenEmptyFields;
+GtkWidget *ConvertOldId3v2TagVersion;
+
+GtkWidget *FileWritingId3v2VersionCombo;
+GtkWidget *FileWritingId3v2UnicodeCharacterSetCombo;
+GtkWidget *FileWritingId3v2NoUnicodeCharacterSetCombo;
+GtkWidget *FileWritingId3v1CharacterSetCombo;
+GtkWidget *FileWritingId3v2UseUnicodeCharacterSet;
+GtkWidget *FileWritingId3v2UseNoUnicodeCharacterSet;
+GtkWidget *UseNonStandardId3ReadingCharacterSet;
+GtkWidget *FileReadingId3v1v2CharacterSetCombo;
+
+GtkWidget *FileReadingId3v1v2CharacterSetCombo;
+GtkWidget *FileWritingId3v2IconvOptionsNo;
+GtkWidget *FileWritingId3v2IconvOptionsTranslit;
+GtkWidget *FileWritingId3v2IconvOptionsIgnore;
+GtkWidget *FileWritingId3v1IconvOptionsNo;
+GtkWidget *FileWritingId3v1IconvOptionsTranslit;
+GtkWidget *FileWritingId3v1IconvOptionsIgnore;
+
+GtkWidget *LabelAdditionalId3v1IconvOptions;
+GtkWidget *LabelAdditionalId3v2IconvOptions;
+GtkWidget *LabelId3v2Charset;
+GtkWidget *LabelId3v1Charset;
+GtkWidget *LabelId3v2Version;
+
+GtkWidget *DateAutoCompletion;
+GtkWidget *NumberTrackFormated;
+GtkWidget *NumberTrackFormatedSpinButton;
+GtkWidget *OggTagWriteXmmsComment;
+GtkWidget *SetFocusToSameTagField;
+GtkWidget *SetFocusToFirstTagField;
+
+
+/* Scanner */
+GtkWidget *FTSConvertUnderscoreAndP20IntoSpace;
+GtkWidget *FTSConvertSpaceIntoUnderscore;
+GtkWidget *RFSConvertUnderscoreAndP20IntoSpace;
+GtkWidget *RFSConvertSpaceIntoUnderscore;
+GtkWidget *PFSDontUpperSomeWords;
+GtkWidget *OverwriteTagField;
+GtkWidget *OpenScannerWindowOnStartup;
+GtkWidget *ScannerWindowOnTop;
+
+GtkWidget *SetDefaultComment;
+GtkWidget *DefaultComment;
+GtkWidget *Crc32Comment;
+GtkListStore *DefaultCommentModel;
+
+/* CDDB */
+GtkWidget *CddbServerNameAutomaticSearch;
+GtkWidget *CddbServerPortAutomaticSearch;
+GtkWidget *CddbServerCgiPathAutomaticSearch;
+GtkWidget *CddbServerNameAutomaticSearch2;
+GtkWidget *CddbServerPortAutomaticSearch2;
+GtkWidget *CddbServerCgiPathAutomaticSearch2;
+GtkWidget *CddbServerNameManualSearch;
+GtkWidget *CddbServerPortManualSearch;
+GtkWidget *CddbServerCgiPathManualSearch;
+GtkWidget *CddbUseProxy;
+GtkWidget *CddbProxyName;
+GtkWidget *CddbProxyPort;
+GtkWidget *CddbProxyUserName;
+GtkWidget *CddbProxyUserPassword;
+
+GtkWidget *CddbLocalPath;
+GtkListStore *CddbLocalPathModel;
+
+GtkWidget *SetCddbWindowSize;
+GtkWidget *CddbWindowWidth;
+GtkWidget *CddbWindowHeight;
+GtkWidget *CddbWindowButton;
+GtkWidget *SetCddbPaneHandlePosition;
+GtkWidget *CddbPaneHandlePosition;
+GtkWidget *CddbPaneHandleButton;
+
+GtkWidget *CddbFollowFile;
+GtkWidget *CddbUseDLM; // Also used in the cddb.c
+
+
+/* Confirmation */
+GtkWidget *ConfirmBeforeExit;
+GtkWidget *ConfirmWriteTag;
+GtkWidget *ConfirmRenameFile;
+GtkWidget *ConfirmDeleteFile;
+GtkWidget *ConfirmWritePlayList;
+
+
+
+/**************
+ * Prototypes *
+ **************/
+
+void Init_OptionsWindow (void);
+void Open_OptionsWindow (void);
+void OptionsWindow_Apply_Changes (void);
+
+void File_Selection_Window_For_File (GtkWidget *widget);
+void File_Selection_Window_For_Directory (GtkWidget *widget);
+
+
+#endif /* __PREFS_H__ */
diff --git a/src/scan.c b/src/scan.c
new file mode 100755
index 0000000..2f7be4a
--- /dev/null
+++ b/src/scan.c
@@ -0,0 +1,3455 @@
+/* scan.c - 2000/06/16 */
+/*
+ * EasyTAG - Tag editor for MP3 and Ogg Vorbis files
+ * Copyright (C) 2000-2003 Jerome Couderc <easytag@gmail.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+
+#include <gtk/gtk.h>
+#include <ctype.h>
+#include <string.h>
+#include <stdlib.h>
+#include <gdk/gdkkeysyms.h>
+#include <config.h>
+#include <glib/gi18n-lib.h>
+
+#include "scan.h"
+#include "easytag.h"
+#include "prefs.h"
+#include "setting.h"
+#include "id3_tag.h"
+#include "bar.h"
+#include "browser.h"
+#include "log.h"
+#include "misc.h"
+#include "et_core.h"
+#include "crc32.h"
+#include "msgbox.h"
+#include "charset.h"
+
+
+#define step(a,b) (b-a)+1
+
+
+/****************
+ * Declarations *
+ ****************/
+GtkWidget *DummyEntry = NULL; /* Used to simulate a gtkentry widget for mask code '%i' */
+GtkWidget *ScanTagMaskCombo = NULL;
+GtkWidget *RenameFileMaskCombo = NULL;
+GtkWidget *ScannerOptionCombo = NULL;
+GtkWidget *RenameFilePrefixPathButton = NULL;
+
+GtkWidget *ScanTagFrame;
+GtkWidget *RenameFileFrame;
+GtkWidget *ProcessFieldsFrame;
+GtkWidget *FillTagPreviewLabel = NULL;
+GtkWidget *RenameFilePreviewLabel = NULL;
+
+GtkListStore *RenameFileListModel;
+GtkListStore *ScanTagListModel;
+
+GtkWidget *ProcessFileNameField;
+GtkWidget *ProcessTitleField;
+GtkWidget *ProcessArtistField;
+GtkWidget *ProcessAlbumField;
+GtkWidget *ProcessGenreField;
+GtkWidget *ProcessCommentField;
+GtkWidget *ProcessComposerField;
+GtkWidget *ProcessOrigArtistField;
+GtkWidget *ProcessCopyrightField;
+GtkWidget *ProcessURLField;
+GtkWidget *ProcessEncodedByField;
+GtkWidget *ProcessFieldsConvertIntoSpace = NULL;
+GtkWidget *ProcessFieldsConvertSpace = NULL;
+GtkWidget *ProcessFieldsConvert = NULL;
+GtkWidget *ProcessFieldsConvertLabelTo;
+GtkWidget *ProcessFieldsConvertTo = NULL;
+GtkWidget *ProcessFieldsConvertFrom = NULL;
+GtkWidget *ProcessFieldsAllUppercase = NULL;
+GtkWidget *ProcessFieldsAllDowncase = NULL;
+GtkWidget *ProcessFieldsFirstLetterUppercase = NULL;
+GtkWidget *ProcessFieldsFirstLettersUppercase = NULL;
+GtkWidget *ProcessFieldsRemoveSpace = NULL;
+GtkWidget *ProcessFieldsInsertSpace = NULL;
+GtkWidget *ProcessFieldsOnlyOneSpace = NULL;
+
+GtkWidget *LegendFrame = NULL;
+GtkWidget *LegendButton = NULL;
+
+GtkWidget *MaskEditorButton = NULL;
+GtkWidget *MaskEditorFrame = NULL;
+GtkWidget *MaskEditorVBox;
+GtkWidget *MaskEditorHBox;
+GtkWidget *MaskEditorScrollWindow;
+GtkWidget *MaskEditorList;
+GtkWidget *MaskEditorEntry;
+GtkWidget *MaskEditorNewButton;
+GtkWidget *MaskEditorCopyButton;
+GtkWidget *MaskEditorAddButton;
+GtkWidget *MaskEditorRemoveButton;
+GtkWidget *MaskEditorUpButton;
+GtkWidget *MaskEditorDownButton;
+GtkWidget *MaskEditorSaveButton;
+
+/* Some predefined masks -- IMPORTANT: Null-terminate me! */
+gchar *Scan_Masks [] =
+{
+ "%a - %b"G_DIR_SEPARATOR_S"%n - %t",
+ "%a_-_%b"G_DIR_SEPARATOR_S"%n_-_%t",
+ "%a - %b (%y)"G_DIR_SEPARATOR_S"%n - %a - %t",
+ "%a_-_%b_(%y)"G_DIR_SEPARATOR_S"%n_-_%a_-_%t",
+ "%a - %b (%y) - %g"G_DIR_SEPARATOR_S"%n - %a - %t",
+ "%a_-_%b_(%y)_-_%g"G_DIR_SEPARATOR_S"%n_-_%a_-_%t",
+ "%a - %b"G_DIR_SEPARATOR_S"%n. %t",
+ "%a_-_%b"G_DIR_SEPARATOR_S"%n._%t",
+ "%a-%b"G_DIR_SEPARATOR_S"%n-%t",
+ "%b"G_DIR_SEPARATOR_S"%n. %a - %t",
+ "%b"G_DIR_SEPARATOR_S"%n._%a_-_%t",
+ "%b"G_DIR_SEPARATOR_S"%n - %a - %t",
+ "%b"G_DIR_SEPARATOR_S"%n_-_%a_-_%t",
+ "%b"G_DIR_SEPARATOR_S"%n-%a-%t",
+ "%a-%b"G_DIR_SEPARATOR_S"%n-%t",
+ "%a"G_DIR_SEPARATOR_S"%b"G_DIR_SEPARATOR_S"%n. %t",
+ "%g"G_DIR_SEPARATOR_S"%a"G_DIR_SEPARATOR_S"%b"G_DIR_SEPARATOR_S"%t",
+ "%a_-_%b-%n-%t-%y",
+ "%a - %b"G_DIR_SEPARATOR_S"%n. %t(%c)",
+ "%t",
+ "Track%n",
+ "Track%i %n",
+ NULL
+};
+
+gchar *Rename_File_Masks [] =
+{
+ "%n - %a - %t",
+ "%n_-_%a_-_%t",
+ "%n. %a - %t",
+ "%n._%a_-_%t",
+ "%n - %t",
+ "%n_-_%t",
+ "%n. %t",
+ "%n._%t",
+ "%n - %a - %b - %t",
+ "%n_-_%a_-_%b_-_%t",
+ "%a - %b - %t",
+ "%a_-_%b_-_%t",
+ "%a - %b - %n - %t",
+ "%a_-_%b_-_%n_-_%t",
+ "%a - %t",
+ "%a_-_%t",
+ "Track %n",
+ NULL
+};
+
+/**gchar *Rename_Directory_Masks [] =
+{
+ "%a - %b",
+ "%a_-_%b",
+ "%a - %b (%y) - %g",
+ "%a_-_%b_(%y)_-_%g",
+ "VA - %b (%y)",
+ "VA_-_%b_(%y)",
+ NULL
+};**/
+
+
+gchar *Scanner_Option_Menu_Items [] =
+{
+ N_("Fill Tag"),
+ N_("Rename File and Directory"),
+ N_("Process Fields")
+};
+
+typedef enum
+{
+ UNKNOWN = 0, /* Default value when initialized */
+ LEADING_SEPARATOR, /* characters before the first code */
+ TRAILING_SEPARATOR, /* characters after the last code */
+ SEPARATOR, /* item is a separator between two codes */
+ DIRECTORY_SEPARATOR, /* item is a separator between two codes with character '/' (G_DIR_SEPARATOR) */
+ FIELD, /* item contains text (not empty) of entry */
+ EMPTY_FIELD /* item when entry contains no text */
+} Mask_Item_Type;
+
+
+
+/*
+ * Used into Rename File Scanner
+ */
+typedef struct _File_Mask_Item File_Mask_Item;
+struct _File_Mask_Item
+{
+ Mask_Item_Type type;
+ gchar *string;
+};
+
+/*
+ * Used into Scan Tag Scanner
+ */
+typedef struct _Scan_Mask_Item Scan_Mask_Item;
+struct _Scan_Mask_Item
+{
+ gchar code; // The code of the mask without % (ex: %a => a)
+ gchar *string; // The string found by the scanner for the code defined the line above
+};
+
+
+
+/**************
+ * Prototypes *
+ **************/
+void ScannerWindow_Quit (void);
+gboolean ScannerWindow_Key_Press (GtkWidget *window, GdkEvent *event);
+void Scan_Toggle_Legend_Button (void);
+void Scan_Toggle_Mask_Editor_Button (void);
+gchar *Scan_Replace_String (gchar *string, gchar *last, gchar *new);
+void Scan_Option_Button (void);
+gboolean Scan_Check_Scan_Tag_Mask (GtkObject *widget_to_show_hide, GtkEntry *widget_source);
+gboolean Scan_Check_Rename_File_Mask (GtkObject *widget_to_show_hide, GtkEntry *widget_source);
+gboolean Scan_Check_Editor_Mask (GtkObject *widget_to_show_hide, GtkEntry *widget_source);
+
+gchar *Scan_Generate_New_Filename_From_Mask (ET_File *ETFile, gchar *mask, gboolean no_dir_check_or_conversion);
+GList *Scan_Generate_New_Tag_From_Mask (ET_File *ETFile, gchar *mask);
+void Scan_Rename_File_Generate_Preview (void);
+void Scan_Rename_File_Prefix_Path (void);
+void Scan_Fill_Tag_Generate_Preview (void);
+void Scan_Free_File_Rename_List (GList *list);
+void Scan_Free_File_Fill_Tag_List (GList *list);
+void Scan_Rename_Directory_Generate_Preview (void);
+
+gchar **Scan_Return_File_Tag_Field_From_Mask_Code (File_Tag *FileTag, gchar code);
+void Scan_Process_Fields_Functions (gchar *string);
+
+void Process_Fields_Check_Button_Toggled (GtkObject *object, GList *list);
+void Process_Fields_Convert_Check_Button_Toggled (GtkObject *object);
+void Select_Fields_Invert_Selection (void);
+void Select_Fields_Select_Unselect_All (void);
+void Select_Fields_Set_Sensitive (void);
+
+void Mask_Editor_List_Row_Selected (GtkTreeSelection* selection, gpointer data);
+void Mask_Editor_List_New (void);
+void Mask_Editor_List_Duplicate (void);
+void Mask_Editor_List_Add (void);
+void Mask_Editor_List_Remove (void);
+void Mask_Editor_List_Move_Up (void);
+void Mask_Editor_List_Move_Down (void);
+void Mask_Editor_List_Save_Button (void);
+void Mask_Editor_Entry_Changed (void);
+gboolean Mask_Editor_List_Key_Press (GtkWidget *widget, GdkEvent *event);
+
+void Mask_Editor_Clean_Up_Masks_List (void);
+
+void Scanner_Option_Menu_Activate_Item (GtkWidget *widget, gpointer data);
+
+void Populate_Scan_Tag_Masks();
+void Populate_Rename_File_Masks();
+
+
+/*************
+ * *
+ * Functions *
+ * *
+ *************/
+
+void Init_ScannerWindow (void)
+{
+ ScannerWindow = (GtkWidget *)NULL;
+ ScannerOptionCombo= (GtkWidget *)NULL;
+ SWScanButton = (GtkWidget *)NULL;
+}
+
+
+/*
+ * Uses the filename and path to fill tag informations
+ * Note: mask and source are read from the right to the left
+ */
+void Scan_Tag_With_Mask (ET_File *ETFile)
+{
+ GList *fill_tag_list = NULL;
+ gchar **dest = NULL;
+ gchar *mask; // The 'mask' in the entry
+ gchar *filename_utf8;
+ File_Tag *FileTag;
+
+ if (!ScannerWindow || !ScanTagMaskCombo || !ETFile) return;
+ mask = g_strdup(gtk_entry_get_text(GTK_ENTRY(GTK_BIN(ScanTagMaskCombo)->child)));
+ if (!mask) return;
+
+ // Create a new File_Tag item
+ FileTag = ET_File_Tag_Item_New();
+ ET_Copy_File_Tag_Item(ETFile,FileTag);
+
+ // Process this mask with file
+ fill_tag_list = Scan_Generate_New_Tag_From_Mask(ETFile,mask);
+ while (fill_tag_list)
+ {
+ Scan_Mask_Item *mask_item = fill_tag_list->data;
+
+ // Get the target entry for this code
+ dest = Scan_Return_File_Tag_Field_From_Mask_Code(FileTag,mask_item->code);
+
+ // We display the text affected to the code
+ if ( dest && ( OVERWRITE_TAG_FIELD || *dest==NULL || strlen(*dest)==0 ) )
+ ET_Set_Field_File_Tag_Item(dest,mask_item->string);
+
+ if (!fill_tag_list->next) break;
+ fill_tag_list = fill_tag_list->next;
+ }
+ Scan_Free_File_Fill_Tag_List(fill_tag_list);
+
+ // Set the default text to comment
+ if (SET_DEFAULT_COMMENT && (OVERWRITE_TAG_FIELD || FileTag->comment==NULL || strlen(FileTag->comment)==0 ) )
+ ET_Set_Field_File_Tag_Item((void *)&FileTag->comment,DEFAULT_COMMENT);
+
+ // Set CRC-32 value as default comment (for files with ID3 tag only ;-)
+ if (SET_CRC32_COMMENT && (OVERWRITE_TAG_FIELD || FileTag->comment==NULL || strlen(FileTag->comment)==0 ) )
+ {
+ gulong crc32_value = 0;
+ gchar *buffer;
+ ET_File_Description *ETFileDescription;
+
+ ETFileDescription = ETFile->ETFileDescription;
+ switch (ETFileDescription->TagType)
+ {
+ case ID3_TAG:
+ crc32_file_with_ID3_tag( ((File_Name *)((GList *)ETFile->FileNameNew)->data)->value, &crc32_value);
+
+ if (crc32_value > 0)
+ {
+ buffer = g_strdup_printf("%.8lx",crc32_value);
+ ET_Set_Field_File_Tag_Item((void *)&FileTag->comment,buffer);
+ g_free(buffer);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+
+ // Save changes of the 'File_Tag' item
+ ET_Manage_Changes_Of_File_Data(ETFile,NULL,FileTag);
+
+ g_free(mask);
+ Statusbar_Message(_("Tag successfully scanned..."),TRUE);
+ filename_utf8 = g_path_get_basename( ((File_Name *)ETFile->FileNameNew->data)->value_utf8 );
+ Log_Print(_("Tag successfully scanned...(%s)"),filename_utf8);
+ g_free(filename_utf8);
+}
+
+GList *Scan_Generate_New_Tag_From_Mask (ET_File *ETFile, gchar *mask)
+{
+ GList *fill_tag_list = NULL;
+ gchar *filename_utf8;
+ gchar *tmp;
+ gchar *buf;
+ gchar *separator;
+ gchar *string;
+ gint len, i, loop=0;
+ gchar **mask_splitted;
+ gchar **file_splitted;
+ guint mask_splitted_number;
+ guint file_splitted_number;
+ guint mask_splitted_index;
+ guint file_splitted_index;
+ Scan_Mask_Item *mask_item;
+
+ if (!ETFile || !mask) return NULL;
+
+ filename_utf8 = g_strdup(((File_Name *)((GList *)ETFile->FileNameNew)->data)->value_utf8);
+ if (!filename_utf8) return NULL;
+
+ // Remove extension of file (if found)
+ tmp = strrchr(filename_utf8,'.');
+ for (i=0; i<=(gint)ET_FILE_DESCRIPTION_SIZE; i++)
+ {
+ if ( strcasecmp(tmp,ETFileDescription[i].Extension)==0 )
+ {
+ *tmp = 0; //strrchr(source,'.') = 0;
+ break;
+ }
+ }
+
+ if (i==ET_FILE_DESCRIPTION_SIZE)
+ {
+ gchar *tmp1 = g_path_get_basename(filename_utf8);
+ Log_Print(_("Tag scanner: strange..., the extension '%s' was not found into filename '%s'!"),tmp,tmp1);
+ g_free(tmp1);
+ }
+
+ // Replace characters into mask and filename before parsing
+ if (FTS_CONVERT_UNDERSCORE_AND_P20_INTO_SPACE)
+ {
+ Scan_Convert_Underscore_Into_Space(mask);
+ Scan_Convert_Underscore_Into_Space(filename_utf8);
+ Scan_Convert_P20_Into_Space(mask);
+ Scan_Convert_P20_Into_Space(filename_utf8);
+ }
+ if (FTS_CONVERT_SPACE_INTO_UNDERSCORE)
+ {
+ Scan_Convert_Space_Into_Undescore(mask);
+ Scan_Convert_Space_Into_Undescore(filename_utf8);
+ }
+
+
+ // Split the Scanner mask
+ mask_splitted = g_strsplit(mask,G_DIR_SEPARATOR_S,0);
+ // Get number of arguments into 'mask_splitted'
+ for (mask_splitted_number=0;mask_splitted[mask_splitted_number];mask_splitted_number++);
+
+ // Split the File Path
+ file_splitted = g_strsplit(filename_utf8,G_DIR_SEPARATOR_S,0);
+ // Get number of arguments into 'file_splitted'
+ for (file_splitted_number=0;file_splitted[file_splitted_number];file_splitted_number++);
+
+ // Set the starting position for each tab
+ if (mask_splitted_number <= file_splitted_number)
+ {
+ mask_splitted_index = 0;
+ file_splitted_index = file_splitted_number - mask_splitted_number;
+ }else
+ {
+ mask_splitted_index = mask_splitted_number - file_splitted_number;
+ file_splitted_index = 0;
+ }
+
+ loop = 0;
+ while ( mask_splitted[mask_splitted_index]!= NULL && file_splitted[file_splitted_index]!=NULL )
+ {
+ gchar *mask_seq = mask_splitted[mask_splitted_index];
+ gchar *file_seq = file_splitted[file_splitted_index];
+ gchar *file_seq_utf8 = filename_to_display(file_seq);
+
+ //g_print(">%d> seq '%s' '%s'\n",loop,mask_seq,file_seq);
+ while ( mask_seq && strlen(mask_seq)>0 )
+ {
+
+ /*
+ * Determine (first) code and destination
+ */
+ if ( (tmp=strchr(mask_seq,'%')) == NULL || strlen(tmp) < 2 )
+ {
+ break;
+ }
+
+ /*
+ * Allocate a new iten for the fill_tag_list
+ */
+ mask_item = g_malloc0(sizeof(Scan_Mask_Item));
+
+ // Get the code (used to determine the corresponding target entry)
+ mask_item->code = tmp[1];
+
+ /*
+ * Delete text before the code
+ */
+ if ( (len = strlen(mask_seq) - strlen(tmp)) > 0 )
+ {
+ // Get this text in 'mask_seq'
+ buf = g_strndup(mask_seq,len);
+ // We remove it in 'mask_seq'
+ mask_seq = mask_seq + len;
+ // Find the same text at the begining of 'file_seq' ?
+ if ( (strstr(file_seq,buf)) == file_seq )
+ {
+ file_seq = file_seq + len; // We remove it
+ }else
+ {
+ Log_Print(_("Scan Error: can't find separator '%s' within '%s'"),buf,file_seq_utf8);
+ }
+ g_free(buf);
+ }
+
+ // Remove the current code into 'mask_seq'
+ mask_seq = mask_seq + 2;
+
+ /*
+ * Determine separator between two code or trailing text (after code)
+ */
+ if ( mask_seq && strlen(mask_seq)>0 )
+ {
+ if ( (tmp=strchr(mask_seq,'%')) == NULL || strlen(tmp) < 2 )
+ {
+ // No more code found
+ len = strlen(mask_seq);
+ }else
+ {
+ len = strlen(mask_seq) - strlen(tmp);
+ }
+ separator = g_strndup(mask_seq,len);
+
+ // Remove the current separator in 'mask_seq'
+ mask_seq = mask_seq + len;
+
+ // Try to find the separator in 'file_seq'
+ if ( (tmp=strstr(file_seq,separator)) == NULL )
+ {
+ Log_Print(_("Scan Error: can't find separator '%s' within '%s'"),separator,file_seq_utf8);
+ separator[0] = 0; // Needed to avoid error when calculting 'len' below
+ }
+
+ // Get the string affected to the code (or the corresponding entry field)
+ len = strlen(file_seq) - (tmp!=NULL?strlen(tmp):0);
+ string = g_strndup(file_seq,len);
+
+ // Remove the current separator in 'file_seq'
+ file_seq = file_seq + strlen(string) + strlen(separator);
+ g_free(separator);
+
+ // We get the text affected to the code
+ mask_item->string = string;
+ }else
+ {
+ // We display the remaining text, affected to the code (no more data in 'mask_seq')
+ mask_item->string = g_strdup(file_seq);
+ }
+
+ // Add the filled mask_iten to the list
+ fill_tag_list = g_list_append(fill_tag_list,mask_item);
+ }
+
+ g_free(file_seq_utf8);
+
+ // Next sequences
+ mask_splitted_index++;
+ file_splitted_index++;
+ loop++;
+ }
+
+ g_free(filename_utf8);
+ g_strfreev(mask_splitted);
+ g_strfreev(file_splitted);
+
+ // The 'fill_tag_list' must be freed after use
+ return fill_tag_list;
+}
+
+void Scan_Fill_Tag_Generate_Preview (void)
+{
+ gchar *mask = NULL;
+ gchar *preview_text = NULL;
+ GList *fill_tag_list = NULL;
+
+ if (!ETCore->ETFileDisplayedList
+ || !ScannerWindow || !RenameFileMaskCombo || !FillTagPreviewLabel
+ || gtk_combo_box_get_active(GTK_COMBO_BOX(ScannerOptionCombo)) != SCANNER_FILL_TAG)
+ return;
+
+ mask = g_strdup(gtk_entry_get_text(GTK_ENTRY(GTK_BIN(ScanTagMaskCombo)->child)));
+ if (!mask)
+ return;
+
+ preview_text = g_strdup("");
+ fill_tag_list = Scan_Generate_New_Tag_From_Mask(ETCore->ETFileDisplayed,mask);
+ while (fill_tag_list)
+ {
+ Scan_Mask_Item *mask_item = fill_tag_list->data;
+ gchar *tmp_code = g_strdup_printf("%c",mask_item->code);
+ gchar *tmp_string = g_markup_printf_escaped("%s",mask_item->string); // To avoid problem with strings containing characters like '&'
+ gchar *tmp_preview_text = preview_text;
+
+ preview_text = g_strconcat(tmp_preview_text,"<b>","%",tmp_code," = ",
+ "</b>","<i>",tmp_string,"</i>",NULL);
+ g_free(tmp_code);
+ g_free(tmp_string);
+ g_free(tmp_preview_text);
+
+ if (!fill_tag_list->next) break;
+ fill_tag_list = fill_tag_list->next;
+
+ tmp_preview_text = preview_text;
+ preview_text = g_strconcat(tmp_preview_text," || ",NULL);
+ g_free(tmp_preview_text);
+ }
+ Scan_Free_File_Fill_Tag_List(fill_tag_list);
+
+ if (GTK_IS_LABEL(FillTagPreviewLabel))
+ {
+ if (preview_text)
+ {
+ //gtk_label_set_text(GTK_LABEL(FillTagPreviewLabel),preview_text);
+ gtk_label_set_markup(GTK_LABEL(FillTagPreviewLabel),preview_text);
+ } else
+ {
+ gtk_label_set_text(GTK_LABEL(FillTagPreviewLabel),"");
+ }
+
+ // Force the window to be redrawed
+ gtk_widget_queue_resize(ScannerWindow);
+ }
+
+ g_free(mask);
+ g_free(preview_text);
+}
+
+void Scan_Free_File_Fill_Tag_List (GList *list)
+{
+ // Free the list
+ list = g_list_first(list);
+ while (list)
+ {
+ if (list->data)
+ {
+ g_free(((Scan_Mask_Item *)list->data)->string);
+ g_free( (Scan_Mask_Item *)list->data );
+ }
+ if (!list->next) break;
+ list = list->next;
+ }
+ g_list_free(list);
+ list = (GList *)NULL;
+}
+
+
+
+/**************************
+ * Scanner To Rename File *
+ **************************/
+/*
+ * Uses tag informations (displayed into tag entries) to rename file
+ * Note: mask and source are read from the right to the left.
+ * Note1: a mask code may be used severals times...
+ */
+void Scan_Rename_File_With_Mask (ET_File *ETFile)
+{
+ gchar *filename_generated_utf8 = NULL;
+ gchar *filename_generated = NULL;
+ gchar *filename_new_utf8 = NULL;
+ gchar *mask = NULL;
+ File_Name *FileName;
+
+ if (!ScannerWindow || !RenameFileMaskCombo || !ETFile) return;
+
+ mask = g_strdup(gtk_entry_get_text(GTK_ENTRY(GTK_BIN(RenameFileMaskCombo)->child)));
+ if (!mask) return;
+
+ // Note : if the first character is '/', we have a path with the filename,
+ // else we have only the filename. The both are in UTF-8.
+ filename_generated_utf8 = Scan_Generate_New_Filename_From_Mask(ETFile,mask,FALSE);
+ g_free(mask);
+
+ if (!filename_generated_utf8)
+ return;
+ if (g_utf8_strlen(filename_generated_utf8,-1)<1)
+ {
+ g_free(filename_generated_utf8);
+ return;
+ }
+
+ // Convert filename to file-system encoding
+ filename_generated = filename_from_display(filename_generated_utf8);
+ if (!filename_generated)
+ {
+ GtkWidget *msgbox;
+ gchar *msg = g_strdup_printf(_("Could not convert filename '%s' into system filename encoding."), filename_generated_utf8);
+ msgbox = msg_box_new(_("Filename translation"),msg,GTK_STOCK_DIALOG_ERROR,BUTTON_OK,0);
+ msg_box_run(MSG_BOX(msgbox));
+ gtk_widget_destroy(msgbox);
+ g_free(msg);
+ g_free(filename_generated_utf8);
+ return;
+ }
+
+ /* Build the filename with the full path or relative to old path */
+ filename_new_utf8 = ET_File_Name_Generate(ETFile,filename_generated_utf8);
+ g_free(filename_generated);
+ g_free(filename_generated_utf8);
+
+ /* Set the new filename */
+ // Create a new 'File_Name' item
+ FileName = ET_File_Name_Item_New();
+ // Save changes of the 'File_Name' item
+ ET_Set_Filename_File_Name_Item(FileName,filename_new_utf8,NULL);
+
+ ET_Manage_Changes_Of_File_Data(ETFile,FileName,NULL);
+ g_free(filename_new_utf8);
+
+ Statusbar_Message(_("New file name successfully scanned..."),TRUE);
+
+ filename_new_utf8 = g_path_get_basename(((File_Name *)ETFile->FileNameNew->data)->value_utf8);
+ Log_Print(_("New file name successfully scanned...(%s)"),filename_new_utf8);
+ g_free(filename_new_utf8);
+
+ return;
+}
+
+/*
+ * Build the new filename using tag + mask
+ * Used also to rename the directory (from the browser)
+ * @param ETFile : the etfile to process
+ * @param mask : the pattern to parse
+ * @param no_dir_check_or_conversion : if FALSE, disable checking of a directory
+ * in the mask, and don't convert "illegal" characters. This is used in the
+ * function "Write_Playlist" for the content of the playlist.
+ * Returns filename in UTF-8
+ */
+gchar *Scan_Generate_New_Filename_From_Mask (ET_File *ETFile, gchar *mask, gboolean no_dir_check_or_conversion)
+{
+ gchar *tmp;
+ gchar **source = NULL;
+ gchar *path_utf8_cur = NULL;
+ gchar *filename_new_utf8 = NULL;
+ gchar *filename_tmp = NULL;
+ GList *rename_file_list = NULL;
+ File_Mask_Item *mask_item;
+ File_Mask_Item *mask_item_prev;
+ File_Mask_Item *mask_item_next;
+ gint counter = 0;
+
+
+ if (!ETFile || !mask) return NULL;
+
+ /*
+ * Check for a directory in the mask
+ */
+ if (!no_dir_check_or_conversion)
+ {
+ if (g_path_is_absolute(mask))
+ {
+ // Absolute directory
+ }else if (strrchr(mask,G_DIR_SEPARATOR)!=NULL) // This is '/' on UNIX machines and '\' under Windows
+ {
+ // Relative path => set beginning of the path
+ path_utf8_cur = g_path_get_dirname( ((File_Name *)ETFile->FileNameCur->data)->value_utf8 );
+ }
+ }
+
+
+ /*
+ * Parse the codes to generate a list (1rst item = 1rst code)
+ */
+ while ( mask!=NULL && (tmp=strrchr(mask,'%'))!=NULL && strlen(tmp)>1 )
+ {
+ // Mask contains some characters after the code ('%b__')
+ if (strlen(tmp)>2)
+ {
+ mask_item = g_malloc0(sizeof(File_Mask_Item));
+ if (counter)
+ {
+ if (strchr(tmp+2,G_DIR_SEPARATOR))
+ mask_item->type = DIRECTORY_SEPARATOR;
+ else
+ mask_item->type = SEPARATOR;
+ } else
+ {
+ mask_item->type = TRAILING_SEPARATOR;
+ }
+ mask_item->string = g_strdup(tmp+2);
+ rename_file_list = g_list_prepend(rename_file_list,mask_item);
+ }
+
+ // Now, parses the code to get the corresponding string (from tag)
+ source = Scan_Return_File_Tag_Field_From_Mask_Code((File_Tag *)ETFile->FileTag->data,tmp[1]);
+ mask_item = g_malloc0(sizeof(File_Mask_Item));
+ if (source && *source && strlen(*source)>0)
+ {
+ mask_item->type = FIELD;
+ mask_item->string = g_strdup(*source);
+
+ // Replace invalid characters for this field
+ // Note : shouldn't be done always as for the content of a playlist, we don't need to replace...
+ if (!no_dir_check_or_conversion)
+ {
+ ET_File_Name_Convert_Character(mask_item->string);
+ }
+
+ // Replace characters (rules) (!! don't convert in directory path_utf8_cur)
+ if (RFS_CONVERT_UNDERSCORE_AND_P20_INTO_SPACE)
+ {
+ Scan_Convert_Underscore_Into_Space(mask_item->string);
+ Scan_Convert_P20_Into_Space(mask_item->string);
+ }
+ if (RFS_CONVERT_SPACE_INTO_UNDERSCORE)
+ Scan_Convert_Space_Into_Undescore(mask_item->string);
+ }else
+ {
+ mask_item->type = EMPTY_FIELD;
+ mask_item->string = NULL;
+ }
+ rename_file_list = g_list_prepend(rename_file_list,mask_item);
+ *tmp = '\0'; // Cut parsed data of mask
+ counter++; // To indicate that we made at least one loop to identifiate 'separator' or 'trailing_separator'
+ }
+
+ // It may have some characters before the last remaining code ('__%a')
+ if (mask!=NULL && strlen(mask)>0)
+ {
+ mask_item = g_malloc0(sizeof(File_Mask_Item));
+ mask_item->type = LEADING_SEPARATOR;
+ mask_item->string = g_strdup(mask);
+ rename_file_list = g_list_prepend(rename_file_list,mask_item);
+ }
+
+ if (!rename_file_list) return NULL;
+
+ /*
+ * For Debugging : display the "rename_file_list" list
+ */
+ /***{
+ GList *list = g_list_first(rename_file_list);
+ gint i = 0;
+ g_print("## rename_file_list - start\n");
+ while (list)
+ {
+ File_Mask_Item *mask_item = (File_Mask_Item *)list->data;
+ Mask_Item_Type type = mask_item->type;
+ gchar *string = mask_item->string;
+
+ //g_print("item %d : \n",i++);
+ //g_print(" - type : '%s'\n",type==UNKNOWN?"UNKNOWN":type==LEADING_SEPARATOR?"LEADING_SEPARATOR":type==TRAILING_SEPARATOR?"TRAILING_SEPARATOR":type==SEPARATOR?"SEPARATOR":type==DIRECTORY_SEPARATOR?"DIRECTORY_SEPARATOR":type==FIELD?"FIELD":type==EMPTY_FIELD?"EMPTY_FIELD":"???");
+ //g_print(" - string : '%s'\n",string);
+ g_print("%d -> %s (%s) | ",i++,type==UNKNOWN?"UNKNOWN":type==LEADING_SEPARATOR?"LEADING_SEPARATOR":type==TRAILING_SEPARATOR?"TRAILING_SEPARATOR":type==SEPARATOR?"SEPARATOR":type==DIRECTORY_SEPARATOR?"DIRECTORY_SEPARATOR":type==FIELD?"FIELD":type==EMPTY_FIELD?"EMPTY_FIELD":"???",string);
+
+ list = list->next;
+ }
+ g_print("\n## rename_file_list - end\n\n");
+ }***/
+
+ /*
+ * Build the new filename with items placed into the list
+ * (read the list from the end to the beginning)
+ */
+ rename_file_list = g_list_last(rename_file_list);
+ filename_new_utf8 = g_strdup("");
+
+ while (rename_file_list)
+ {
+ File_Mask_Item *mask_item = rename_file_list->data;
+
+ if ( mask_item->type==TRAILING_SEPARATOR ) // Trailing characters of mask
+ {
+ // Doesn't write it if previous field is empty
+ if (rename_file_list->prev && ((File_Mask_Item *)rename_file_list->prev->data)->type!=EMPTY_FIELD)
+ {
+ filename_tmp = filename_new_utf8;
+ filename_new_utf8 = g_strconcat(mask_item->string,filename_new_utf8,NULL);
+ g_free(filename_tmp);
+ }
+ }else
+ if ( mask_item->type==EMPTY_FIELD )
+ // We don't concatenate the field value (empty) and the previous
+ // separator (except leading separator) to the filename.
+ // If the empty field is the 'first', we don't concatenate it, and the
+ // next separator too.
+ {
+ if (rename_file_list->prev)
+ {
+ // The empty field isn't the first.
+ // If previous string is a separator, we don't use it, except if the next
+ // string is a FIELD (not empty)
+ mask_item_prev = rename_file_list->prev->data;
+ if ( mask_item_prev->type==SEPARATOR )
+ {
+ if ( !(rename_file_list->next && (mask_item_next=rename_file_list->next->data)
+ && mask_item_next->type==FIELD) )
+ {
+ rename_file_list = rename_file_list->prev;
+ }
+ }
+ }else
+ if (rename_file_list->next && (mask_item_next=rename_file_list->next->data)
+ && mask_item_next->type==SEPARATOR)
+ // We are at the 'beginning' of the mask (so empty field is the first)
+ // and next field is a separator. As the separator may have been already added, we remove it
+ {
+ if ( filename_new_utf8 && mask_item_next->string && (strncmp(filename_new_utf8,mask_item_next->string,strlen(mask_item_next->string))==0) ) // To avoid crash if filename_new_utf8 is 'empty'
+ {
+ filename_tmp = filename_new_utf8;
+ filename_new_utf8 = g_strdup(filename_new_utf8+strlen(mask_item_next->string));
+ g_free(filename_tmp);
+ }
+ }
+
+ }else // SEPARATOR, FIELD, LEADING_SEPARATOR, DIRECTORY_SEPARATOR
+ {
+ filename_tmp = filename_new_utf8;
+ filename_new_utf8 = g_strconcat(mask_item->string,filename_new_utf8,NULL);
+ g_free(filename_tmp);
+ }
+
+ if (!rename_file_list->prev) break;
+ rename_file_list = rename_file_list->prev;
+ }
+
+ // Free the list
+ Scan_Free_File_Rename_List(rename_file_list);
+
+
+ // Add current path if relative path entered
+ if (path_utf8_cur)
+ {
+ filename_tmp = filename_new_utf8; // in UTF-8!
+ filename_new_utf8 = g_strconcat(path_utf8_cur,G_DIR_SEPARATOR_S,filename_new_utf8,NULL);
+ g_free(filename_tmp);
+ g_free(path_utf8_cur);
+ }
+
+ return filename_new_utf8; // in UTF-8!
+}
+
+void Scan_Rename_File_Generate_Preview (void)
+{
+ gchar *preview_text = NULL;
+ gchar *mask = NULL;
+
+ if (!ETCore->ETFileDisplayed
+ || !ScannerWindow || !RenameFileMaskCombo || !RenameFilePreviewLabel)
+ return;
+
+ if (gtk_combo_box_get_active(GTK_COMBO_BOX(ScannerOptionCombo)) != SCANNER_RENAME_FILE)
+ return;
+
+ mask = g_strdup(gtk_entry_get_text(GTK_ENTRY(GTK_BIN(RenameFileMaskCombo)->child)));
+ if (!mask)
+ return;
+
+ preview_text = Scan_Generate_New_Filename_From_Mask(ETCore->ETFileDisplayed,mask,FALSE);
+
+ if (GTK_IS_LABEL(RenameFilePreviewLabel))
+ {
+ if (preview_text)
+ {
+ //gtk_label_set_text(GTK_LABEL(RenameFilePreviewLabel),preview_text);
+ gchar *tmp_string = g_markup_printf_escaped("%s",preview_text); // To avoid problem with strings containing characters like '&'
+ gchar *str = g_strdup_printf("<i>%s</i>",tmp_string);
+ gtk_label_set_markup(GTK_LABEL(RenameFilePreviewLabel),str);
+ g_free(tmp_string);
+ g_free(str);
+ } else
+ {
+ gtk_label_set_text(GTK_LABEL(RenameFilePreviewLabel),"");
+ }
+
+ // Force the window to be redrawed
+ gtk_widget_queue_resize(ScannerWindow);
+ }
+
+ g_free(mask);
+ g_free(preview_text);
+}
+
+
+void Scan_Free_File_Rename_List (GList *list)
+{
+ // Free the list
+ list = g_list_last(list);
+ while (list)
+ {
+ if (list->data)
+ {
+ g_free(((File_Mask_Item *)list->data)->string);
+ g_free( (File_Mask_Item *)list->data );
+ }
+ if (!list->prev) break;
+ list = list->prev;
+ }
+ g_list_free(list);
+ list = (GList *)NULL;
+}
+
+/*
+ * Adds the current path of the file to the mask on the "Rename File Scanner" entry
+ */
+void Scan_Rename_File_Prefix_Path (void)
+{
+ gchar *path_tmp;
+ gchar *combo_text = NULL;
+ gchar *combo_tmp;
+ ET_File *ETFile = ETCore->ETFileDisplayed;
+ gchar *filename_utf8_cur = ((File_Name *)ETFile->FileNameCur->data)->value_utf8;
+ gchar *path_utf8_cur;
+
+
+ // The path to prefix
+ path_utf8_cur = g_path_get_dirname(filename_utf8_cur);
+
+ // The current text in the combobox
+ combo_text = (gchar *)gtk_entry_get_text(GTK_ENTRY(GTK_BIN(RenameFileMaskCombo)->child));
+ if (!g_utf8_validate(combo_text, -1, NULL))
+ {
+ combo_tmp = convert_to_utf8(combo_text);
+ }else
+ {
+ combo_tmp = g_strdup(combo_text);
+ }
+
+ // If the path already exists we don't add it again
+ // Use g_utf8_collate_key instead of strncmp
+ if (combo_tmp && path_utf8_cur && strncmp(combo_tmp,path_utf8_cur,strlen(path_utf8_cur))!=0)
+ {
+ if (g_path_is_absolute(combo_tmp))
+ {
+ path_tmp = g_strdup(path_utf8_cur);
+ } else
+ {
+ path_tmp = g_strconcat(path_utf8_cur,G_DIR_SEPARATOR_S,NULL);
+ }
+ gtk_entry_prepend_text(GTK_ENTRY(GTK_BIN(RenameFileMaskCombo)->child),path_tmp);
+ g_free(path_tmp);
+ }
+
+ g_free(path_utf8_cur);
+}
+
+
+/*******************************
+ * Scanner To Rename Directory *
+ *******************************/
+void Scan_Rename_Directory_Generate_Preview (void)
+{
+ gchar *preview_text = NULL;
+ gchar *mask = NULL;
+
+ if (!ETCore->ETFileDisplayed
+ || !RenameDirectoryWindow || !RenameDirectoryMaskCombo || !RenameDirectoryPreviewLabel)
+ return;
+
+ mask = g_strdup(gtk_entry_get_text(GTK_ENTRY(GTK_BIN(RenameDirectoryMaskCombo)->child)));
+ if (!mask)
+ return;
+
+ preview_text = Scan_Generate_New_Filename_From_Mask(ETCore->ETFileDisplayed,mask,FALSE);
+
+ if (GTK_IS_LABEL(RenameDirectoryPreviewLabel))
+ {
+ if (preview_text)
+ {
+ //gtk_label_set_text(GTK_LABEL(RenameFilePreviewLabel),preview_text);
+ gchar *tmp_string = g_markup_printf_escaped("%s",preview_text); // To avoid problem with strings containing characters like '&'
+ gchar *str = g_strdup_printf("<i>%s</i>",tmp_string);
+ gtk_label_set_markup(GTK_LABEL(RenameDirectoryPreviewLabel),str);
+ g_free(tmp_string);
+ g_free(str);
+ } else
+ {
+ gtk_label_set_text(GTK_LABEL(RenameDirectoryPreviewLabel),"");
+ }
+
+ // Force the window to be redrawed else the preview label may be not placed correctly
+ gtk_widget_queue_resize(RenameDirectoryWindow);
+ }
+
+ g_free(mask);
+ g_free(preview_text);
+}
+
+gchar *Scan_Generate_New_Directory_Name_From_Mask (ET_File *ETFile, gchar *mask, gboolean no_dir_check_or_conversion)
+{
+ return Scan_Generate_New_Filename_From_Mask(ETFile,mask,no_dir_check_or_conversion);
+}
+
+
+
+/*****************************
+ * Scanner To Process Fields *
+ *****************************/
+/* See also functions : Convert_P20_And_Undescore_Into_Spaces, ... in easytag.c */
+void Scan_Process_Fields (ET_File *ETFile)
+{
+ File_Name *FileName = NULL;
+ File_Tag *FileTag = NULL;
+ File_Name *st_filename;
+ File_Tag *st_filetag;
+ gchar *filename_utf8;
+ //GString *string2process;
+ guint string_length;
+ gchar *string;
+ gchar *temp;
+
+
+ if (!ScannerWindow || !ETFile) return;
+
+ st_filename = (File_Name *)ETFile->FileNameNew->data;
+ st_filetag = (File_Tag *)ETFile->FileTag->data;
+ //string2process = g_string_sized_new(512);
+
+ /* Process the filename */
+ if (st_filename != NULL)
+ {
+ if (st_filename->value_utf8 && GTK_TOGGLE_BUTTON(ProcessFileNameField)->active) // File name field
+ {
+ gchar *string_utf8;
+ gchar *pos;
+
+ filename_utf8 = st_filename->value_utf8;
+
+ if (!FileName)
+ FileName = ET_File_Name_Item_New();
+
+ // FIX ME : we suppose that it will not grow more than 2 times its size...
+ temp = g_path_get_basename(filename_utf8);
+ string_length = 2 * g_utf8_strlen(temp,-1);
+ string = g_malloc(string_length+1);
+ strncpy(string,temp,string_length);
+ g_free(temp);
+ string[string_length]='\0';
+ // Remove the extension to set it to lower case (to avoid problem with undo)
+ if ((pos=strrchr(string,'.'))!=NULL) *pos = 0;
+
+ Scan_Process_Fields_Functions(string);
+
+ string_utf8 = ET_File_Name_Generate(ETFile,string);
+ ET_Set_Filename_File_Name_Item(FileName,string_utf8,NULL);
+ g_free(string_utf8);
+ g_free(string);
+ }
+ }
+
+ /* Process data of the tag */
+ if (st_filetag != NULL)
+ {
+ // Title field
+ if (st_filetag->title && GTK_TOGGLE_BUTTON(ProcessTitleField)->active)
+ {
+ if (!FileTag)
+ {
+ FileTag = ET_File_Tag_Item_New();
+ ET_Copy_File_Tag_Item(ETFile,FileTag);
+ }
+
+ // FIX ME : we suppose that it will not grow more than 2 times its size...
+ string_length = 2 * strlen(st_filetag->title);
+ string = g_malloc(string_length+1);
+ strncpy(string,st_filetag->title,string_length);
+ string[string_length]='\0';
+
+ Scan_Process_Fields_Functions(string);
+
+ ET_Set_Field_File_Tag_Item(&FileTag->title,string);
+
+ g_free(string);
+ }
+
+ // Artist field
+ if (st_filetag->artist && GTK_TOGGLE_BUTTON(ProcessArtistField)->active)
+ {
+ if (!FileTag)
+ {
+ FileTag = ET_File_Tag_Item_New();
+ ET_Copy_File_Tag_Item(ETFile,FileTag);
+ }
+
+ // FIX ME : we suppose that it will not grow more than 2 times its size...
+ string_length = 2 * strlen(st_filetag->artist);
+ string = g_malloc(string_length+1);
+ strncpy(string,st_filetag->artist,string_length);
+ string[string_length]='\0';
+
+ Scan_Process_Fields_Functions(string);
+
+ ET_Set_Field_File_Tag_Item(&FileTag->artist,string);
+
+ g_free(string);
+ }
+
+ // Album field
+ if (st_filetag->album && GTK_TOGGLE_BUTTON(ProcessAlbumField)->active)
+ {
+ if (!FileTag)
+ {
+ FileTag = ET_File_Tag_Item_New();
+ ET_Copy_File_Tag_Item(ETFile,FileTag);
+ }
+
+ // FIX ME : we suppose that it will not grow more than 2 times its size...
+ string_length = 2 * strlen(st_filetag->album);
+ string = g_malloc(string_length+1);
+ strncpy(string,st_filetag->album,string_length);
+ string[string_length]='\0';
+
+ Scan_Process_Fields_Functions(string);
+
+ ET_Set_Field_File_Tag_Item(&FileTag->album,string);
+
+ g_free(string);
+ }
+
+ // Genre field
+ if (st_filetag->genre && GTK_TOGGLE_BUTTON(ProcessGenreField)->active)
+ {
+ if (!FileTag)
+ {
+ FileTag = ET_File_Tag_Item_New();
+ ET_Copy_File_Tag_Item(ETFile,FileTag);
+ }
+
+ // FIX ME : we suppose that it will not grow more than 2 times its size...
+ string_length = 2 * strlen(st_filetag->genre);
+ string = g_malloc(string_length+1);
+ strncpy(string,st_filetag->genre,string_length);
+ string[string_length]='\0';
+
+ Scan_Process_Fields_Functions(string);
+
+ ET_Set_Field_File_Tag_Item(&FileTag->genre,string);
+
+ g_free(string);
+ }
+
+ // Comment field
+ if (st_filetag->comment && GTK_TOGGLE_BUTTON(ProcessCommentField)->active)
+ {
+ if (!FileTag)
+ {
+ FileTag = ET_File_Tag_Item_New();
+ ET_Copy_File_Tag_Item(ETFile,FileTag);
+ }
+
+ // FIX ME : we suppose that it will not grow more than 2 times its size...
+ string_length = 2 * strlen(st_filetag->comment);
+ string = g_malloc(string_length+1);
+ strncpy(string,st_filetag->comment,string_length);
+ string[string_length]='\0';
+
+ Scan_Process_Fields_Functions(string);
+
+ ET_Set_Field_File_Tag_Item(&FileTag->comment,string);
+
+ g_free(string);
+ }
+
+ // Composer field
+ if (st_filetag->composer && GTK_TOGGLE_BUTTON(ProcessComposerField)->active)
+ {
+ if (!FileTag)
+ {
+ FileTag = ET_File_Tag_Item_New();
+ ET_Copy_File_Tag_Item(ETFile,FileTag);
+ }
+
+ // FIX ME : we suppose that it will not grow more than 2 times its size...
+ string_length = 2 * strlen(st_filetag->composer);
+ string = g_malloc(string_length+1);
+ strncpy(string,st_filetag->composer,string_length);
+ string[string_length]='\0';
+
+ Scan_Process_Fields_Functions(string);
+
+ ET_Set_Field_File_Tag_Item(&FileTag->composer,string);
+
+ g_free(string);
+ }
+
+ // Original artist field
+ if (st_filetag->orig_artist && GTK_TOGGLE_BUTTON(ProcessOrigArtistField)->active)
+ {
+ if (!FileTag)
+ {
+ FileTag = ET_File_Tag_Item_New();
+ ET_Copy_File_Tag_Item(ETFile,FileTag);
+ }
+
+ // FIX ME : we suppose that it will not grow more than 2 times its size...
+ string_length = 2 * strlen(st_filetag->orig_artist);
+ string = g_malloc(string_length+1);
+ strncpy(string,st_filetag->orig_artist,string_length);
+ string[string_length]='\0';
+
+ Scan_Process_Fields_Functions(string);
+
+ ET_Set_Field_File_Tag_Item(&FileTag->orig_artist,string);
+
+ g_free(string);
+ }
+
+ // Copyright field
+ if (st_filetag->copyright && GTK_TOGGLE_BUTTON(ProcessCopyrightField)->active)
+ {
+ if (!FileTag)
+ {
+ FileTag = ET_File_Tag_Item_New();
+ ET_Copy_File_Tag_Item(ETFile,FileTag);
+ }
+
+ // FIX ME : we suppose that it will not grow more than 2 times its size...
+ string_length = 2 * strlen(st_filetag->copyright);
+ string = g_malloc(string_length+1);
+ strncpy(string,st_filetag->copyright,string_length);
+ string[string_length]='\0';
+
+ Scan_Process_Fields_Functions(string);
+
+ ET_Set_Field_File_Tag_Item(&FileTag->copyright,string);
+
+ g_free(string);
+ }
+
+ // URL field
+ if (st_filetag->url && GTK_TOGGLE_BUTTON(ProcessURLField)->active)
+ {
+ if (!FileTag)
+ {
+ FileTag = ET_File_Tag_Item_New();
+ ET_Copy_File_Tag_Item(ETFile,FileTag);
+ }
+
+ // FIX ME : we suppose that it will not grow more than 2 times its size...
+ string_length = 2 * strlen(st_filetag->url);
+ string = g_malloc(string_length+1);
+ strncpy(string,st_filetag->url,string_length);
+ string[string_length]='\0';
+
+ Scan_Process_Fields_Functions(string);
+
+ ET_Set_Field_File_Tag_Item(&FileTag->url,string);
+
+ g_free(string);
+ }
+
+ // 'Encoded by' field
+ if (st_filetag->encoded_by && GTK_TOGGLE_BUTTON(ProcessEncodedByField)->active)
+ {
+ if (!FileTag)
+ {
+ FileTag = ET_File_Tag_Item_New();
+ ET_Copy_File_Tag_Item(ETFile,FileTag);
+ }
+
+ // FIX ME : we suppose that it will not grow more than 2 times its size...
+ string_length = 2 * strlen(st_filetag->encoded_by);
+ string = g_malloc(string_length+1);
+ strncpy(string,st_filetag->encoded_by,string_length);
+ string[string_length]='\0';
+
+ Scan_Process_Fields_Functions(string);
+
+ ET_Set_Field_File_Tag_Item(&FileTag->encoded_by,string);
+
+ g_free(string);
+ }
+ }
+
+ if (FileName && FileTag)
+ {
+ // Synchronize undo key of the both structures (used for the
+ // undo functions, as they are generated as the same time)
+ FileName->key = FileTag->key;
+ }
+ ET_Manage_Changes_Of_File_Data(ETFile,FileName,FileTag);
+
+}
+
+
+void Scan_Process_Fields_Functions (gchar *string)
+{
+
+ if (GTK_TOGGLE_BUTTON(ProcessFieldsConvertIntoSpace)->active)
+ {
+ Scan_Convert_Underscore_Into_Space(string);
+ Scan_Convert_P20_Into_Space(string);
+ }
+
+ if (GTK_TOGGLE_BUTTON(ProcessFieldsConvertSpace)->active)
+ Scan_Convert_Space_Into_Undescore(string);
+
+ if (GTK_TOGGLE_BUTTON(ProcessFieldsInsertSpace)->active)
+ Scan_Process_Fields_Insert_Space(string);
+
+ if (GTK_TOGGLE_BUTTON(ProcessFieldsOnlyOneSpace)->active)
+ Scan_Process_Fields_Keep_One_Space(string);
+
+ if (GTK_TOGGLE_BUTTON(ProcessFieldsConvert)->active)
+ Scan_Convert(string);
+
+ if (GTK_TOGGLE_BUTTON(ProcessFieldsAllUppercase)->active)
+ Scan_Process_Fields_All_Uppercase(string);
+
+ if (GTK_TOGGLE_BUTTON(ProcessFieldsAllDowncase)->active)
+ Scan_Process_Fields_All_Downcase(string);
+
+ if (GTK_TOGGLE_BUTTON(ProcessFieldsFirstLetterUppercase)->active)
+ Scan_Process_Fields_Letter_Uppercase(string);
+
+ if (GTK_TOGGLE_BUTTON(ProcessFieldsFirstLettersUppercase)->active)
+ Scan_Process_Fields_First_Letters_Uppercase(string);
+
+ if (GTK_TOGGLE_BUTTON(ProcessFieldsRemoveSpace)->active)
+ Scan_Process_Fields_Remove_Space(string);
+
+}
+
+void Scan_Process_Fields_All_Uppercase (gchar *text)
+{
+ gchar *temp;
+ gchar temp2[6]; // Must have at least 6 bytes of space
+ gunichar c;
+
+ for (temp = text; *temp; temp = g_utf8_next_char(temp))
+ {
+ c = g_utf8_get_char(temp);
+ if (g_unichar_islower(c))
+ strncpy(temp, temp2, g_unichar_to_utf8(g_unichar_toupper(c), temp2));
+ }
+}
+
+void Scan_Process_Fields_All_Downcase (gchar *text)
+{
+ gchar *temp;
+ gchar temp2[6];
+ gunichar c;
+
+ for (temp = text; *temp; temp = g_utf8_next_char(temp))
+ {
+ c = g_utf8_get_char(temp);
+ if (g_unichar_isupper(c))
+ strncpy(temp, temp2, g_unichar_to_utf8(g_unichar_tolower(c), temp2));
+ }
+}
+
+void Scan_Process_Fields_Letter_Uppercase (gchar *text)
+{
+ gchar *temp;
+ gchar temp2[6];
+ gboolean set_to_upper_case = TRUE;
+ gunichar c;
+ gchar utf8_character[6];
+ gchar *word, *word1, *word2;
+
+ for (temp = text; *temp; temp = g_utf8_next_char(temp))
+ {
+ c = g_utf8_get_char(temp);
+ if (set_to_upper_case && g_unichar_islower(c))
+ strncpy(temp, temp2, g_unichar_to_utf8(g_unichar_toupper(c), temp2));
+ else if (!set_to_upper_case && g_unichar_isupper(c))
+ strncpy(temp, temp2, g_unichar_to_utf8(g_unichar_tolower(c), temp2));
+ set_to_upper_case = FALSE; // After the first time, all will be down case
+ }
+
+ temp = text;
+
+ // Uppercase again the word 'I' in english
+ while ( temp )
+ {
+ word1 = g_utf8_strchr(temp,-1,' ');
+ word2 = g_utf8_strchr(temp,-1,'_');
+
+ // Take the first string found (near beginning of string)
+ if (word1 && word2)
+ word = MIN(word1,word2);
+ else if (word1)
+ word = word1;
+ else if (word2)
+ word = word2;
+ else
+ break;
+
+ // Go to first character of the word (char. after ' ' or '_')
+ word = word+1;
+
+ // Set uppercase word 'I'
+ if (g_ascii_strncasecmp("I ", word, strlen("I ")) == 0)
+ {
+ c = g_utf8_get_char(word);
+ strncpy(word, utf8_character, g_unichar_to_utf8(g_unichar_toupper(c), utf8_character));
+ }
+
+ temp = word;
+ }
+}
+
+void Scan_Process_Fields_First_Letters_Uppercase (gchar *text)
+{
+/**** DANIEL TEST *****
+ gchar *iter;
+ gchar utf8_character[6];
+ gboolean set_to_upper_case = TRUE;
+ gunichar c;
+
+ for (iter = text; *iter; iter = g_utf8_next_char(iter))
+ {
+ c = g_utf8_get_char(iter);
+ if (set_to_upper_case && g_unichar_islower(c))
+ strncpy(iter, utf8_character, g_unichar_to_utf8(g_unichar_toupper(c), utf8_character));
+ else if (!set_to_upper_case && g_unichar_isupper(c))
+ strncpy(iter, utf8_character, g_unichar_to_utf8(g_unichar_tolower(c), utf8_character));
+
+ set_to_upper_case = (g_unichar_isalpha(c)
+ || c == (gunichar)'.'
+ || c == (gunichar)'\''
+ || c == (gunichar)'`') ? FALSE : TRUE;
+ }
+****/
+/**** Barış Çiçek version ****/
+ gchar *word, *word1, *word2, *temp;
+ gint i;
+ gchar utf8_character[6];
+ gunichar c;
+ gboolean set_to_upper_case, set_to_upper_case_tmp;
+ // There have to be space at the end of words to seperate them from prefix
+ gchar *exempt[] =
+ {
+ "a ", "a_",
+ "an ", "an_",
+ "and ", "and_",
+ "at ", "at_",
+ "but ", "but_",
+ "feat. ", "feat._",
+ "for ", "for_",
+ "in ", "in_",
+ "nor ", "nor_",
+ "of ", "of_",
+ "off ", "off_",
+ "on ", "on_",
+ "or ", "or_",
+ "over ", "over_",
+ "so ", "so_",
+ "the ", "the_",
+ "to ", "to_",
+ "with ", "with_",
+ NULL
+ };
+
+ if (!PFS_DONT_UPPER_SOME_WORDS)
+ {
+ exempt[0] = NULL;
+ }
+ Scan_Process_Fields_All_Downcase(text);
+
+ if (!g_utf8_validate(text,-1,NULL))
+ {
+ Log_Print("Scan_Process_Fields_First_Letters_Uppercase: Not a valid utf8! quiting");
+ return;
+ }
+ // Removes trailing whitespace
+ text = g_strchomp(text);
+
+ temp = text;
+
+ // Set first character to uppercase
+ c = g_utf8_get_char(temp);
+ strncpy(text, utf8_character, g_unichar_to_utf8(g_unichar_toupper(c), utf8_character));
+
+ // Uppercase first character of each word, except for 'exempt[]' words lists
+ while ( temp )
+ {
+ word1 = g_utf8_strchr(temp,-1,' ');
+ word2 = g_utf8_strchr(temp,-1,'_');
+
+ // Take the first string found (near beginning of string)
+ if (word1 && word2)
+ word = MIN(word1,word2);
+ else if (word1)
+ word = word1;
+ else if (word2)
+ word = word2;
+ else
+ break;
+
+ // Go to first character of the word (char. after ' ' or '_')
+ word = word+1;
+
+ // Set uppercase the first character of this word
+ c = g_utf8_get_char(word);
+ strncpy(word, utf8_character, g_unichar_to_utf8(g_unichar_toupper(c), utf8_character));
+
+ // Set lowercase the first character of this word if found in the exempt words list
+ for (i=0; exempt[i]!=NULL; i++)
+ {
+ if (g_ascii_strncasecmp(exempt[i], word, strlen(exempt[i])) == 0)
+ {
+ c = g_utf8_get_char(word);
+ strncpy(word, utf8_character, g_unichar_to_utf8(g_unichar_tolower(c), utf8_character));
+ break;
+ }
+ }
+
+ temp = word;
+ }
+
+ // Uppercase letter placed after some characters like '(', '[', '{'
+ set_to_upper_case = FALSE;
+ for (temp = text; *temp; temp = g_utf8_next_char(temp))
+ {
+ c = g_utf8_get_char(temp);
+ set_to_upper_case_tmp = ( c == (gunichar)'('
+ || c == (gunichar)'['
+ || c == (gunichar)'{'
+ || c == (gunichar)'"'
+ ) ? TRUE : FALSE;
+
+ if (set_to_upper_case && g_unichar_islower(c))
+ strncpy(temp, utf8_character, g_unichar_to_utf8(g_unichar_toupper(c), utf8_character));
+
+ set_to_upper_case = set_to_upper_case_tmp;
+ }
+
+}
+
+void Scan_Process_Fields_Remove_Space (gchar *text)
+{
+ gchar *tmp, *tmp1;
+
+ tmp = tmp1 = text;
+ while (*tmp)
+ {
+ while (*tmp == ' ')
+ tmp++;
+ if (*tmp)
+ *(tmp1++) = *(tmp++);
+ }
+ *tmp1 = '\0';
+}
+
+void Scan_Process_Fields_Insert_Space (gchar *text)
+{
+ gchar *iter;
+ gunichar c;
+ gint j;
+
+ // FIXME: try to use realloc
+ for (iter = g_utf8_next_char(text); *iter; iter = g_utf8_next_char(iter)) // i=1 to not consider first "uppercase" letter
+ {
+ c = g_utf8_get_char(iter);
+
+ if (g_unichar_isupper(c))
+ {
+ for (j = strlen(iter); j > 0; j--)
+ *(iter + j) = *(iter + j - 1);
+ *iter = ' ';
+ iter++;
+ }
+ }
+}
+void Scan_Process_Fields_Keep_One_Space (gchar *text)
+{
+ gchar *tmp, *tmp1;
+ tmp = tmp1 = text;
+
+ // Remove multiple consecutive underscores and spaces.
+ while (*tmp1)
+ {
+ while (*tmp1 && *tmp1 != ' ' && *tmp1 != '_')
+ *(tmp++) = *(tmp1++);
+ if (!*tmp1)
+ break;
+ *(tmp++) = *(tmp1++);
+ while (*tmp1 == ' ' || *tmp1 == '_')
+ *tmp1++;
+ }
+ *tmp = '\0';
+}
+
+/*
+ * Function to replace underscore '_' by a space
+ */
+void Scan_Convert_Underscore_Into_Space (gchar *string)
+{
+ gchar *tmp;
+
+ while ((tmp=strchr(string,'_'))!=NULL)
+ *tmp = ' ';
+}
+
+/*
+ * Function to replace %20 by a space
+ */
+void Scan_Convert_P20_Into_Space (gchar *string)
+{
+ gchar *tmp, *tmp1;
+
+ while ((tmp=strstr(string,"%20"))!=NULL)
+ {
+ tmp1 = tmp + 3;
+ *(tmp++) = ' ';
+ while (*tmp1)
+ *(tmp++) = *(tmp1++);
+ *tmp = '\0';
+ }
+}
+
+/*
+ * Function to replace space by '_'
+ */
+void Scan_Convert_Space_Into_Undescore (gchar *string)
+{
+ gchar *tmp;
+
+ while ((tmp=strchr(string,' '))!=NULL)
+ *tmp = '_';
+}
+
+/*
+ * Replace something with something else ;)
+ * Currently this only works with one character for each
+ */
+void Scan_Convert (gchar *string)
+{
+ gchar *tmp;
+ gchar *from = gtk_editable_get_chars(GTK_EDITABLE(ProcessFieldsConvertFrom),0,-1 );
+ gchar *to = gtk_editable_get_chars(GTK_EDITABLE(ProcessFieldsConvertTo),0,-1 );
+
+ if ( from && to && strlen(from)>0 && strlen(to)>0 )
+ while ((tmp=strchr(string,*from))!=NULL)
+ *tmp = *to;
+}
+
+
+
+/*
+ * Return the field of a 'File_Tag' structure corresponding to the mask code
+ */
+gchar **Scan_Return_File_Tag_Field_From_Mask_Code (File_Tag *FileTag, gchar code)
+{
+ switch (code)
+ {
+ case 't': /* Title */
+ return &FileTag->title;
+ case 'a': /* Artist */
+ return &FileTag->artist;
+ case 'b': /* Album */
+ return &FileTag->album;
+ case 'd': /* Disc Number */
+ return &FileTag->disc_number;
+ case 'y': /* Year */
+ return &FileTag->year;
+ case 'n': /* Track */
+ return &FileTag->track;
+ case 'l': /* Track Total */
+ return &FileTag->track_total;
+ case 'g': /* Genre */
+ return &FileTag->genre;
+ case 'c': /* Comment */
+ return &FileTag->comment;
+ case 'p': /* Composer */
+ return &FileTag->composer;
+ case 'o': /* Orig. Artist */
+ return &FileTag->orig_artist;
+ case 'r': /* Copyright */
+ return &FileTag->copyright;
+ case 'u': /* URL */
+ return &FileTag->url;
+ case 'e': /* Encoded by */
+ return &FileTag->encoded_by;
+ case 'i': /* Ignored */
+ return NULL;
+ default:
+ Log_Print("Scanner: Invalid code '%%%c' found!",code);
+ return NULL;
+ }
+}
+
+
+
+/******************
+ * Scanner Window *
+ ******************/
+#include "../pixmaps/black.xpm"
+#include "../pixmaps/blackwhite.xpm"
+void Open_ScannerWindow (gint scanner_type)
+{
+ GtkWidget *ScanVBox;
+ GtkWidget *HBox1, *HBox2, *HBox4, *VBox, *hbox, *vbox;
+ GtkWidget *Table;
+ GtkWidget *Label;
+ GtkWidget *Button;
+ GtkWidget *Separator;
+ GtkWidget *Icon;
+ GtkWidget *EventBox;
+ GtkTooltips *Tips;
+ GtkWidget *MaskStatusIconBox;
+ GList *pf_cb_group1 = NULL;
+ GList *pf_cb_group2 = NULL;
+ GList *pf_cb_group3 = NULL;
+ GtkTreeViewColumn * column;
+ GtkCellRenderer *renderer;
+
+ /* Check if already opened */
+ if (ScannerWindow)
+ {
+ //gdk_window_show(ScannerWindow->window);
+ gdk_window_raise(ScannerWindow->window);
+ if (ScannerOptionCombo)
+ {
+ gtk_combo_box_set_active(GTK_COMBO_BOX(ScannerOptionCombo), scanner_type);
+ }
+ return;
+ }
+
+ if ( scanner_type < SCANNER_FILL_TAG
+ || scanner_type > SCANNER_PROCESS_FIELDS)
+ scanner_type = SCANNER_FILL_TAG;
+
+ /* The window */
+ ScannerWindow = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ /* Config */
+ gtk_container_set_border_width(GTK_CONTAINER(ScannerWindow), 5);
+ gtk_window_set_resizable(GTK_WINDOW(ScannerWindow), FALSE);
+ gtk_window_set_wmclass(GTK_WINDOW(ScannerWindow),"EasyTag_Scanner","easytag"); // Patch from Nikolai Prokoschenko (Debian)
+ if (SCANNER_WINDOW_ON_TOP)
+ gtk_window_set_transient_for(GTK_WINDOW(ScannerWindow),GTK_WINDOW(MainWindow));
+
+ /* The init position is define below, cause the scanner window must be showed before
+ * to be able to move it. */
+
+ /* Title */
+ gtk_window_set_title(GTK_WINDOW(ScannerWindow),_("Tag and File Name scan"));
+
+ /* Signals connection */
+ g_signal_connect(G_OBJECT(ScannerWindow),"destroy",G_CALLBACK(ScannerWindow_Quit),NULL);
+ g_signal_connect(G_OBJECT(ScannerWindow),"delete_event",G_CALLBACK(ScannerWindow_Quit),NULL);
+ g_signal_connect(G_OBJECT(ScannerWindow),"key_press_event",G_CALLBACK(ScannerWindow_Key_Press),NULL);
+
+ /* The tooltips */
+ Tips = gtk_tooltips_new();
+
+ /* The main vbox */
+ ScanVBox = gtk_vbox_new(FALSE,2);
+ gtk_container_add(GTK_CONTAINER(ScannerWindow),ScanVBox);
+
+
+ /*
+ * The hbox for mode buttons + buttons + what to scan
+ */
+ HBox1 = gtk_hbox_new(FALSE,0);
+ gtk_box_pack_start(GTK_BOX(ScanVBox),HBox1,FALSE,FALSE,0);
+
+ /* Option Menu */
+ Label = gtk_label_new(_("Scanner:"));
+ gtk_box_pack_start(GTK_BOX(HBox1),Label,FALSE,FALSE,0);
+
+ EventBox = gtk_event_box_new();
+ ScannerOptionCombo = gtk_combo_box_new_text();
+ gtk_container_add(GTK_CONTAINER(EventBox),ScannerOptionCombo);
+ gtk_box_pack_start(GTK_BOX(HBox1),EventBox,TRUE,TRUE,2);
+ gtk_widget_set_size_request(ScannerOptionCombo, 160, -1);
+
+ /* Option for Tag */
+ gtk_combo_box_append_text(GTK_COMBO_BOX(ScannerOptionCombo), _(Scanner_Option_Menu_Items[SCANNER_FILL_TAG]));
+
+ /* Option for FileName */
+ gtk_combo_box_append_text(GTK_COMBO_BOX(ScannerOptionCombo), _(Scanner_Option_Menu_Items[SCANNER_RENAME_FILE]));
+
+ /* Option for ProcessFields */
+ gtk_combo_box_append_text(GTK_COMBO_BOX(ScannerOptionCombo), _(Scanner_Option_Menu_Items[SCANNER_PROCESS_FIELDS]));
+
+ // Selection of the item made at the end of the function
+ gtk_tooltips_set_tip(Tips, EventBox, _("Select the type of scanner to use"), NULL);
+ g_signal_connect(G_OBJECT(ScannerOptionCombo), "changed", G_CALLBACK(Scanner_Option_Menu_Activate_Item), NULL);
+
+ /* 'Scan selected files' button */
+ SWScanButton = gtk_button_new();
+ Icon = gtk_image_new_from_stock("easytag-scan", GTK_ICON_SIZE_BUTTON);
+ gtk_container_add(GTK_CONTAINER(SWScanButton),Icon);
+ gtk_box_pack_start(GTK_BOX(HBox1),SWScanButton,FALSE,FALSE,0);
+ gtk_button_set_relief(GTK_BUTTON(SWScanButton),GTK_RELIEF_NONE);
+ gtk_tooltips_set_tip(Tips,SWScanButton,_("Open scanner window / Scan selected files"),NULL);
+ g_signal_connect(G_OBJECT(SWScanButton),"clicked",G_CALLBACK(Action_Scan_Selected_Files),NULL);
+
+ /* Separator line */
+ Separator = gtk_vseparator_new();
+ gtk_box_pack_start(GTK_BOX(HBox1),Separator,FALSE,FALSE,2);
+
+ /* Options button */
+ Button = gtk_button_new();
+ Icon = gtk_image_new_from_stock(GTK_STOCK_PREFERENCES, GTK_ICON_SIZE_BUTTON);
+ gtk_container_add(GTK_CONTAINER(Button),Icon);
+ gtk_box_pack_start(GTK_BOX(HBox1),Button,FALSE,FALSE,0);
+ gtk_button_set_relief(GTK_BUTTON(Button),GTK_RELIEF_NONE);
+ gtk_tooltips_set_tip(Tips,Button,_("Scanner Options"),NULL);
+ g_signal_connect(G_OBJECT(Button),"clicked",G_CALLBACK(Scan_Option_Button),NULL);
+
+ /* Mask Editor button */
+ MaskEditorButton = gtk_toggle_button_new();
+ Icon = gtk_image_new_from_stock("easytag-mask", GTK_ICON_SIZE_BUTTON);
+ gtk_container_add(GTK_CONTAINER(MaskEditorButton),Icon);
+ gtk_box_pack_start(GTK_BOX(HBox1),MaskEditorButton,FALSE,FALSE,0);
+ gtk_button_set_relief(GTK_BUTTON(MaskEditorButton),GTK_RELIEF_NONE);
+ gtk_tooltips_set_tip(Tips,MaskEditorButton,_("Show / Hide Masks Editor"),NULL);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(MaskEditorButton),SCAN_MASK_EDITOR_BUTTON);
+ g_signal_connect(G_OBJECT(MaskEditorButton),"toggled",G_CALLBACK(Scan_Toggle_Mask_Editor_Button),NULL);
+
+ /* Legend button */
+ LegendButton = gtk_toggle_button_new();
+ Icon = gtk_image_new_from_stock(GTK_STOCK_HELP, GTK_ICON_SIZE_BUTTON);
+ gtk_container_add(GTK_CONTAINER(LegendButton),Icon);
+ gtk_box_pack_start(GTK_BOX(HBox1),LegendButton,FALSE,FALSE,0);
+ gtk_button_set_relief(GTK_BUTTON(LegendButton),GTK_RELIEF_NONE);
+ gtk_tooltips_set_tip(Tips,LegendButton,_("Show / Hide Legend"),NULL);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(LegendButton),SCAN_LEGEND_BUTTON);
+ g_signal_connect(G_OBJECT(LegendButton),"toggled",G_CALLBACK(Scan_Toggle_Legend_Button),NULL);
+
+ /* Close button */
+ Button = gtk_button_new();
+ Icon = gtk_image_new_from_stock(GTK_STOCK_QUIT, GTK_ICON_SIZE_BUTTON);
+ gtk_container_add(GTK_CONTAINER(Button),Icon);
+ gtk_box_pack_start(GTK_BOX(HBox1),Button,FALSE,FALSE,0);
+ gtk_button_set_relief(GTK_BUTTON(Button),GTK_RELIEF_NONE);
+ gtk_tooltips_set_tip(Tips,Button,_("Close this window"),NULL);
+ g_signal_connect(G_OBJECT(Button),"clicked",G_CALLBACK(ScannerWindow_Quit),NULL);
+
+ /*
+ * Frame for Scan Tag
+ */
+ ScanTagFrame = gtk_frame_new (_(Scanner_Option_Menu_Items[0]));
+ gtk_box_pack_start(GTK_BOX(ScanVBox),ScanTagFrame,FALSE,FALSE,0);
+
+ vbox = gtk_vbox_new(FALSE,4);
+ gtk_container_add(GTK_CONTAINER(ScanTagFrame),vbox);
+ gtk_container_set_border_width(GTK_CONTAINER(vbox), 4);
+ gtk_widget_show(vbox);
+
+ /* The combo box + Status icon */
+ HBox2 = gtk_hbox_new(FALSE,2);
+ gtk_box_pack_start(GTK_BOX(vbox),HBox2,TRUE,TRUE,0);
+
+ // Set up list model which is used both by the combobox and the editor
+ ScanTagListModel = gtk_list_store_new(MASK_EDITOR_COUNT, G_TYPE_STRING);
+
+ // The combo box to select the mask to apply
+ ScanTagMaskCombo = gtk_combo_box_entry_new();
+ gtk_combo_box_set_model(GTK_COMBO_BOX(ScanTagMaskCombo), GTK_TREE_MODEL(ScanTagListModel));
+ gtk_combo_box_entry_set_text_column(GTK_COMBO_BOX_ENTRY(ScanTagMaskCombo), MASK_EDITOR_TEXT);
+
+ gtk_box_pack_start(GTK_BOX(HBox2),ScanTagMaskCombo,TRUE,TRUE,2);
+ gtk_tooltips_set_tip(Tips,GTK_WIDGET(GTK_ENTRY(GTK_BIN(ScanTagMaskCombo)->child)),
+ _("Select or type in a mask using codes (see Legend) to parse file name and "
+ "path. Used to fill in tag fields."),NULL);
+ // Signal to generate preview (preview of the new tag values)
+ g_signal_connect_swapped(G_OBJECT(GTK_ENTRY(GTK_BIN(ScanTagMaskCombo)->child)),"changed",
+ G_CALLBACK(Scan_Fill_Tag_Generate_Preview),NULL);
+
+ // Load masks into the combobox from a file
+ Load_Scan_Tag_Masks_List(ScanTagListModel, MASK_EDITOR_TEXT, Scan_Masks);
+ if (SCAN_TAG_DEFAULT_MASK)
+ {
+ Add_String_To_Combo_List(ScanTagListModel, SCAN_TAG_DEFAULT_MASK);
+ gtk_entry_set_text(GTK_ENTRY(GTK_BIN(ScanTagMaskCombo)->child), SCAN_TAG_DEFAULT_MASK);
+ }else
+ {
+ gtk_combo_box_set_active(GTK_COMBO_BOX(ScanTagMaskCombo), 0);
+ }
+
+ // Mask status icon
+ MaskStatusIconBox = Create_Pixmap_Icon_With_Event_Box("easytag-forbidden");
+ gtk_box_pack_start(GTK_BOX(HBox2),MaskStatusIconBox,FALSE,FALSE,0);
+ gtk_tooltips_set_tip(Tips,MaskStatusIconBox,_("Invalid Scanner Mask"),NULL);
+ // Signal connection to check if mask is correct into the mask entry
+ g_signal_connect_swapped(G_OBJECT(GTK_BIN(ScanTagMaskCombo)->child),"changed",
+ G_CALLBACK(Scan_Check_Scan_Tag_Mask),GTK_OBJECT(MaskStatusIconBox));
+
+ // Preview label
+ FillTagPreviewLabel = gtk_label_new(_("Fill tag preview..."));
+ gtk_label_set_line_wrap(GTK_LABEL(FillTagPreviewLabel),TRUE);
+ gtk_widget_show(FillTagPreviewLabel);
+ gtk_box_pack_start(GTK_BOX(vbox),FillTagPreviewLabel,TRUE,TRUE,0);
+
+ /*
+ * Frame for Rename File
+ */
+ RenameFileFrame = gtk_frame_new (_(Scanner_Option_Menu_Items[1]));
+ gtk_box_pack_start(GTK_BOX(ScanVBox),RenameFileFrame,FALSE,FALSE,0);
+
+ vbox = gtk_vbox_new(FALSE,4);
+ gtk_container_add(GTK_CONTAINER(RenameFileFrame),vbox);
+ gtk_container_set_border_width(GTK_CONTAINER(vbox), 4);
+ gtk_widget_show(vbox);
+
+ /* The button to prefix path + combo box + Status icon */
+ HBox4 = gtk_hbox_new(FALSE,2);
+ gtk_box_pack_start(GTK_BOX(vbox),HBox4,TRUE,TRUE,0);
+
+ // Button to prefix path
+ RenameFilePrefixPathButton = gtk_button_new();
+ Icon = gtk_image_new_from_stock("easytag-add-folder", GTK_ICON_SIZE_SMALL_TOOLBAR); // On Win32, GTK_ICON_SIZE_BUTTON enlarge the combobox...
+ gtk_container_add(GTK_CONTAINER(RenameFilePrefixPathButton),Icon);
+ gtk_box_pack_start(GTK_BOX(HBox4),RenameFilePrefixPathButton,FALSE,FALSE,0);
+ gtk_button_set_relief(GTK_BUTTON(RenameFilePrefixPathButton),GTK_RELIEF_NONE);
+ g_signal_connect(G_OBJECT(RenameFilePrefixPathButton),"clicked",G_CALLBACK(Scan_Rename_File_Prefix_Path),NULL);
+ gtk_tooltips_set_tip(Tips,RenameFilePrefixPathButton,_("Prefix mask with current path"),NULL);
+
+ // Set up list model which is used both by the combobox and the editor
+ RenameFileListModel = gtk_list_store_new(MASK_EDITOR_COUNT, G_TYPE_STRING);
+
+ // The combo box to select the mask to apply
+ RenameFileMaskCombo = gtk_combo_box_entry_new();
+ gtk_combo_box_set_model(GTK_COMBO_BOX(RenameFileMaskCombo), GTK_TREE_MODEL(RenameFileListModel));
+ gtk_combo_box_entry_set_text_column(GTK_COMBO_BOX_ENTRY(RenameFileMaskCombo), MASK_EDITOR_TEXT);
+
+ gtk_box_pack_start(GTK_BOX(HBox4),RenameFileMaskCombo,TRUE,TRUE,2);
+ gtk_container_set_border_width(GTK_CONTAINER(HBox4), 2);
+ gtk_tooltips_set_tip(Tips,GTK_WIDGET(GTK_ENTRY(GTK_BIN(RenameFileMaskCombo)->child)),
+ _("Select or type in a mask using codes (see Legend) to parse tag fields. "
+ "Used to rename the file.\nUse / to make directories. If the first character "
+ "is /, it's a absolute path, otherwise is relative to the old path."),NULL);
+ // Signal to generate preview (preview of the new filename)
+ g_signal_connect_swapped(G_OBJECT(GTK_ENTRY(GTK_BIN(RenameFileMaskCombo)->child)),"changed",
+ G_CALLBACK(Scan_Rename_File_Generate_Preview),NULL);
+
+ // Load masks into the combobox from a file
+ Load_Rename_File_Masks_List(RenameFileListModel, MASK_EDITOR_TEXT, Rename_File_Masks);
+ if (RENAME_FILE_DEFAULT_MASK)
+ {
+ Add_String_To_Combo_List(RenameFileListModel, RENAME_FILE_DEFAULT_MASK);
+ gtk_entry_set_text(GTK_ENTRY(GTK_BIN(RenameFileMaskCombo)->child), RENAME_FILE_DEFAULT_MASK);
+ }else
+ {
+ gtk_combo_box_set_active(GTK_COMBO_BOX(RenameFileMaskCombo), 0);
+ }
+
+ // Mask status icon
+ MaskStatusIconBox = Create_Pixmap_Icon_With_Event_Box("easytag-forbidden");
+ gtk_box_pack_start(GTK_BOX(HBox4),MaskStatusIconBox,FALSE,FALSE,0);
+ gtk_tooltips_set_tip(Tips,MaskStatusIconBox,_("Invalid Scanner Mask"),NULL);
+ // Signal connection to check if mask is correct into the mask entry
+ g_signal_connect_swapped(G_OBJECT(GTK_ENTRY(GTK_BIN(RenameFileMaskCombo)->child)),"changed",
+ G_CALLBACK(Scan_Check_Rename_File_Mask),G_OBJECT(MaskStatusIconBox));
+
+ /* Preview label */
+ RenameFilePreviewLabel = gtk_label_new(_("Rename file preview..."));
+ gtk_label_set_line_wrap(GTK_LABEL(RenameFilePreviewLabel),TRUE);
+ gtk_widget_show(RenameFilePreviewLabel);
+ gtk_box_pack_start(GTK_BOX(vbox),RenameFilePreviewLabel,TRUE,TRUE,0);
+
+ /*
+ * Frame for Processing Fields
+ */
+ ProcessFieldsFrame = gtk_frame_new (_(Scanner_Option_Menu_Items[2]));
+ gtk_box_pack_start(GTK_BOX(ScanVBox),ProcessFieldsFrame,FALSE,FALSE,0);
+
+ VBox = gtk_vbox_new(FALSE,0);
+ gtk_container_add(GTK_CONTAINER(ProcessFieldsFrame),VBox);
+ gtk_container_set_border_width(GTK_CONTAINER(VBox), 4);
+ gtk_widget_show(VBox);
+
+ /* Group: select entry fields to process */
+ hbox = gtk_hbox_new(FALSE,0);
+ gtk_box_pack_start(GTK_BOX(VBox),hbox,FALSE,FALSE,2);
+ EventBox = gtk_event_box_new();
+ Label = gtk_label_new(_("Select fields:"));
+ gtk_box_pack_start(GTK_BOX(hbox),EventBox,FALSE,FALSE,2);
+ gtk_container_add(GTK_CONTAINER(EventBox),Label);
+ gtk_tooltips_set_tip(Tips,EventBox,_("The buttons on the right represent the fields which can "
+ "be processed. Select those who interest you."),NULL);
+ // Advice for Translators : set the first letter of filename translated
+ ProcessFileNameField = gtk_toggle_button_new_with_label( _("F"));
+ gtk_tooltips_set_tip(Tips,ProcessFileNameField, _("Process file name field"),NULL);
+ // Advice for Translators : set the first letter of title translated
+ ProcessTitleField = gtk_toggle_button_new_with_label( _("T"));
+ gtk_tooltips_set_tip(Tips,ProcessTitleField, _("Process title field"),NULL);
+ // Advice for Translators : set the first letter of artist translated
+ ProcessArtistField = gtk_toggle_button_new_with_label( _("Ar"));
+ gtk_tooltips_set_tip(Tips,ProcessArtistField, _("Process file artist field"),NULL);
+ // Advice for Translators : set the first letter of album translated
+ ProcessAlbumField = gtk_toggle_button_new_with_label( _("Al"));
+ gtk_tooltips_set_tip(Tips,ProcessAlbumField, _("Process album field"),NULL);
+ // Advice for Translators : set the first letter of genre translated
+ ProcessGenreField = gtk_toggle_button_new_with_label( _("G"));
+ gtk_tooltips_set_tip(Tips,ProcessGenreField, _("Process genre field"),NULL);
+ // Advice for Translators : set the first letter of comment translated
+ ProcessCommentField = gtk_toggle_button_new_with_label( _("Cm"));
+ gtk_tooltips_set_tip(Tips,ProcessCommentField, _("Process comment field"),NULL);
+ // Advice for Translators : set the first letter of composer translated
+ ProcessComposerField = gtk_toggle_button_new_with_label( _("Cp"));
+ gtk_tooltips_set_tip(Tips,ProcessComposerField, _("Process composer field"),NULL);
+ // Advice for Translators : set the first letter of orig artist translated
+ ProcessOrigArtistField = gtk_toggle_button_new_with_label( _("O"));
+ gtk_tooltips_set_tip(Tips,ProcessOrigArtistField, _("Process original artist field"),NULL);
+ // Advice for Translators : set the first letter of copyright translated
+ ProcessCopyrightField = gtk_toggle_button_new_with_label( _("Cr"));
+ gtk_tooltips_set_tip(Tips,ProcessCopyrightField, _("Process copyright field"),NULL);
+ // Advice for Translators : set the first letter of URL translated
+ ProcessURLField = gtk_toggle_button_new_with_label( _("U"));
+ gtk_tooltips_set_tip(Tips,ProcessURLField, _("Process URL field"),NULL);
+ // Advice for Translators : set the first letter of encoder name translated
+ ProcessEncodedByField = gtk_toggle_button_new_with_label( _("E"));
+ gtk_tooltips_set_tip(Tips,ProcessEncodedByField, _("Process encoder name field"),NULL);
+ gtk_box_pack_start(GTK_BOX(hbox),ProcessFileNameField, TRUE,TRUE,2);
+ gtk_box_pack_start(GTK_BOX(hbox),ProcessTitleField, TRUE,TRUE,2);
+ gtk_box_pack_start(GTK_BOX(hbox),ProcessArtistField, TRUE,TRUE,2);
+ gtk_box_pack_start(GTK_BOX(hbox),ProcessAlbumField, TRUE,TRUE,2);
+ gtk_box_pack_start(GTK_BOX(hbox),ProcessGenreField, TRUE,TRUE,2);
+ gtk_box_pack_start(GTK_BOX(hbox),ProcessCommentField, TRUE,TRUE,2);
+ gtk_box_pack_start(GTK_BOX(hbox),ProcessComposerField, TRUE,TRUE,2);
+ gtk_box_pack_start(GTK_BOX(hbox),ProcessOrigArtistField, TRUE,TRUE,2);
+ gtk_box_pack_start(GTK_BOX(hbox),ProcessCopyrightField, TRUE,TRUE,2);
+ gtk_box_pack_start(GTK_BOX(hbox),ProcessURLField, TRUE,TRUE,2);
+ gtk_box_pack_start(GTK_BOX(hbox),ProcessEncodedByField, TRUE,TRUE,2);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessFileNameField), PROCESS_FILENAME_FIELD);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessTitleField), PROCESS_TITLE_FIELD);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessArtistField), PROCESS_ARTIST_FIELD);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessAlbumField), PROCESS_ALBUM_FIELD);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessGenreField), PROCESS_GENRE_FIELD);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessCommentField), PROCESS_COMMENT_FIELD);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessComposerField), PROCESS_COMPOSER_FIELD);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessOrigArtistField), PROCESS_ORIG_ARTIST_FIELD);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessCopyrightField), PROCESS_COPYRIGHT_FIELD);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessURLField), PROCESS_URL_FIELD);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessEncodedByField), PROCESS_ENCODED_BY_FIELD);
+ g_signal_connect(G_OBJECT(ProcessFileNameField), "toggled",G_CALLBACK(Select_Fields_Set_Sensitive),NULL);
+ g_signal_connect(G_OBJECT(ProcessTitleField), "toggled",G_CALLBACK(Select_Fields_Set_Sensitive),NULL);
+ g_signal_connect(G_OBJECT(ProcessArtistField), "toggled",G_CALLBACK(Select_Fields_Set_Sensitive),NULL);
+ g_signal_connect(G_OBJECT(ProcessAlbumField), "toggled",G_CALLBACK(Select_Fields_Set_Sensitive),NULL);
+ g_signal_connect(G_OBJECT(ProcessGenreField), "toggled",G_CALLBACK(Select_Fields_Set_Sensitive),NULL);
+ g_signal_connect(G_OBJECT(ProcessCommentField), "toggled",G_CALLBACK(Select_Fields_Set_Sensitive),NULL);
+ g_signal_connect(G_OBJECT(ProcessComposerField), "toggled",G_CALLBACK(Select_Fields_Set_Sensitive),NULL);
+ g_signal_connect(G_OBJECT(ProcessOrigArtistField), "toggled",G_CALLBACK(Select_Fields_Set_Sensitive),NULL);
+ g_signal_connect(G_OBJECT(ProcessCopyrightField), "toggled",G_CALLBACK(Select_Fields_Set_Sensitive),NULL);
+ g_signal_connect(G_OBJECT(ProcessURLField), "toggled",G_CALLBACK(Select_Fields_Set_Sensitive),NULL);
+ g_signal_connect(G_OBJECT(ProcessEncodedByField), "toggled",G_CALLBACK(Select_Fields_Set_Sensitive),NULL);
+ /* The small buttons */
+ vbox = gtk_vbox_new(FALSE,2);
+ gtk_box_pack_start(GTK_BOX(hbox),vbox,FALSE,FALSE,0);
+ Button = gtk_button_new();
+ g_signal_connect(G_OBJECT(Button),"clicked",G_CALLBACK(Select_Fields_Invert_Selection),NULL);
+ gtk_box_pack_start(GTK_BOX(vbox),Button,FALSE,FALSE,0);
+ gtk_widget_set_size_request(Button, 12, 12);
+ //Icon = gtk_image_new_from_stock("easytag-blackwhite", GTK_ICON_SIZE_BUTTON);
+ Icon = Create_Xpm_Image((const char **)blackwhite_xpm);
+ GTK_WIDGET_UNSET_FLAGS(Button,GTK_CAN_DEFAULT); // To have enought space to display the icon
+ GTK_WIDGET_UNSET_FLAGS(Button,GTK_CAN_FOCUS);
+ gtk_container_add(GTK_CONTAINER(Button),Icon);
+ gtk_tooltips_set_tip(Tips,Button,_("Invert Selection"),NULL);
+ Button = gtk_button_new();
+ g_signal_connect(G_OBJECT(Button),"clicked",G_CALLBACK(Select_Fields_Select_Unselect_All),NULL);
+ gtk_box_pack_start(GTK_BOX(vbox),Button,FALSE,FALSE,0);
+ gtk_widget_set_size_request(Button, 12, 12);
+ Icon = Create_Xpm_Image((const char **)black_xpm);
+ GTK_WIDGET_UNSET_FLAGS(Button,GTK_CAN_DEFAULT); // To have enought space to display the icon
+ GTK_WIDGET_UNSET_FLAGS(Button,GTK_CAN_FOCUS);
+ gtk_container_add(GTK_CONTAINER(Button),Icon);
+ gtk_tooltips_set_tip(Tips,Button,_("Select/Unselect All."),NULL);
+
+ /* Separator line */
+ Separator = gtk_hseparator_new();
+ gtk_box_pack_start(GTK_BOX(VBox),Separator,FALSE,FALSE,0);
+
+ /* Group: character conversion */
+ ProcessFieldsConvertIntoSpace = gtk_check_button_new_with_label(_("Convert '_' and '%20' to ' '"));
+ ProcessFieldsConvertSpace = gtk_check_button_new_with_label(_("Convert ' ' to '_'"));
+ gtk_box_pack_start(GTK_BOX(VBox),ProcessFieldsConvertIntoSpace,FALSE,FALSE,0);
+ gtk_box_pack_start(GTK_BOX(VBox),ProcessFieldsConvertSpace, FALSE,FALSE,0);
+ hbox = gtk_hbox_new(FALSE,2);
+ ProcessFieldsConvert = gtk_check_button_new_with_label(_("Convert:")); // Patch from Ben Hearsum, Oct. 3, 2003
+ ProcessFieldsConvertTo = gtk_entry_new();
+ ProcessFieldsConvertLabelTo = gtk_label_new(_("to: ")); // A "space" at the end to allow an other traduction for "to :" (needed in French!)
+ ProcessFieldsConvertFrom = gtk_entry_new();
+ gtk_entry_set_max_length(GTK_ENTRY(ProcessFieldsConvertTo), 1);
+ gtk_entry_set_max_length(GTK_ENTRY(ProcessFieldsConvertFrom), 1);
+ gtk_widget_set_size_request(ProcessFieldsConvertTo,40,-1);
+ gtk_widget_set_size_request(ProcessFieldsConvertFrom,40,-1);
+ gtk_box_pack_start(GTK_BOX(hbox),ProcessFieldsConvert, FALSE,FALSE,0);
+ gtk_box_pack_start(GTK_BOX(hbox),ProcessFieldsConvertFrom, FALSE,FALSE,0);
+ gtk_box_pack_start(GTK_BOX(hbox),ProcessFieldsConvertLabelTo,FALSE,FALSE,4);
+ gtk_box_pack_start(GTK_BOX(hbox),ProcessFieldsConvertTo, FALSE,FALSE,0);
+ gtk_box_pack_start(GTK_BOX(VBox),hbox,FALSE,FALSE,0);
+ /* List creation for check buttons in group */
+ pf_cb_group1 = g_list_append (pf_cb_group1,ProcessFieldsConvertIntoSpace);
+ pf_cb_group1 = g_list_append (pf_cb_group1,ProcessFieldsConvertSpace);
+ pf_cb_group1 = g_list_append (pf_cb_group1,ProcessFieldsConvert);
+ /* Toggled signals */
+ g_signal_connect(G_OBJECT(ProcessFieldsConvertIntoSpace),"toggled",G_CALLBACK(Process_Fields_Check_Button_Toggled),pf_cb_group1);
+ g_signal_connect(G_OBJECT(ProcessFieldsConvertSpace), "toggled",G_CALLBACK(Process_Fields_Check_Button_Toggled),pf_cb_group1);
+ g_signal_connect(G_OBJECT(ProcessFieldsConvert), "toggled",G_CALLBACK(Process_Fields_Check_Button_Toggled),pf_cb_group1);
+ g_signal_connect(G_OBJECT(ProcessFieldsConvert), "toggled",G_CALLBACK(Process_Fields_Convert_Check_Button_Toggled),NULL);
+ /* Set check buttons to init value */
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessFieldsConvertIntoSpace),PF_CONVERT_INTO_SPACE);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessFieldsConvertSpace),PF_CONVERT_SPACE);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessFieldsConvert),PF_CONVERT);
+ /* Tooltips */
+ gtk_tooltips_set_tip(Tips,ProcessFieldsConvertIntoSpace,
+ _("The underscore character or the string '%20' are replaced by one space. "
+ "Example, before: 'Text%20In%20An_Entry', after: 'Text In An Entry'."),NULL);
+ gtk_tooltips_set_tip(Tips,ProcessFieldsConvertSpace,
+ _("The space character is replaced by one underscore character. "
+ "Example, before: 'Text In An Entry', after: 'Text_In_An_Entry'."),NULL);
+ gtk_tooltips_set_tip(Tips,ProcessFieldsConvert,_("Replace a character by an other one."),NULL);
+
+ /* Separator line */
+ Separator = gtk_hseparator_new();
+ gtk_box_pack_start(GTK_BOX(VBox),Separator,FALSE,FALSE,0);
+
+ /* Group: capitalize, ... */
+ ProcessFieldsAllUppercase = gtk_check_button_new_with_label (_("All uppercase"));
+ ProcessFieldsAllDowncase = gtk_check_button_new_with_label (_("All downcase"));
+ ProcessFieldsFirstLetterUppercase = gtk_check_button_new_with_label(_("First letter uppercase"));
+ ProcessFieldsFirstLettersUppercase = gtk_check_button_new_with_label(_("First letter uppercase of each word"));
+ gtk_box_pack_start(GTK_BOX(VBox),ProcessFieldsAllUppercase, FALSE,FALSE,0);
+ gtk_box_pack_start(GTK_BOX(VBox),ProcessFieldsAllDowncase, FALSE,FALSE,0);
+ gtk_box_pack_start(GTK_BOX(VBox),ProcessFieldsFirstLetterUppercase, FALSE,FALSE,0);
+ gtk_box_pack_start(GTK_BOX(VBox),ProcessFieldsFirstLettersUppercase,FALSE,FALSE,0);
+ /* List creation for check buttons in group */
+ pf_cb_group2 = g_list_append(pf_cb_group2,ProcessFieldsAllUppercase);
+ pf_cb_group2 = g_list_append(pf_cb_group2,ProcessFieldsAllDowncase);
+ pf_cb_group2 = g_list_append(pf_cb_group2,ProcessFieldsFirstLetterUppercase);
+ pf_cb_group2 = g_list_append(pf_cb_group2,ProcessFieldsFirstLettersUppercase);
+ /* Toggled signals */
+ g_signal_connect(G_OBJECT(ProcessFieldsAllUppercase),"toggled",G_CALLBACK(Process_Fields_Check_Button_Toggled),pf_cb_group2);
+ g_signal_connect(G_OBJECT(ProcessFieldsAllDowncase), "toggled",G_CALLBACK(Process_Fields_Check_Button_Toggled),pf_cb_group2);
+ g_signal_connect(G_OBJECT(ProcessFieldsFirstLetterUppercase),"toggled",G_CALLBACK(Process_Fields_Check_Button_Toggled),pf_cb_group2);
+ g_signal_connect(G_OBJECT(ProcessFieldsFirstLettersUppercase),"toggled",G_CALLBACK(Process_Fields_Check_Button_Toggled),pf_cb_group2);
+ /* Set check buttons to init value */
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessFieldsAllUppercase),PF_CONVERT_ALL_UPPERCASE);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessFieldsAllDowncase),PF_CONVERT_ALL_DOWNCASE);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessFieldsFirstLetterUppercase),PF_CONVERT_FIRST_LETTER_UPPERCASE);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessFieldsFirstLettersUppercase),PF_CONVERT_FIRST_LETTERS_UPPERCASE);
+ /* Tooltips */
+ gtk_tooltips_set_tip(Tips,ProcessFieldsAllUppercase,
+ _("Convert all words in all fields to upper case. "
+ "Example, before: 'Text IN AN entry', after: 'TEXT IN AN ENTRY'."),NULL);
+ gtk_tooltips_set_tip(Tips,ProcessFieldsAllDowncase,
+ _("Convert all words in all fields to lower case. "
+ "Example, before: 'TEXT IN an entry', after: 'text in an entry'."),NULL);
+ gtk_tooltips_set_tip(Tips,ProcessFieldsFirstLetterUppercase,
+ _("Convert the initial of the first word in all fields to upper case. "
+ "Example, before: 'text IN An ENTRY', after: 'Text in an entry'."),NULL);
+ gtk_tooltips_set_tip(Tips,ProcessFieldsFirstLettersUppercase,
+ _("Convert the initial of each word in all fields to upper case. "
+ "Example, before: 'Text in an ENTRY', after: 'Text In An Entry'."),NULL);
+
+ /* Separator line */
+ Separator = gtk_hseparator_new();
+ gtk_box_pack_start(GTK_BOX(VBox),Separator,FALSE,FALSE,0);
+
+ /* Group: insert/remove spaces */
+ ProcessFieldsRemoveSpace = gtk_check_button_new_with_label(_("Remove spaces"));
+ ProcessFieldsInsertSpace = gtk_check_button_new_with_label(_("Insert a space before an uppercase letter"));
+ ProcessFieldsOnlyOneSpace = gtk_check_button_new_with_label(_("Remove duplicates of space or underscore"));
+ gtk_box_pack_start(GTK_BOX(VBox),ProcessFieldsRemoveSpace,FALSE,FALSE,0);
+ gtk_box_pack_start(GTK_BOX(VBox),ProcessFieldsInsertSpace,FALSE,FALSE,0);
+ gtk_box_pack_start(GTK_BOX(VBox),ProcessFieldsOnlyOneSpace,FALSE,FALSE,0);
+ /* List creation for check buttons in group */
+ pf_cb_group3 = g_list_append (pf_cb_group3,ProcessFieldsRemoveSpace);
+ pf_cb_group3 = g_list_append (pf_cb_group3,ProcessFieldsInsertSpace);
+ pf_cb_group3 = g_list_append (pf_cb_group3,ProcessFieldsOnlyOneSpace);
+ /* Toggled signals */
+ g_signal_connect(G_OBJECT(ProcessFieldsRemoveSpace), "toggled",G_CALLBACK(Process_Fields_Check_Button_Toggled),pf_cb_group3);
+ g_signal_connect(G_OBJECT(ProcessFieldsInsertSpace), "toggled",G_CALLBACK(Process_Fields_Check_Button_Toggled),pf_cb_group3);
+ g_signal_connect(G_OBJECT(ProcessFieldsOnlyOneSpace),"toggled",G_CALLBACK(Process_Fields_Check_Button_Toggled),pf_cb_group3);
+ /* Set check buttons to init value */
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessFieldsRemoveSpace),PF_REMOVE_SPACE);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessFieldsInsertSpace),PF_INSERT_SPACE);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessFieldsOnlyOneSpace),PF_ONLY_ONE_SPACE);
+ /* Tooltips */
+ gtk_tooltips_set_tip(Tips,ProcessFieldsRemoveSpace,
+ _("All spaces between words are removed. "
+ "Example, before: 'Text In An Entry', after: 'TextInAnEntry'."),NULL);
+ gtk_tooltips_set_tip(Tips,ProcessFieldsInsertSpace,
+ _("A space is inserted before each upper case letter. "
+ "Example, before: 'TextInAnEntry', after: 'Text In An Entry'."),NULL);
+ gtk_tooltips_set_tip(Tips,ProcessFieldsOnlyOneSpace,
+ _("Duplicated spaces or underscores are removed. "
+ "Example, before: 'Text__In__An Entry', after: 'Text_In_An Entry'."),NULL);
+
+ /*
+ * Frame to display codes legend
+ */
+ LegendFrame = gtk_frame_new (_("Legend"));
+ gtk_box_pack_start(GTK_BOX(ScanVBox),LegendFrame,FALSE,FALSE,0);
+ /* Legend labels */
+ Table = gtk_table_new(3,3,FALSE);
+ gtk_container_add(GTK_CONTAINER(LegendFrame),Table);
+ gtk_container_set_border_width(GTK_CONTAINER(Table),4);
+ Label = gtk_label_new(_("%a : artist"));
+ gtk_table_attach_defaults(GTK_TABLE(Table),Label,0,1,0,1);
+ gtk_misc_set_alignment(GTK_MISC(Label),0,0.5);
+ Label = gtk_label_new(_("%b : album"));
+ gtk_table_attach_defaults(GTK_TABLE(Table),Label,0,1,1,2);
+ gtk_misc_set_alignment(GTK_MISC(Label),0,0.5);
+ Label = gtk_label_new(_("%c : comment"));
+ gtk_table_attach_defaults(GTK_TABLE(Table),Label,0,1,2,3);
+ gtk_misc_set_alignment(GTK_MISC(Label),0,0.5);
+ Label = gtk_label_new(_("%p : composer"));
+ gtk_table_attach_defaults(GTK_TABLE(Table),Label,0,1,3,4);
+ gtk_misc_set_alignment(GTK_MISC(Label),0,0.5);
+ Label = gtk_label_new(_("%r : copyright"));
+ gtk_table_attach_defaults(GTK_TABLE(Table),Label,0,1,4,5);
+ gtk_misc_set_alignment(GTK_MISC(Label),0,0.5);
+ Label = gtk_label_new(_("%d : disc number"));
+ gtk_table_attach_defaults(GTK_TABLE(Table),Label,1,2,0,1);
+ gtk_misc_set_alignment(GTK_MISC(Label),0,0.5);
+ Label = gtk_label_new(_("%e : encoded by"));
+ gtk_table_attach_defaults(GTK_TABLE(Table),Label,1,2,1,2);
+ gtk_misc_set_alignment(GTK_MISC(Label),0,0.5);
+ Label = gtk_label_new(_("%g : genre"));
+ gtk_table_attach_defaults(GTK_TABLE(Table),Label,1,2,2,3);
+ gtk_misc_set_alignment(GTK_MISC(Label),0,0.5);
+ Label = gtk_label_new(_("%i : ignored"));
+ gtk_table_attach_defaults(GTK_TABLE(Table),Label,1,2,3,4);
+ gtk_misc_set_alignment(GTK_MISC(Label),0,0.5);
+ Label = gtk_label_new(_("%l : number of tracks"));
+ gtk_table_attach_defaults(GTK_TABLE(Table),Label,1,2,4,5);
+ gtk_misc_set_alignment(GTK_MISC(Label),0,0.5);
+ Label = gtk_label_new(_("%o : orig. artist"));
+ gtk_table_attach_defaults(GTK_TABLE(Table),Label,2,3,0,1);
+ gtk_misc_set_alignment(GTK_MISC(Label),0,0.5);
+ Label = gtk_label_new(_("%n : track"));
+ gtk_table_attach_defaults(GTK_TABLE(Table),Label,2,3,1,2);
+ gtk_misc_set_alignment(GTK_MISC(Label),0,0.5);
+ Label = gtk_label_new(_("%t : title"));
+ gtk_table_attach_defaults(GTK_TABLE(Table),Label,2,3,2,3);
+ gtk_misc_set_alignment(GTK_MISC(Label),0,0.5);
+ Label = gtk_label_new(_("%u : URL"));
+ gtk_table_attach_defaults(GTK_TABLE(Table),Label,2,3,3,4);
+ gtk_misc_set_alignment(GTK_MISC(Label),0,0.5);
+ Label = gtk_label_new(_("%y : year"));
+ gtk_table_attach_defaults(GTK_TABLE(Table),Label,2,3,4,5);
+ gtk_misc_set_alignment(GTK_MISC(Label),0,0.5);
+
+ /*
+ * Masks Editor
+ */
+ MaskEditorFrame = gtk_frame_new (_("Mask Editor"));
+ gtk_box_pack_start(GTK_BOX(ScanVBox),MaskEditorFrame,FALSE,FALSE,0);
+ MaskEditorHBox = gtk_hbox_new(FALSE,4);
+ gtk_container_add(GTK_CONTAINER(MaskEditorFrame),MaskEditorHBox);
+ gtk_container_set_border_width(GTK_CONTAINER(MaskEditorHBox), 4);
+
+ /* The editor part */
+ MaskEditorVBox = gtk_vbox_new(FALSE,2);
+ gtk_box_pack_start(GTK_BOX(MaskEditorHBox),MaskEditorVBox,TRUE,TRUE,0);
+ MaskEditorScrollWindow = gtk_scrolled_window_new(NULL,NULL);
+ gtk_box_pack_start(GTK_BOX(MaskEditorVBox),MaskEditorScrollWindow,TRUE,TRUE,0);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(MaskEditorScrollWindow),
+ GTK_POLICY_AUTOMATIC,GTK_POLICY_AUTOMATIC);
+ gtk_widget_set_size_request(GTK_WIDGET(MaskEditorScrollWindow), -1, 101);
+
+ /* The list */
+ MaskEditorList = gtk_tree_view_new();
+ gtk_tree_view_set_model(GTK_TREE_VIEW(MaskEditorList), GTK_TREE_MODEL(ScanTagListModel));
+
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes(NULL,
+ renderer, "text", MASK_EDITOR_TEXT, NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(MaskEditorList), column);
+ gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(MaskEditorList), FALSE);
+ gtk_tree_selection_set_mode(gtk_tree_view_get_selection(GTK_TREE_VIEW(MaskEditorList)),
+ GTK_SELECTION_MULTIPLE);
+ gtk_tree_view_set_reorderable(GTK_TREE_VIEW(MaskEditorList), TRUE);
+ gtk_container_add(GTK_CONTAINER(MaskEditorScrollWindow), MaskEditorList);
+ g_signal_connect_after(G_OBJECT(gtk_tree_view_get_selection(GTK_TREE_VIEW(MaskEditorList))),
+ "changed", G_CALLBACK(Mask_Editor_List_Row_Selected), NULL);
+ g_signal_connect(G_OBJECT(MaskEditorList), "key-press-event",
+ G_CALLBACK(Mask_Editor_List_Key_Press), NULL);
+ /* The entry */
+ hbox = gtk_hbox_new(FALSE,2);
+ gtk_box_pack_start(GTK_BOX(MaskEditorVBox),hbox,FALSE,FALSE,0);
+ MaskEditorEntry = gtk_entry_new();
+ gtk_box_pack_start(GTK_BOX(hbox),MaskEditorEntry,TRUE,TRUE,2);
+ g_signal_connect(G_OBJECT(MaskEditorEntry),"changed",
+ G_CALLBACK(Mask_Editor_Entry_Changed),NULL);
+ // Mask status icon
+ MaskStatusIconBox = Create_Pixmap_Icon_With_Event_Box("easytag-forbidden");
+ gtk_box_pack_start(GTK_BOX(hbox),MaskStatusIconBox,FALSE,FALSE,0);
+ gtk_tooltips_set_tip(Tips,MaskStatusIconBox,_("Invalid Scanner Mask"),NULL);
+ // Signal connection to check if mask is correct into the mask entry
+ g_signal_connect_swapped(G_OBJECT(MaskEditorEntry),"changed",
+ G_CALLBACK(Scan_Check_Editor_Mask),G_OBJECT(MaskStatusIconBox));
+
+ /* The buttons part */
+ MaskEditorVBox = gtk_vbox_new(FALSE,0);
+ gtk_box_pack_start(GTK_BOX(MaskEditorHBox),MaskEditorVBox,FALSE,FALSE,0);
+
+ /* New mask button */
+ MaskEditorNewButton = gtk_button_new();
+ Icon = gtk_image_new_from_stock(GTK_STOCK_NEW, GTK_ICON_SIZE_SMALL_TOOLBAR);
+ gtk_container_add(GTK_CONTAINER(MaskEditorNewButton),Icon);
+ gtk_box_pack_start(GTK_BOX(MaskEditorVBox),MaskEditorNewButton,FALSE,FALSE,0);
+ gtk_button_set_relief(GTK_BUTTON(MaskEditorNewButton),GTK_RELIEF_NONE);
+ gtk_tooltips_set_tip(Tips,MaskEditorNewButton,_("Create New Mask"),NULL);
+ g_signal_connect(G_OBJECT(MaskEditorNewButton),"clicked",
+ G_CALLBACK(Mask_Editor_List_New),NULL);
+
+ /* Move up mask button */
+ MaskEditorUpButton = gtk_button_new();
+ Icon = gtk_image_new_from_stock(GTK_STOCK_GO_UP, GTK_ICON_SIZE_SMALL_TOOLBAR);
+ gtk_container_add(GTK_CONTAINER(MaskEditorUpButton),Icon);
+ gtk_box_pack_start(GTK_BOX(MaskEditorVBox),MaskEditorUpButton,FALSE,FALSE,0);
+ gtk_button_set_relief(GTK_BUTTON(MaskEditorUpButton),GTK_RELIEF_NONE);
+ gtk_tooltips_set_tip(Tips,MaskEditorUpButton,_("Move Up this Mask"),NULL);
+ g_signal_connect(G_OBJECT(MaskEditorUpButton),"clicked",
+ G_CALLBACK(Mask_Editor_List_Move_Up),NULL);
+
+ /* Move down mask button */
+ MaskEditorDownButton = gtk_button_new();
+ Icon = gtk_image_new_from_stock(GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_SMALL_TOOLBAR);
+ gtk_container_add(GTK_CONTAINER(MaskEditorDownButton),Icon);
+ gtk_box_pack_start(GTK_BOX(MaskEditorVBox),MaskEditorDownButton,FALSE,FALSE,0);
+ gtk_button_set_relief(GTK_BUTTON(MaskEditorDownButton),GTK_RELIEF_NONE);
+ gtk_tooltips_set_tip(Tips,MaskEditorDownButton,_("Move Down this Mask"),NULL);
+ g_signal_connect(G_OBJECT(MaskEditorDownButton),"clicked",
+ G_CALLBACK(Mask_Editor_List_Move_Down),NULL);
+
+ /* Copy mask button */
+ MaskEditorCopyButton = gtk_button_new();
+ Icon = gtk_image_new_from_stock(GTK_STOCK_COPY, GTK_ICON_SIZE_SMALL_TOOLBAR);
+ gtk_container_add(GTK_CONTAINER(MaskEditorCopyButton),Icon);
+ gtk_box_pack_start(GTK_BOX(MaskEditorVBox),MaskEditorCopyButton,FALSE,FALSE,0);
+ gtk_button_set_relief(GTK_BUTTON(MaskEditorCopyButton),GTK_RELIEF_NONE);
+ gtk_tooltips_set_tip(Tips,MaskEditorCopyButton,_("Duplicate Mask"),NULL);
+ g_signal_connect(G_OBJECT(MaskEditorCopyButton),"clicked",
+ G_CALLBACK(Mask_Editor_List_Duplicate),NULL);
+
+ /* Add mask button */
+ MaskEditorAddButton = gtk_button_new();
+ Icon = gtk_image_new_from_stock(GTK_STOCK_ADD, GTK_ICON_SIZE_SMALL_TOOLBAR);
+ gtk_container_add(GTK_CONTAINER(MaskEditorAddButton),Icon);
+ gtk_box_pack_start(GTK_BOX(MaskEditorVBox),MaskEditorAddButton,FALSE,FALSE,0);
+ gtk_button_set_relief(GTK_BUTTON(MaskEditorAddButton),GTK_RELIEF_NONE);
+ gtk_tooltips_set_tip(Tips,MaskEditorAddButton,_("Add Default Masks"),NULL);
+ g_signal_connect(G_OBJECT(MaskEditorAddButton),"clicked",
+ G_CALLBACK(Mask_Editor_List_Add),NULL);
+
+ /* Remove mask button */
+ MaskEditorRemoveButton = gtk_button_new();
+ Icon = gtk_image_new_from_stock(GTK_STOCK_REMOVE, GTK_ICON_SIZE_SMALL_TOOLBAR);
+ gtk_container_add(GTK_CONTAINER(MaskEditorRemoveButton),Icon);
+ gtk_box_pack_start(GTK_BOX(MaskEditorVBox),MaskEditorRemoveButton,FALSE,FALSE,0);
+ gtk_button_set_relief(GTK_BUTTON(MaskEditorRemoveButton),GTK_RELIEF_NONE);
+ gtk_tooltips_set_tip(Tips,MaskEditorRemoveButton,_("Remove Mask"),NULL);
+ g_signal_connect(G_OBJECT(MaskEditorRemoveButton),"clicked",
+ G_CALLBACK(Mask_Editor_List_Remove),NULL);
+
+ /* Save mask button */
+ MaskEditorSaveButton = gtk_button_new();
+ Icon = gtk_image_new_from_stock(GTK_STOCK_SAVE, GTK_ICON_SIZE_SMALL_TOOLBAR);
+ gtk_container_add(GTK_CONTAINER(MaskEditorSaveButton),Icon);
+ gtk_box_pack_end(GTK_BOX(MaskEditorVBox),MaskEditorSaveButton,FALSE,FALSE,0);
+ gtk_button_set_relief(GTK_BUTTON(MaskEditorSaveButton),GTK_RELIEF_NONE);
+ gtk_tooltips_set_tip(Tips,MaskEditorSaveButton,_("Save Masks"),NULL);
+ g_signal_connect(G_OBJECT(MaskEditorSaveButton),"clicked",
+ G_CALLBACK(Mask_Editor_List_Save_Button),NULL);
+
+ gtk_widget_show(ScanVBox);
+ gtk_widget_show_all(HBox1);
+ gtk_widget_show_all(HBox2);
+ gtk_widget_show_all(HBox4);
+ gtk_widget_show(ScannerWindow);
+
+ /* Init position of the scanner window */
+ Scan_Set_Scanner_Window_Init_Position();
+
+ /* To initialize the mask status icon and visibility */
+ g_signal_emit_by_name(G_OBJECT(GTK_ENTRY(GTK_BIN(ScanTagMaskCombo)->child)),"changed");
+ g_signal_emit_by_name(G_OBJECT(GTK_ENTRY(GTK_BIN(RenameFileMaskCombo)->child)),"changed");
+ g_signal_emit_by_name(G_OBJECT(MaskEditorEntry),"changed");
+ g_signal_emit_by_name(G_OBJECT(LegendButton),"toggled"); /* To hide legend frame */
+ g_signal_emit_by_name(G_OBJECT(MaskEditorButton),"toggled"); /* To hide mask editor frame */
+ g_signal_emit_by_name(G_OBJECT(ProcessFieldsConvert),"toggled");/* To enable / disable entries */
+
+ // Activate the current menu in the option menu
+ gtk_combo_box_set_active(GTK_COMBO_BOX(ScannerOptionCombo), scanner_type);
+}
+
+gboolean ScannerWindow_Key_Press (GtkWidget *window, GdkEvent *event)
+{
+ GdkEventKey *kevent;
+
+ if (event && event->type == GDK_KEY_PRESS)
+ {
+ kevent = (GdkEventKey *)event;
+ switch(kevent->keyval)
+ {
+ case GDK_Escape:
+ ScannerWindow_Quit();
+ break;
+ }
+ }
+ return FALSE;
+}
+
+/*
+ * Select the scanner to run for the current ETFile
+ */
+void Scan_Select_Mode_And_Run_Scanner (ET_File *ETFile)
+{
+ if (!ScannerWindow || !ETFile) return;
+
+ if (gtk_combo_box_get_active(GTK_COMBO_BOX(ScannerOptionCombo)) == SCANNER_FILL_TAG)
+ {
+ /* Run Scanner Type: Scan Tag */
+ Scan_Tag_With_Mask(ETFile);
+ } else if (gtk_combo_box_get_active(GTK_COMBO_BOX(ScannerOptionCombo)) == SCANNER_RENAME_FILE)
+ {
+ /* Run Scanner Type: Rename File */
+ Scan_Rename_File_With_Mask(ETFile);
+ } else if (gtk_combo_box_get_active(GTK_COMBO_BOX(ScannerOptionCombo)) == SCANNER_PROCESS_FIELDS)
+ {
+ /* Run Scanner Type: Process Fields */
+ Scan_Process_Fields(ETFile);
+ }
+}
+
+void Scan_Use_Fill_Tag_Scanner (void)
+{
+ if (!ScannerWindow || gtk_combo_box_get_active(GTK_COMBO_BOX(ScannerOptionCombo)) != SCANNER_FILL_TAG)
+ Open_ScannerWindow(SCANNER_FILL_TAG);
+ else
+ Action_Scan_Selected_Files();
+}
+
+
+void Scan_Use_Rename_File_Scanner (void)
+{
+ if (!ScannerWindow || gtk_combo_box_get_active(GTK_COMBO_BOX(ScannerOptionCombo)) != SCANNER_RENAME_FILE)
+ Open_ScannerWindow(SCANNER_RENAME_FILE);
+ else
+ Action_Scan_Selected_Files();
+}
+
+void Scan_Use_Process_Fields_Scanner (void)
+{
+ if (!ScannerWindow || gtk_combo_box_get_active(GTK_COMBO_BOX(ScannerOptionCombo)) != SCANNER_PROCESS_FIELDS)
+ Open_ScannerWindow(SCANNER_PROCESS_FIELDS);
+ else
+ Action_Scan_Selected_Files();
+}
+
+
+/* Callback from Open_ScannerWindow */
+void ScannerWindow_Quit (void)
+{
+ if (ScannerWindow)
+ {
+ if (SCAN_TAG_DEFAULT_MASK) g_free(SCAN_TAG_DEFAULT_MASK);
+ SCAN_TAG_DEFAULT_MASK = g_strdup(gtk_entry_get_text(GTK_ENTRY(GTK_BIN(ScanTagMaskCombo)->child)));
+ Add_String_To_Combo_List(ScanTagListModel, SCAN_TAG_DEFAULT_MASK);
+ Save_Rename_File_Masks_List(ScanTagListModel, MASK_EDITOR_TEXT);
+
+ if (RENAME_FILE_DEFAULT_MASK) g_free(RENAME_FILE_DEFAULT_MASK);
+ RENAME_FILE_DEFAULT_MASK = g_strdup(gtk_entry_get_text(GTK_ENTRY(GTK_BIN(RenameFileMaskCombo)->child)));
+ Add_String_To_Combo_List(RenameFileListModel, RENAME_FILE_DEFAULT_MASK);
+ Save_Rename_File_Masks_List(RenameFileListModel, MASK_EDITOR_TEXT);
+
+ ScannerWindow_Apply_Changes();
+
+ gtk_widget_destroy(ScannerWindow);
+ gtk_list_store_clear(ScanTagListModel);
+ gtk_list_store_clear(RenameFileListModel);
+ ScannerWindow = (GtkWidget *)NULL;
+ ScannerOptionCombo= (GtkWidget *)NULL;
+ SWScanButton = (GtkWidget *)NULL;
+
+ // To avoid crashs after tests
+ ScanTagMaskCombo = (GtkWidget *)NULL;
+ RenameFileMaskCombo = (GtkWidget *)NULL;
+ MaskEditorEntry = (GtkWidget *)NULL;
+ LegendFrame = (GtkWidget *)NULL;
+ ProcessFieldsConvertIntoSpace = (GtkWidget *)NULL;
+ ProcessFieldsConvertSpace = (GtkWidget *)NULL;
+ FillTagPreviewLabel = (GtkWidget *)NULL;
+ RenameFilePreviewLabel = (GtkWidget *)NULL;
+ }
+}
+
+
+/*
+ * For the configuration file...
+ */
+void ScannerWindow_Apply_Changes (void)
+{
+ if (ScannerWindow)
+ {
+ gint x, y;//, width, height;
+
+ if ( ScannerWindow->window!=NULL && gdk_window_is_visible(ScannerWindow->window)
+ && gdk_window_get_state(ScannerWindow->window)!=GDK_WINDOW_STATE_MAXIMIZED )
+ {
+ // Position and Origin of the scanner window
+ gdk_window_get_root_origin(ScannerWindow->window,&x,&y);
+ SCANNER_WINDOW_X = x;
+ SCANNER_WINDOW_Y = y;
+ //gdk_window_get_size(ScannerWindow->window,&width,&height);
+ //SCANNER_WINDOW_WIDTH = width;
+ //SCANNER_WINDOW_HEIGHT = height;
+ }
+
+ // The scanner selected
+ SCANNER_TYPE = gtk_combo_box_get_active(GTK_COMBO_BOX(ScannerOptionCombo));
+
+ SCAN_MASK_EDITOR_BUTTON = GTK_TOGGLE_BUTTON(MaskEditorButton)->active;
+ SCAN_LEGEND_BUTTON = GTK_TOGGLE_BUTTON(LegendButton)->active;
+
+ /* Group: select entries to process */
+ PROCESS_FILENAME_FIELD = GTK_TOGGLE_BUTTON(ProcessFileNameField)->active;
+ PROCESS_TITLE_FIELD = GTK_TOGGLE_BUTTON(ProcessTitleField)->active;
+ PROCESS_ARTIST_FIELD = GTK_TOGGLE_BUTTON(ProcessArtistField)->active;
+ PROCESS_ALBUM_FIELD = GTK_TOGGLE_BUTTON(ProcessAlbumField)->active;
+ PROCESS_GENRE_FIELD = GTK_TOGGLE_BUTTON(ProcessGenreField)->active;
+ PROCESS_COMMENT_FIELD = GTK_TOGGLE_BUTTON(ProcessCommentField)->active;
+ PROCESS_COMPOSER_FIELD = GTK_TOGGLE_BUTTON(ProcessComposerField)->active;
+ PROCESS_ORIG_ARTIST_FIELD = GTK_TOGGLE_BUTTON(ProcessOrigArtistField)->active;
+ PROCESS_COPYRIGHT_FIELD = GTK_TOGGLE_BUTTON(ProcessCopyrightField)->active;
+ PROCESS_URL_FIELD = GTK_TOGGLE_BUTTON(ProcessURLField)->active;
+ PROCESS_ENCODED_BY_FIELD = GTK_TOGGLE_BUTTON(ProcessEncodedByField)->active;
+
+ /* Group: convert one character */
+ PF_CONVERT_INTO_SPACE = GTK_TOGGLE_BUTTON(ProcessFieldsConvertIntoSpace)->active;
+ PF_CONVERT_SPACE = GTK_TOGGLE_BUTTON(ProcessFieldsConvertSpace)->active;
+ PF_CONVERT = GTK_TOGGLE_BUTTON(ProcessFieldsConvert)->active;
+
+ /* Group: capitalize */
+ PF_CONVERT_ALL_UPPERCASE = GTK_TOGGLE_BUTTON(ProcessFieldsAllUppercase)->active;
+ PF_CONVERT_ALL_DOWNCASE = GTK_TOGGLE_BUTTON(ProcessFieldsAllDowncase)->active;
+ PF_CONVERT_FIRST_LETTER_UPPERCASE = GTK_TOGGLE_BUTTON(ProcessFieldsFirstLetterUppercase)->active;
+ PF_CONVERT_FIRST_LETTERS_UPPERCASE = GTK_TOGGLE_BUTTON(ProcessFieldsFirstLettersUppercase)->active;
+
+ /* Group: remove/insert space */
+ PF_REMOVE_SPACE = GTK_TOGGLE_BUTTON(ProcessFieldsConvertIntoSpace)->active;
+ PF_INSERT_SPACE = GTK_TOGGLE_BUTTON(ProcessFieldsConvertSpace)->active;
+ PF_ONLY_ONE_SPACE = GTK_TOGGLE_BUTTON(ProcessFieldsOnlyOneSpace)->active;
+ }
+}
+
+
+/* Callback from Option button */
+void Scan_Option_Button (void)
+{
+ Open_OptionsWindow();
+ gtk_notebook_set_current_page(GTK_NOTEBOOK(OptionsNoteBook), OptionsNoteBook_Scanner_Page_Num);
+}
+
+
+
+/*
+ * Check if mask of the "Scan Tag" is valid. Return TRUE if valid, else FALSE.
+ */
+gboolean Scan_Check_Scan_Tag_Mask (GtkObject *widget_to_show_hide, GtkEntry *widget_source)
+{
+ gchar *tmp = NULL;
+ gchar *mask = NULL;
+ gint loop = 0;
+
+
+ if (!widget_to_show_hide || !widget_source)
+ goto Bad_Mask;
+
+ mask = g_strdup(gtk_entry_get_text(GTK_ENTRY(widget_source)));
+ if (!mask || strlen(mask)<1)
+ goto Bad_Mask;
+
+ while (mask)
+ {
+ if ( (tmp=strrchr(mask,'%'))==NULL )
+ {
+ if (loop==0)
+ /* There is no code the first time => not accepted */
+ goto Bad_Mask;
+ else
+ /* There is no more code => accepted */
+ goto Good_Mask;
+ }
+ if ( strlen(tmp)>1
+ && (tmp[1]=='a' || tmp[1]=='b' || tmp[1]=='c' || tmp[1]=='d' || tmp[1]=='p' ||
+ tmp[1]=='r' || tmp[1]=='e' || tmp[1]=='g' || tmp[1]=='i' || tmp[1]=='l' ||
+ tmp[1]=='o' || tmp[1]=='n' || tmp[1]=='t' || tmp[1]=='u' || tmp[1]=='y' ) )
+ {
+ /* Code is correct */
+ *(mask+strlen(mask)-strlen(tmp)) = '\0';
+ }else
+ {
+ goto Bad_Mask;
+ }
+
+ /* Check the following code and separator */
+ if ( (tmp=strrchr(mask,'%'))==NULL )
+ /* There is no more code => accepted */
+ goto Good_Mask;
+
+ if ( strlen(tmp)>2
+ && (tmp[1]=='a' || tmp[1]=='b' || tmp[1]=='c' || tmp[1]=='d' || tmp[1]=='p' ||
+ tmp[1]=='r' || tmp[1]=='e' || tmp[1]=='g' || tmp[1]=='i' || tmp[1]=='l' ||
+ tmp[1]=='o' || tmp[1]=='n' || tmp[1]=='t' || tmp[1]=='u' || tmp[1]=='y' ) )
+ {
+ /* There is a separator and code is correct */
+ *(mask+strlen(mask)-strlen(tmp)) = '\0';
+ }else
+ {
+ goto Bad_Mask;
+ }
+ loop++;
+ }
+
+ Bad_Mask:
+ g_free(mask);
+ gtk_widget_show(GTK_WIDGET(widget_to_show_hide));
+ return FALSE;
+
+ Good_Mask:
+ g_free(mask);
+ gtk_widget_hide(GTK_WIDGET(widget_to_show_hide));
+ return TRUE;
+}
+/*
+ * Check if mask of the "Rename File" is valid. Return TRUE if valid, else FALSE.
+ */
+gboolean Scan_Check_Rename_File_Mask (GtkObject *widget_to_show_hide, GtkEntry *widget_source)
+{
+ gchar *tmp = NULL;
+ gchar *mask = NULL;
+
+
+ if (!widget_to_show_hide || !widget_source)
+ goto Bad_Mask;
+
+ mask = g_strdup(gtk_entry_get_text(GTK_ENTRY(widget_source)));
+ if (!mask || strlen(mask)<1)
+ goto Bad_Mask;
+
+ // Since version 1.99.4, it is available!
+ /*if ( strchr(mask,G_DIR_SEPARATOR)!=NULL ) // Renaming directory is not yet available
+ goto Bad_Mask;*/
+ // Not a valid path....
+ if ( strstr(mask,"//") != NULL
+ || strstr(mask,"./") != NULL
+ || strstr(mask,"../") != NULL)
+ goto Bad_Mask;
+
+ while (mask)
+ {
+ if ( (tmp=strrchr(mask,'%'))==NULL )
+ {
+ /* There is no more code. */
+ /* No code in mask is accepted. */
+ goto Good_Mask;
+ }
+ if ( strlen(tmp)>1
+ && (tmp[1]=='a' || tmp[1]=='b' || tmp[1]=='c' || tmp[1]=='d' || tmp[1]=='p' ||
+ tmp[1]=='r' || tmp[1]=='e' || tmp[1]=='g' || tmp[1]=='i' || tmp[1]=='l' ||
+ tmp[1]=='o' || tmp[1]=='n' || tmp[1]=='t' || tmp[1]=='u' || tmp[1]=='y' ) )
+ {
+ /* The code is valid. */
+ /* No separator is accepted. */
+ *(mask+strlen(mask)-strlen(tmp)) = '\0';
+ }else
+ {
+ goto Bad_Mask;
+ }
+ }
+
+ Bad_Mask:
+ g_free(mask);
+ gtk_widget_show(GTK_WIDGET(widget_to_show_hide));
+ return FALSE;
+
+ Good_Mask:
+ g_free(mask);
+ gtk_widget_hide(GTK_WIDGET(widget_to_show_hide));
+ return TRUE;
+}
+
+
+/*
+ * Check if the selected mask in the Mask Editor is valid, else display the mask status icon.
+ */
+gboolean Scan_Check_Editor_Mask (GtkObject *widget_to_show_hide, GtkEntry *widget_source)
+{
+ /* Select and get result of check scanner */
+ if (gtk_combo_box_get_active(GTK_COMBO_BOX(ScannerOptionCombo)) == SCANNER_FILL_TAG)
+ {
+ return Scan_Check_Scan_Tag_Mask(widget_to_show_hide,widget_source);
+ } else if (gtk_combo_box_get_active(GTK_COMBO_BOX(ScannerOptionCombo)) == SCANNER_RENAME_FILE)
+ {
+ return Scan_Check_Rename_File_Mask(widget_to_show_hide,widget_source);
+ } else
+ return FALSE;
+}
+
+
+void Scan_Toggle_Legend_Button (void)
+{
+ if (!LegendButton || !LegendFrame) return;
+
+ if ( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(LegendButton)) )
+ gtk_widget_show_all(LegendFrame);
+ else
+ gtk_widget_hide(LegendFrame);
+}
+
+
+void Scan_Toggle_Mask_Editor_Button (void)
+{
+ GtkTreeModel *treemodel;
+ GtkTreeSelection *selection;
+ GtkTreeIter iter;
+
+ if (!MaskEditorButton || !MaskEditorFrame || !MaskEditorList) return;
+
+ if ( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(MaskEditorButton)) )
+ {
+ gtk_widget_show_all(MaskEditorFrame);
+
+ // Select first row in list
+ treemodel = gtk_tree_view_get_model(GTK_TREE_VIEW(MaskEditorList));
+ if (gtk_tree_model_get_iter_first(treemodel, &iter))
+ {
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(MaskEditorList));
+ gtk_tree_selection_unselect_all(selection);
+ gtk_tree_selection_select_iter(selection, &iter);
+ }
+
+ // Update status of the icon box cause prev instruction show it for all cases
+ g_signal_emit_by_name(GTK_OBJECT(MaskEditorEntry),"changed");
+ }else
+ {
+ gtk_widget_hide_all(MaskEditorFrame);
+ }
+}
+
+
+
+/*
+ * Manage/Toggle check buttons into 'Process Fields' frame
+ */
+void Process_Fields_Check_Button_Toggled (GtkObject *object, GList *list)
+{
+ gint i = 0;
+
+ if ( GTK_TOGGLE_BUTTON(object)->active )
+ {
+ while (list)
+ {
+ if ( list->data!=NULL && GTK_OBJECT(list->data)!=object )
+ gtk_toggle_button_set_active((GtkToggleButton *)list->data,FALSE);
+ i++;
+ if (!list->next) break;
+ list = list->next;
+ }
+ }
+}
+
+
+void Process_Fields_Convert_Check_Button_Toggled (GtkObject *object)
+{
+ gtk_widget_set_sensitive(GTK_WIDGET(ProcessFieldsConvertTo),GTK_TOGGLE_BUTTON(object)->active);
+ gtk_widget_set_sensitive(GTK_WIDGET(ProcessFieldsConvertFrom),GTK_TOGGLE_BUTTON(object)->active);
+}
+
+
+/*
+ * Small buttons of Process Fields scanner
+ */
+void Select_Fields_Invert_Selection (void)
+{
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessFileNameField),
+ !GTK_TOGGLE_BUTTON(ProcessFileNameField)->active);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessTitleField),
+ !GTK_TOGGLE_BUTTON(ProcessTitleField)->active);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessArtistField),
+ !GTK_TOGGLE_BUTTON(ProcessArtistField)->active);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessAlbumField),
+ !GTK_TOGGLE_BUTTON(ProcessAlbumField)->active);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessGenreField),
+ !GTK_TOGGLE_BUTTON(ProcessGenreField)->active);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessCommentField),
+ !GTK_TOGGLE_BUTTON(ProcessCommentField)->active);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessComposerField),
+ !GTK_TOGGLE_BUTTON(ProcessComposerField)->active);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessOrigArtistField),
+ !GTK_TOGGLE_BUTTON(ProcessOrigArtistField)->active);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessCopyrightField),
+ !GTK_TOGGLE_BUTTON(ProcessCopyrightField)->active);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessURLField),
+ !GTK_TOGGLE_BUTTON(ProcessURLField)->active);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessEncodedByField),
+ !GTK_TOGGLE_BUTTON(ProcessEncodedByField)->active);
+}
+void Select_Fields_Select_Unselect_All (void)
+{
+ static gboolean state = 1;
+
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessFileNameField), state);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessTitleField), state);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessArtistField), state);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessAlbumField), state);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessGenreField), state);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessCommentField), state);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessComposerField), state);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessOrigArtistField), state);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessCopyrightField), state);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessURLField), state);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessEncodedByField), state);
+ state = !state;
+}
+
+/*
+ * Set sensitive state of the processing check boxes : if no one is selected => all disabled
+ */
+void Select_Fields_Set_Sensitive (void)
+{
+ if (GTK_TOGGLE_BUTTON(ProcessFileNameField)->active
+ || GTK_TOGGLE_BUTTON(ProcessTitleField)->active
+ || GTK_TOGGLE_BUTTON(ProcessArtistField)->active
+ || GTK_TOGGLE_BUTTON(ProcessAlbumField)->active
+ || GTK_TOGGLE_BUTTON(ProcessGenreField)->active
+ || GTK_TOGGLE_BUTTON(ProcessCommentField)->active
+ || GTK_TOGGLE_BUTTON(ProcessComposerField)->active
+ || GTK_TOGGLE_BUTTON(ProcessOrigArtistField)->active
+ || GTK_TOGGLE_BUTTON(ProcessCopyrightField)->active
+ || GTK_TOGGLE_BUTTON(ProcessURLField)->active
+ || GTK_TOGGLE_BUTTON(ProcessEncodedByField)->active)
+ {
+ gtk_widget_set_sensitive(GTK_WIDGET(ProcessFieldsConvertIntoSpace), TRUE);
+ gtk_widget_set_sensitive(GTK_WIDGET(ProcessFieldsConvertSpace), TRUE);
+ gtk_widget_set_sensitive(GTK_WIDGET(ProcessFieldsConvert), TRUE);
+ gtk_widget_set_sensitive(GTK_WIDGET(ProcessFieldsConvertLabelTo), TRUE);
+ // Activate the two entries only if the check box is actived, esle keep them disabled
+ if (GTK_TOGGLE_BUTTON(ProcessFieldsConvert)->active)
+ {
+ gtk_widget_set_sensitive(GTK_WIDGET(ProcessFieldsConvertTo), TRUE);
+ gtk_widget_set_sensitive(GTK_WIDGET(ProcessFieldsConvertFrom), TRUE);
+ }
+ gtk_widget_set_sensitive(GTK_WIDGET(ProcessFieldsAllUppercase), TRUE);
+ gtk_widget_set_sensitive(GTK_WIDGET(ProcessFieldsAllDowncase), TRUE);
+ gtk_widget_set_sensitive(GTK_WIDGET(ProcessFieldsFirstLetterUppercase), TRUE);
+ gtk_widget_set_sensitive(GTK_WIDGET(ProcessFieldsFirstLettersUppercase),TRUE);
+ gtk_widget_set_sensitive(GTK_WIDGET(ProcessFieldsRemoveSpace), TRUE);
+ gtk_widget_set_sensitive(GTK_WIDGET(ProcessFieldsInsertSpace), TRUE);
+ gtk_widget_set_sensitive(GTK_WIDGET(ProcessFieldsOnlyOneSpace), TRUE);
+ }else
+ {
+ gtk_widget_set_sensitive(GTK_WIDGET(ProcessFieldsConvertIntoSpace), FALSE);
+ gtk_widget_set_sensitive(GTK_WIDGET(ProcessFieldsConvertSpace), FALSE);
+ gtk_widget_set_sensitive(GTK_WIDGET(ProcessFieldsConvert), FALSE);
+ gtk_widget_set_sensitive(GTK_WIDGET(ProcessFieldsConvertTo), FALSE);
+ gtk_widget_set_sensitive(GTK_WIDGET(ProcessFieldsConvertLabelTo), FALSE);
+ gtk_widget_set_sensitive(GTK_WIDGET(ProcessFieldsConvertFrom), FALSE);
+ gtk_widget_set_sensitive(GTK_WIDGET(ProcessFieldsAllUppercase), FALSE);
+ gtk_widget_set_sensitive(GTK_WIDGET(ProcessFieldsAllDowncase), FALSE);
+ gtk_widget_set_sensitive(GTK_WIDGET(ProcessFieldsFirstLetterUppercase), FALSE);
+ gtk_widget_set_sensitive(GTK_WIDGET(ProcessFieldsFirstLettersUppercase),FALSE);
+ gtk_widget_set_sensitive(GTK_WIDGET(ProcessFieldsRemoveSpace), FALSE);
+ gtk_widget_set_sensitive(GTK_WIDGET(ProcessFieldsInsertSpace), FALSE);
+ gtk_widget_set_sensitive(GTK_WIDGET(ProcessFieldsOnlyOneSpace), FALSE);
+ }
+}
+
+/*
+ * Callbacks from Mask Editor buttons
+ */
+
+/*
+ * Callback from the mask edit list
+ * Previously known as Mask_Editor_List_Select_Row
+ */
+void Mask_Editor_List_Row_Selected (GtkTreeSelection* selection, gpointer data)
+{
+ GList *selectedRows;
+ gchar *text = NULL;
+ GtkTreePath *lastSelected;
+ GtkTreeIter lastFile;
+ GtkTreeModel *treemodel;
+ gboolean valid;
+
+ treemodel = gtk_tree_view_get_model(GTK_TREE_VIEW(MaskEditorList));
+
+ /* We must block the function, else the previous selected row will be modified */
+ g_signal_handlers_block_by_func(G_OBJECT(MaskEditorEntry),
+ G_CALLBACK(Mask_Editor_Entry_Changed),NULL);
+
+ selectedRows = gtk_tree_selection_get_selected_rows(selection, NULL);
+
+ /*
+ * At some point, we might get called when no rows are selected?
+ */
+ if (g_list_length(selectedRows) == 0)
+ {
+ g_list_foreach(selectedRows, (GFunc) gtk_tree_path_free, NULL);
+ g_list_free(selectedRows);
+ g_signal_handlers_unblock_by_func(G_OBJECT(MaskEditorEntry),
+ G_CALLBACK(Mask_Editor_Entry_Changed),NULL);
+ return;
+ }
+
+ /* Get the text of the last selected row */
+ lastSelected = (GtkTreePath *)g_list_last(selectedRows)->data;
+
+ valid= gtk_tree_model_get_iter(treemodel, &lastFile, lastSelected);
+ if (valid)
+ {
+ gtk_tree_model_get(treemodel, &lastFile, MASK_EDITOR_TEXT, &text, -1);
+
+ if (text)
+ {
+ gtk_entry_set_text(GTK_ENTRY(MaskEditorEntry),text);
+ g_free(text);
+ }
+ }
+
+ g_signal_handlers_unblock_by_func(G_OBJECT(MaskEditorEntry),
+ G_CALLBACK(Mask_Editor_Entry_Changed),NULL);
+}
+
+
+/*
+ * Add a new mask to the list
+ */
+void Mask_Editor_List_New (void)
+{
+ gchar *text = _("New_mask");
+ GtkTreeIter iter;
+ GtkTreeSelection *selection;
+ GtkTreeModel *treemodel;
+
+ if (!MaskEditorList) return;
+
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(MaskEditorList));
+ treemodel = gtk_tree_view_get_model(GTK_TREE_VIEW(MaskEditorList));
+
+ gtk_list_store_insert(GTK_LIST_STORE(treemodel), &iter, 0);
+ gtk_list_store_set(GTK_LIST_STORE(treemodel), &iter, MASK_EDITOR_TEXT, text, -1);
+
+ gtk_tree_selection_unselect_all(selection);
+ gtk_tree_selection_select_iter(selection, &iter);
+}
+
+/*
+ * Duplicate a mask on the list
+ */
+void Mask_Editor_List_Duplicate (void)
+{
+ gchar *text = NULL;
+ GList *selectedRows;
+ GList *toInsert = NULL;
+ GtkTreeSelection *selection;
+ GtkTreeIter row;
+ GtkTreeModel *treemodel;
+ gboolean valid;
+
+ if (!MaskEditorList) return;
+
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(MaskEditorList));
+ selectedRows = gtk_tree_selection_get_selected_rows(selection, NULL);
+ treemodel = gtk_tree_view_get_model(GTK_TREE_VIEW(MaskEditorList));
+
+ if (g_list_length(selectedRows) == 0)
+ {
+ Log_Print(_("Copy: No row selected!"));
+ g_list_foreach(selectedRows, (GFunc) gtk_tree_path_free, NULL);
+ g_list_free(selectedRows);
+ return;
+ }
+
+ /* Loop through selected rows, duplicating them into a GList */
+ /* We cannot directly insert because the paths in selectedRows get out of date after an insertion */
+ while(selectedRows)
+ {
+ valid = gtk_tree_model_get_iter(treemodel, &row, (GtkTreePath*) selectedRows->data);
+ if (valid)
+ {
+ gtk_tree_model_get(treemodel, &row, MASK_EDITOR_TEXT, &text, -1);
+ toInsert = g_list_append(toInsert, text);
+ }
+
+ selectedRows = selectedRows->next;
+ if (!selectedRows) break;
+ }
+
+ /* Duplicate the relevant entries, by looping through the list backwards (to preserve original order) */
+ toInsert = g_list_last(toInsert);
+ while(toInsert)
+ {
+ gtk_list_store_insert(GTK_LIST_STORE(treemodel), &row, 0);
+ gtk_list_store_set(GTK_LIST_STORE(treemodel), &row, MASK_EDITOR_TEXT, (gchar*) toInsert->data, -1);
+ g_free(toInsert->data);
+
+ toInsert = toInsert->prev;
+ if (!toInsert) break;
+ }
+
+ /* Free data no longer needed */
+ selectedRows = g_list_first(selectedRows);
+ toInsert = g_list_first(toInsert);
+ g_list_foreach(selectedRows, (GFunc) gtk_tree_path_free, NULL);
+ g_list_free(selectedRows);
+ g_list_foreach(toInsert, (GFunc) g_free, NULL);
+ g_list_free(toInsert);
+}
+
+void Mask_Editor_List_Add(void)
+{
+ gint i = 0;
+ GtkTreeIter iter;
+ GtkTreeModel *treemodel;
+ gchar *temp;
+
+ treemodel = gtk_tree_view_get_model(GTK_TREE_VIEW(MaskEditorList));
+
+ if (gtk_combo_box_get_active(GTK_COMBO_BOX(ScannerOptionCombo)) == SCANNER_FILL_TAG)
+ {
+ while(Scan_Masks[i])
+ {
+ if (!g_utf8_validate(Scan_Masks[i], -1, NULL))
+ temp = convert_to_utf8(Scan_Masks[i]);
+ else
+ temp = g_strdup(Scan_Masks[i]);
+
+ gtk_list_store_append(GTK_LIST_STORE(treemodel), &iter);
+ gtk_list_store_set(GTK_LIST_STORE(treemodel), &iter,
+ MASK_EDITOR_TEXT, temp, -1);
+ g_free(temp);
+ i++;
+ }
+ } else if (gtk_combo_box_get_active(GTK_COMBO_BOX(ScannerOptionCombo)) == SCANNER_RENAME_FILE)
+ {
+ while(Rename_File_Masks[i])
+ {
+ if (!g_utf8_validate(Rename_File_Masks[i], -1, NULL))
+ temp = convert_to_utf8(Rename_File_Masks[i]);
+ else
+ temp = g_strdup(Scan_Masks[i]);
+
+ gtk_list_store_append(GTK_LIST_STORE(treemodel), &iter);
+ gtk_list_store_set(GTK_LIST_STORE(treemodel), &iter,
+ MASK_EDITOR_TEXT, temp, -1);
+ g_free(temp);
+ i++;
+ }
+ }
+}
+
+/*
+ * Remove the selected rows from the mask editor list
+ */
+void Mask_Editor_List_Remove (void)
+{
+ GtkTreeSelection *selection;
+ GtkTreeIter iter;
+ GtkTreeModel *treemodel;
+
+ if (!MaskEditorList) return;
+
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(MaskEditorList));
+ treemodel = gtk_tree_view_get_model(GTK_TREE_VIEW(MaskEditorList));
+
+ if (gtk_tree_selection_count_selected_rows(selection) == 0) {
+ Log_Print(_("Remove: No row selected!"));
+ return;
+ }
+
+ if (!gtk_tree_model_get_iter_first(treemodel, &iter))
+ return;
+
+ while (TRUE)
+ {
+ if (gtk_tree_selection_iter_is_selected(selection, &iter))
+ {
+ if (!gtk_list_store_remove(GTK_LIST_STORE(treemodel), &iter))
+ {
+ break;
+ }
+ } else
+ {
+ if (!gtk_tree_model_iter_next(treemodel, &iter))
+ {
+ break;
+ }
+ }
+ }
+}
+
+/*
+ * Move all selected rows up one place in the mask list
+ */
+void Mask_Editor_List_Move_Up (void)
+{
+ GtkTreeSelection *selection;
+ GList *selectedRows;
+ GList *selectedRowsCopy;
+ GtkTreeIter currentFile;
+ GtkTreeIter nextFile;
+ GtkTreePath *currentPath;
+ GtkTreeModel *treemodel;
+ gboolean valid;
+
+ if (!MaskEditorList) return;
+
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(MaskEditorList));
+ treemodel = gtk_tree_view_get_model(GTK_TREE_VIEW(MaskEditorList));
+ selectedRows = gtk_tree_selection_get_selected_rows(selection, NULL);
+
+ if (g_list_length(selectedRows) == 0)
+ {
+ Log_Print(_("Move Up: No row selected!"));
+ g_list_foreach(selectedRows, (GFunc)gtk_tree_path_free, NULL);
+ g_list_free(selectedRows);
+ return;
+ }
+
+ selectedRowsCopy = selectedRows;
+
+ while(selectedRows)
+ {
+ currentPath = (GtkTreePath*) selectedRows->data;
+ valid = gtk_tree_model_get_iter(treemodel, &currentFile, currentPath);
+ if (valid)
+ {
+ /* Find the entry above the node... */
+ if (gtk_tree_path_prev(currentPath)) {
+ /* ...and if it exists, swap the two rows by iter */
+ gtk_tree_model_get_iter(treemodel, &nextFile, currentPath);
+ gtk_list_store_swap(GTK_LIST_STORE(treemodel), &currentFile, &nextFile);
+ }
+ }
+
+ selectedRows = selectedRows->next;
+ if (!selectedRows) break;
+ }
+
+ g_list_foreach(selectedRowsCopy, (GFunc)gtk_tree_path_free, NULL);
+ g_list_free(selectedRowsCopy);
+}
+
+/*
+ * Move all selected rows down one place in the mask list
+ */
+void Mask_Editor_List_Move_Down (void)
+{
+ GtkTreeSelection *selection;
+ GList *selectedRows;
+ GList *selectedRowsCopy;
+ GtkTreeIter currentFile;
+ GtkTreeIter nextFile;
+ GtkTreePath *currentPath;
+ GtkTreeModel *treemodel;
+ gboolean valid;
+
+ if (!MaskEditorList) return;
+
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(MaskEditorList));
+ treemodel = gtk_tree_view_get_model(GTK_TREE_VIEW(MaskEditorList));
+ selectedRows = gtk_tree_selection_get_selected_rows(selection, NULL);
+
+ if (g_list_length(selectedRows) == 0) {
+ Log_Print(_("Move Down: No row selected!"));
+ g_list_foreach(selectedRows, (GFunc)gtk_tree_path_free, NULL);
+ g_list_free(selectedRows);
+ return;
+ }
+
+ selectedRowsCopy = selectedRows;
+
+ while (selectedRows)
+ {
+ currentPath = (GtkTreePath*) selectedRows->data;
+ valid = gtk_tree_model_get_iter(treemodel, &currentFile, currentPath);
+ if (valid)
+ {
+ /* Find the entry below the node and swap the two nodes by iter */
+ gtk_tree_path_next(currentPath);
+ gtk_tree_model_get_iter(treemodel, &nextFile, currentPath);
+ gtk_list_store_swap(GTK_LIST_STORE(treemodel), &currentFile, &nextFile);
+ }
+
+ if (!selectedRows->next) break;
+ selectedRows = selectedRows->next;
+ }
+
+ g_list_foreach(selectedRowsCopy, (GFunc)gtk_tree_path_free, NULL);
+ g_list_free(selectedRowsCopy);
+}
+
+/*
+ * Save the currently displayed mask list in the mask editor
+ */
+void Mask_Editor_List_Save_Button (void)
+{
+ Mask_Editor_Clean_Up_Masks_List();
+
+ if (gtk_combo_box_get_active(GTK_COMBO_BOX(ScannerOptionCombo)) == SCANNER_FILL_TAG)
+ {
+ Save_Scan_Tag_Masks_List(ScanTagListModel, MASK_EDITOR_TEXT);
+ } else if (gtk_combo_box_get_active(GTK_COMBO_BOX(ScannerOptionCombo)) == SCANNER_RENAME_FILE)
+ {
+ Save_Rename_File_Masks_List(RenameFileListModel, MASK_EDITOR_TEXT);
+ }
+}
+
+/*
+ * Clean up the currently displayed masks lists, ready for saving
+ */
+void Mask_Editor_Clean_Up_Masks_List (void)
+{
+ gchar *text = NULL;
+ gchar *text1 = NULL;
+ GtkTreeIter currentIter;
+ GtkTreeIter itercopy;
+ GtkTreeModel *treemodel;
+
+ treemodel = gtk_tree_view_get_model(GTK_TREE_VIEW(MaskEditorList));
+
+ /* Remove blank and duplicate items */
+ if (gtk_tree_model_get_iter_first(treemodel, &currentIter))
+ {
+
+ while(TRUE)
+ {
+ gtk_tree_model_get(treemodel, &currentIter, MASK_EDITOR_TEXT, &text, -1);
+
+ /* Check for blank entry */
+ if (text && g_utf8_strlen(text, -1) == 0)
+ {
+ g_free(text);
+
+ if (!gtk_list_store_remove(GTK_LIST_STORE(treemodel), &currentIter))
+ break; /* No following entries */
+ else
+ continue; /* Go on to next entry, which the remove function already moved onto for us */
+ }
+
+ /* Check for duplicate entries */
+ itercopy = currentIter;
+ if (!gtk_tree_model_iter_next(treemodel, &itercopy))
+ {
+ g_free(text);
+ break;
+ }
+
+ while(TRUE)
+ {
+ gtk_tree_model_get(treemodel, &itercopy, MASK_EDITOR_TEXT, &text1, -1);
+ if (text1 && g_utf8_collate(text,text1) == 0)
+ {
+ g_free(text1);
+
+ if (!gtk_list_store_remove(GTK_LIST_STORE(treemodel), &itercopy))
+ break; /* No following entries */
+ else
+ continue; /* Go on to next entry, which the remove function already set iter to for us */
+
+ }
+ g_free(text1);
+ if (!gtk_tree_model_iter_next(treemodel, &itercopy))
+ break;
+ }
+
+ g_free(text);
+
+ if (!gtk_tree_model_iter_next(treemodel, &currentIter))
+ break;
+ }
+ }
+}
+
+/*
+ * Update the Mask List with the new value of the entry box
+ */
+void Mask_Editor_Entry_Changed (void)
+{
+ GtkTreeSelection *selection;
+ GtkTreePath *firstSelected;
+ GtkTreeModel *treemodel;
+ GList *selectedRows;
+ GtkTreeIter row;
+ const gchar* text;
+ gboolean valid;
+
+ if (!MaskEditorList) return;
+
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(MaskEditorList));
+ treemodel = gtk_tree_view_get_model(GTK_TREE_VIEW(MaskEditorList));
+ selectedRows = gtk_tree_selection_get_selected_rows(selection, NULL);
+
+ if (g_list_length(selectedRows) == 0)
+ {
+ g_list_foreach(selectedRows, (GFunc) gtk_tree_path_free, NULL);
+ g_list_free(selectedRows);
+ return;
+ }
+
+ firstSelected = (GtkTreePath *)g_list_first(selectedRows)->data;
+ text = gtk_entry_get_text(GTK_ENTRY(MaskEditorEntry));
+
+ valid = gtk_tree_model_get_iter(treemodel, &row, firstSelected);
+ if (valid)
+ gtk_list_store_set(GTK_LIST_STORE(treemodel), &row, MASK_EDITOR_TEXT, text, -1);
+
+ g_list_foreach(selectedRows, (GFunc) gtk_tree_path_free, NULL);
+ g_list_free(selectedRows);
+}
+
+/*
+ * Actions when the a key is pressed into the masks editor clist
+ */
+gboolean Mask_Editor_List_Key_Press (GtkWidget *widget, GdkEvent *event)
+{
+ if (event && event->type == GDK_KEY_PRESS) {
+ GdkEventKey *kevent = (GdkEventKey *)event;
+
+ switch(kevent->keyval) {
+ case GDK_Delete:
+ Mask_Editor_List_Remove();
+ break;
+/* case GDK_Up:
+ Mask_Editor_Clist_Move_Up();
+ break;
+ case GDK_Down:
+ Mask_Editor_Clist_Move_Down();
+ break;
+*/ }
+ }
+ return TRUE;
+}
+
+/*
+ * Function when you select an item of the option menu
+ */
+void Scanner_Option_Menu_Activate_Item (GtkWidget *combo, gpointer data)
+{
+ switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
+ {
+ case SCANNER_FILL_TAG:
+ gtk_widget_show(MaskEditorButton);
+ gtk_widget_show(LegendButton);
+ gtk_widget_show(ScanTagFrame);
+ gtk_widget_hide(RenameFileFrame);
+ gtk_widget_hide(ProcessFieldsFrame);
+ gtk_tree_view_set_model(GTK_TREE_VIEW(MaskEditorList), GTK_TREE_MODEL(ScanTagListModel));
+ Scan_Fill_Tag_Generate_Preview();
+ g_signal_emit_by_name(G_OBJECT(LegendButton),"toggled"); /* To hide or show legend frame */
+ g_signal_emit_by_name(G_OBJECT(MaskEditorButton),"toggled"); /* To hide or show mask editor frame */
+ break;
+
+ case SCANNER_RENAME_FILE:
+ gtk_widget_show(MaskEditorButton);
+ gtk_widget_show(LegendButton);
+ gtk_widget_hide(ScanTagFrame);
+ gtk_widget_show(RenameFileFrame);
+ gtk_widget_hide(ProcessFieldsFrame);
+ gtk_tree_view_set_model(GTK_TREE_VIEW(MaskEditorList), GTK_TREE_MODEL(RenameFileListModel));
+ Scan_Rename_File_Generate_Preview();
+ g_signal_emit_by_name(G_OBJECT(LegendButton),"toggled"); /* To hide or show legend frame */
+ g_signal_emit_by_name(G_OBJECT(MaskEditorButton),"toggled"); /* To hide or show mask editor frame */
+ break;
+
+ case SCANNER_PROCESS_FIELDS:
+ gtk_widget_hide(MaskEditorButton);
+ gtk_widget_hide(LegendButton);
+ gtk_widget_hide(ScanTagFrame);
+ gtk_widget_hide(RenameFileFrame);
+ gtk_widget_show_all(ProcessFieldsFrame);
+ // Hide directly the frames to don't change state of the buttons!
+ gtk_widget_hide(LegendFrame);
+ gtk_widget_hide(MaskEditorFrame);
+
+ gtk_tree_view_set_model(GTK_TREE_VIEW(MaskEditorList), NULL);
+ break;
+ }
+}
+
+/*
+ * Init the position of the scanner window
+ */
+void Scan_Set_Scanner_Window_Init_Position (void)
+{
+ if (ScannerWindow && SET_SCANNER_WINDOW_POSITION)
+ {
+ gtk_widget_realize(ScannerWindow);
+ gdk_window_move(ScannerWindow->window,SCANNER_WINDOW_X,SCANNER_WINDOW_Y);
+ }
+}
diff --git a/src/scan.h b/src/scan.h
new file mode 100755
index 0000000..3173aa3
--- /dev/null
+++ b/src/scan.h
@@ -0,0 +1,89 @@
+/* scan.h - 2000/06/16 */
+/*
+ * EasyTAG - Tag editor for MP3 and Ogg Vorbis files
+ * Copyright (C) 2000-2003 Jerome Couderc <easytag@gmail.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef __SCAN_H__
+#define __SCAN_H__
+
+
+#include "et_core.h"
+
+/****************
+ * Declarations *
+ ****************/
+GtkWidget *ScannerWindow;
+GtkWidget *SWScanButton; // To enable/disable it in easytag.c
+
+
+enum
+{
+ SCANNER_FILL_TAG = 0,
+ SCANNER_RENAME_FILE,
+ SCANNER_PROCESS_FIELDS
+}; // Add a new item : Min and Max values used in Open_ScannerWindow
+
+enum {
+ MASK_EDITOR_TEXT,
+ MASK_EDITOR_COUNT
+};
+
+
+
+/**************
+ * Prototypes *
+ **************/
+
+void Scan_Tag_With_Mask (ET_File *ETFile);
+void Scan_Rename_File_With_Mask (ET_File *ETFile);
+void Scan_Process_Fields (ET_File *ETFile);
+void Scan_Select_Mode_And_Run_Scanner (ET_File *ETFile);
+gchar *Scan_Generate_New_Filename_From_Mask (ET_File *ETFile, gchar *mask, gboolean no_dir_check_or_conversion);
+GList *Scan_Generate_New_Tag_From_Mask (ET_File *ETFile, gchar *mask);
+gchar *Scan_Generate_New_Directory_Name_From_Mask (ET_File *ETFile, gchar *mask, gboolean no_dir_check_or_conversion);
+void Scan_Rename_File_Generate_Preview (void);
+void Scan_Fill_Tag_Generate_Preview (void);
+void Scan_Rename_Directory_Generate_Preview (void);
+
+void Scan_Use_Fill_Tag_Scanner (void);
+void Scan_Use_Rename_File_Scanner (void);
+void Scan_Use_Process_Fields_Scanner (void);
+
+gboolean Scan_Check_Rename_File_Mask (GtkObject *widget_to_show_hide, GtkEntry *widget_source);
+
+void Scan_Process_Fields_All_Uppercase (gchar *text);
+void Scan_Process_Fields_All_Downcase (gchar *text);
+void Scan_Process_Fields_Letter_Uppercase (gchar *text);
+void Scan_Process_Fields_First_Letters_Uppercase (gchar *text);
+void Scan_Process_Fields_Remove_Space (gchar *text);
+void Scan_Process_Fields_Insert_Space (gchar *text);
+void Scan_Process_Fields_Keep_One_Space (gchar *text);
+
+void Scan_Convert_Underscore_Into_Space (gchar *string);
+void Scan_Convert_P20_Into_Space (gchar *string);
+void Scan_Convert_Space_Into_Undescore (gchar *string);
+void Scan_Convert (gchar *string);
+
+void Init_ScannerWindow (void);
+void Open_ScannerWindow (gint scanner_type);
+void ScannerWindow_Apply_Changes (void);
+
+void Scan_Set_Scanner_Window_Init_Position (void);
+
+#endif /* __SCAN_H__ */
diff --git a/src/setting.c b/src/setting.c
new file mode 100755
index 0000000..8e68c6b
--- /dev/null
+++ b/src/setting.c
@@ -0,0 +1,1616 @@
+/* config.c - 2000/06/21 */
+/*
+ * EasyTAG - Tag editor for MP3 and Ogg Vorbis files
+ * Copyright (C) 2000-2003 Jerome Couderc <easytag@gmail.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+
+#include <gtk/gtk.h>
+#include <glib/gi18n-lib.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <dirent.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <errno.h>
+
+#include "setting.h"
+#include "prefs.h"
+#include "bar.h"
+#include "easytag.h"
+#include "charset.h"
+#include "scan.h"
+#include "log.h"
+#include "misc.h"
+#include "cddb.h"
+#include "browser.h"
+
+#ifdef WIN32
+# include "win32/win32dep.h"
+#endif
+
+
+/***************
+ * Declaration *
+ ***************/
+
+/*
+ * Nota :
+ * - no trailing slashes on directory name to avoid problem with
+ * NetBSD's mkdir(2).
+ */
+
+// Base directory created into home dir
+gchar *EASYTAG_DIR = ".easytag";
+// File for configuration
+gchar *CONFIG_FILE = ".easytag/easytagrc";
+// File of masks for tag scanner
+gchar *SCAN_TAG_MASKS_FILE = ".easytag/scan_tag.mask";
+// File of masks for rename file scanner
+gchar *RENAME_FILE_MASKS_FILE = ".easytag/rename_file.mask";
+// File for history of RenameDirectoryMaskCombo combobox
+gchar *RENAME_DIRECTORY_MASKS_FILE = ".easytag/rename_directory.mask";
+// File for history of PlayListNameCombo combobox
+gchar *PLAY_LIST_NAME_MASKS_FILE = ".easytag/play_list_name.mask";
+// File for history of PlayListContentMaskEntry combobox
+gchar *PLAYLIST_CONTENT_MASKS_FILE = ".easytag/playlist_content.mask";
+// File for history of DefaultPathToMp3 combobox
+gchar *DEFAULT_PATH_TO_MP3_HISTORY_FILE = ".easytag/default_path_to_mp3.history";
+// File for history of DefaultComment combobox
+gchar *DEFAULT_TAG_COMMENT_HISTORY_FILE = ".easytag/default_tag_comment.history";
+// File for history of BrowserEntry combobox
+gchar *PATH_ENTRY_HISTORY_FILE = ".easytag/browser_path.history";
+// File for history of run program combobox for directories
+gchar *RUN_PROGRAM_WITH_DIRECTORY_HISTORY_FILE = ".easytag/run_program_with_directory.history";
+// File for history of run program combobox for files
+gchar *RUN_PROGRAM_WITH_FILE_HISTORY_FILE = ".easytag/run_program_with_file.history";
+// File for history of run player combobox
+gchar *AUDIO_FILE_PLAYER_HISTORY_FILE = ".easytag/audio_file_player.history";
+// File for history of search string combobox
+gchar *SEARCH_FILE_HISTORY_FILE = ".easytag/search_file.history";
+// File for history of FileToLoad combobox
+gchar *FILE_TO_LOAD_HISTORY_FILE = ".easytag/file_to_load.history";
+// File for history of CddbSearchStringEntry combobox
+gchar *CDDB_SEARCH_STRING_HISTORY_FILE = ".easytag/cddb_search_string.history";
+// File for history of CddbSearchStringInResultEntry combobox
+gchar *CDDB_SEARCH_STRING_IN_RESULT_HISTORY_FILE = ".easytag/cddb_search_string_in_result.history";
+// File for history of CddbLocalPath combobox
+gchar *CDDB_LOCAL_PATH_HISTORY_FILE = ".easytag/cddb_local_path.history";
+
+
+
+
+/**************
+ * Prototypes *
+ **************/
+
+
+
+/********************
+ * Config Variables *
+ ********************/
+tConfigVariable Config_Variables[] =
+{
+ {"load_on_startup", CV_TYPE_BOOL, &LOAD_ON_STARTUP },
+ {"default_path_to_mp3", CV_TYPE_STRING, &DEFAULT_PATH_TO_MP3 },
+ {"browser_line_style", CV_TYPE_BOOL, &BROWSER_LINE_STYLE },
+ {"browser_expander_style", CV_TYPE_BOOL, &BROWSER_EXPANDER_STYLE },
+ {"browse_subdir", CV_TYPE_BOOL, &BROWSE_SUBDIR },
+ {"browse_hidden_dir", CV_TYPE_BOOL, &BROWSE_HIDDEN_DIR },
+ {"open_selected_browser_node", CV_TYPE_BOOL, &OPEN_SELECTED_BROWSER_NODE },
+
+ {"set_main_window_position", CV_TYPE_BOOL, &SET_MAIN_WINDOW_POSITION },
+ {"main_window_x", CV_TYPE_INT, &MAIN_WINDOW_X },
+ {"main_window_y", CV_TYPE_INT, &MAIN_WINDOW_Y },
+ {"main_window_height", CV_TYPE_INT, &MAIN_WINDOW_HEIGHT },
+ {"main_window_width", CV_TYPE_INT, &MAIN_WINDOW_WIDTH },
+ {"pane_handle_position1", CV_TYPE_INT, &PANE_HANDLE_POSITION1 },
+ {"pane_handle_position2", CV_TYPE_INT, &PANE_HANDLE_POSITION2 },
+ {"pane_handle_position3", CV_TYPE_INT, &PANE_HANDLE_POSITION3 },
+ {"pane_handle_position4", CV_TYPE_INT, &PANE_HANDLE_POSITION4 },
+ {"show_header_infos", CV_TYPE_BOOL, &SHOW_HEADER_INFO },
+ {"changed_files_displayed_to_red", CV_TYPE_BOOL, &CHANGED_FILES_DISPLAYED_TO_RED },
+ {"changed_files_displayed_to_bold", CV_TYPE_BOOL, &CHANGED_FILES_DISPLAYED_TO_BOLD },
+
+ {"date_auto_completion", CV_TYPE_BOOL, &DATE_AUTO_COMPLETION },
+ {"number_track_formated", CV_TYPE_BOOL, &NUMBER_TRACK_FORMATED },
+ {"number_track_formated_spin_button", CV_TYPE_INT, &NUMBER_TRACK_FORMATED_SPIN_BUTTON },
+ {"ogg_tag_write_xmms_comment", CV_TYPE_BOOL, &OGG_TAG_WRITE_XMMS_COMMENT },
+ {"set_focus_to_same_tag_field", CV_TYPE_BOOL, &SET_FOCUS_TO_SAME_TAG_FIELD },
+ {"set_focus_to_first_tag_field", CV_TYPE_BOOL, &SET_FOCUS_TO_FIRST_TAG_FIELD },
+ {"sorting_file_mode", CV_TYPE_INT, &SORTING_FILE_MODE },
+ {"sorting_file_case_sensitive", CV_TYPE_BOOL, &SORTING_FILE_CASE_SENSITIVE },
+
+ {"replace_illegal_character_in_filename",CV_TYPE_BOOL, &REPLACE_ILLEGAL_CHARACTERS_IN_FILENAME },
+ {"filename_extension_lower_case", CV_TYPE_BOOL, &FILENAME_EXTENSION_LOWER_CASE },
+ {"filename_extension_upper_case", CV_TYPE_BOOL, &FILENAME_EXTENSION_UPPER_CASE },
+ {"filename_extension_no_change", CV_TYPE_BOOL, &FILENAME_EXTENSION_NO_CHANGE },
+ {"preserve_modification_time", CV_TYPE_BOOL, &PRESERVE_MODIFICATION_TIME },
+ {"filename_character_set_other", CV_TYPE_BOOL, &FILENAME_CHARACTER_SET_OTHER },
+ {"filename_character_set_approximate", CV_TYPE_BOOL, &FILENAME_CHARACTER_SET_APPROXIMATE },
+ {"filename_character_set_discard", CV_TYPE_BOOL, &FILENAME_CHARACTER_SET_DISCARD },
+
+ {"write_id3_tags_in_flac_file", CV_TYPE_BOOL, &WRITE_ID3_TAGS_IN_FLAC_FILE },
+ {"strip_tag_when_empty_fields", CV_TYPE_BOOL, &STRIP_TAG_WHEN_EMPTY_FIELDS },
+ {"convert_old_id3v2_tag_version", CV_TYPE_BOOL, &CONVERT_OLD_ID3V2_TAG_VERSION },
+ {"use_non_standard_id3_reading_character_set", CV_TYPE_BOOL, &USE_NON_STANDARD_ID3_READING_CHARACTER_SET},
+ {"file_reading_id3v1v2_character_set", CV_TYPE_STRING,&FILE_READING_ID3V1V2_CHARACTER_SET},
+ {"file_writing_id3v2_write_tag", CV_TYPE_BOOL, &FILE_WRITING_ID3V2_WRITE_TAG },
+ {"file_writing_id3v2_version_4", CV_TYPE_BOOL, &FILE_WRITING_ID3V2_VERSION_4 },
+ {"file_writing_id3v2_use_crc32", CV_TYPE_BOOL, &FILE_WRITING_ID3V2_USE_CRC32 },
+ {"file_writing_id3v2_use_compression", CV_TYPE_BOOL, &FILE_WRITING_ID3V2_USE_COMPRESSION },
+ {"file_writing_id3v2_use_unicode_character_set", CV_TYPE_BOOL, &FILE_WRITING_ID3V2_USE_UNICODE_CHARACTER_SET},
+ {"file_writing_id3v2_unicode_character_set", CV_TYPE_STRING,&FILE_WRITING_ID3V2_UNICODE_CHARACTER_SET},
+ {"file_writing_id3v2_no_unicode_character_set", CV_TYPE_STRING,&FILE_WRITING_ID3V2_NO_UNICODE_CHARACTER_SET},
+ {"file_writing_id3v2_iconv_options_no", CV_TYPE_BOOL, &FILE_WRITING_ID3V2_ICONV_OPTIONS_NO},
+ {"file_writing_id3v2_iconv_options_translit", CV_TYPE_BOOL, &FILE_WRITING_ID3V2_ICONV_OPTIONS_TRANSLIT},
+ {"file_writing_id3v2_iconv_options_ignore", CV_TYPE_BOOL, &FILE_WRITING_ID3V2_ICONV_OPTIONS_IGNORE},
+ {"file_writing_id3v1_write_tag", CV_TYPE_BOOL, &FILE_WRITING_ID3V1_WRITE_TAG },
+ {"file_writing_id3v1_character_set", CV_TYPE_STRING,&FILE_WRITING_ID3V1_CHARACTER_SET},
+ {"file_writing_id3v1_iconv_options_no", CV_TYPE_BOOL, &FILE_WRITING_ID3V1_ICONV_OPTIONS_NO},
+ {"file_writing_id3v1_iconv_options_translit", CV_TYPE_BOOL, &FILE_WRITING_ID3V1_ICONV_OPTIONS_TRANSLIT},
+ {"file_writing_id3v1_iconv_options_ignore", CV_TYPE_BOOL, &FILE_WRITING_ID3V1_ICONV_OPTIONS_IGNORE},
+
+ {"message_box_position_none", CV_TYPE_BOOL, &MESSAGE_BOX_POSITION_NONE },
+ {"message_box_position_center", CV_TYPE_BOOL, &MESSAGE_BOX_POSITION_CENTER },
+ {"message_box_position_mouse", CV_TYPE_BOOL, &MESSAGE_BOX_POSITION_MOUSE },
+ {"message_box_position_center_on_parent", CV_TYPE_BOOL, &MESSAGE_BOX_POSITION_CENTER_ON_PARENT },
+
+ {"audio_file_player", CV_TYPE_STRING,&AUDIO_FILE_PLAYER },
+
+ {"scanner_type", CV_TYPE_INT, &SCANNER_TYPE },
+ {"scan_mask_editor_button", CV_TYPE_BOOL,&SCAN_MASK_EDITOR_BUTTON },
+ {"scan_legend_button", CV_TYPE_BOOL,&SCAN_LEGEND_BUTTON },
+ {"fts_convert_underscore_and_p20_into_space",CV_TYPE_BOOL,&FTS_CONVERT_UNDERSCORE_AND_P20_INTO_SPACE },
+ {"fts_convert_space_into_underscore", CV_TYPE_BOOL,&FTS_CONVERT_SPACE_INTO_UNDERSCORE },
+ {"rfs_convert_underscore_and_p20_into_space",CV_TYPE_BOOL,&RFS_CONVERT_UNDERSCORE_AND_P20_INTO_SPACE },
+ {"rfs_convert_space_into_underscore", CV_TYPE_BOOL,&RFS_CONVERT_SPACE_INTO_UNDERSCORE },
+ {"pfs_dont_upper_some_words", CV_TYPE_BOOL,&PFS_DONT_UPPER_SOME_WORDS },
+ {"overwrite_tag_field", CV_TYPE_BOOL, &OVERWRITE_TAG_FIELD },
+ {"set_default_comment", CV_TYPE_BOOL, &SET_DEFAULT_COMMENT },
+ {"default_comment", CV_TYPE_STRING, &DEFAULT_COMMENT },
+ {"crc32_comment", CV_TYPE_BOOL, &SET_CRC32_COMMENT },
+ {"open_scanner_window_on_startup", CV_TYPE_BOOL, &OPEN_SCANNER_WINDOW_ON_STARTUP },
+ {"scanner_window_on_top", CV_TYPE_BOOL, &SCANNER_WINDOW_ON_TOP },
+ {"set_scanner_window_position", CV_TYPE_BOOL, &SET_SCANNER_WINDOW_POSITION },
+ {"scanner_window_x", CV_TYPE_INT, &SCANNER_WINDOW_X },
+ {"scanner_window_y", CV_TYPE_INT, &SCANNER_WINDOW_Y },
+
+ {"confirm_before_exit", CV_TYPE_BOOL, &CONFIRM_BEFORE_EXIT },
+ {"confirm_write_tag", CV_TYPE_BOOL, &CONFIRM_WRITE_TAG },
+ {"confirm_rename_file", CV_TYPE_BOOL, &CONFIRM_RENAME_FILE },
+ {"confirm_write_playlist", CV_TYPE_BOOL, &CONFIRM_WRITE_PLAYLIST },
+ {"confirm_delete_file", CV_TYPE_BOOL, &CONFIRM_DELETE_FILE },
+ {"process_filename_field", CV_TYPE_BOOL, &PROCESS_FILENAME_FIELD },
+ {"process_title_field", CV_TYPE_BOOL, &PROCESS_TITLE_FIELD },
+ {"process_artist_field", CV_TYPE_BOOL, &PROCESS_ARTIST_FIELD },
+ {"process_album_field", CV_TYPE_BOOL, &PROCESS_ALBUM_FIELD },
+ {"process_genre_field", CV_TYPE_BOOL, &PROCESS_GENRE_FIELD },
+ {"process_comment_field", CV_TYPE_BOOL, &PROCESS_COMMENT_FIELD },
+ {"process_composer_field", CV_TYPE_BOOL, &PROCESS_COMPOSER_FIELD },
+ {"process_orig_artist_field", CV_TYPE_BOOL, &PROCESS_ORIG_ARTIST_FIELD },
+ {"process_copyright_field", CV_TYPE_BOOL, &PROCESS_COPYRIGHT_FIELD },
+ {"process_url_field", CV_TYPE_BOOL, &PROCESS_URL_FIELD },
+ {"process_encoded_by_field", CV_TYPE_BOOL, &PROCESS_ENCODED_BY_FIELD },
+
+ {"pf_convert_into_space", CV_TYPE_BOOL, &PF_CONVERT_INTO_SPACE },
+ {"pf_convert_space", CV_TYPE_BOOL, &PF_CONVERT_SPACE },
+ {"pf_convert", CV_TYPE_BOOL, &PF_CONVERT },
+ {"pf_convert_all_uppercase", CV_TYPE_BOOL, &PF_CONVERT_ALL_UPPERCASE },
+ {"pf_convert_all_downcase", CV_TYPE_BOOL, &PF_CONVERT_ALL_DOWNCASE },
+ {"pf_convert_first_letter_uppercase", CV_TYPE_BOOL, &PF_CONVERT_FIRST_LETTER_UPPERCASE },
+ {"pf_convert_first_letters_uppercase", CV_TYPE_BOOL, &PF_CONVERT_FIRST_LETTERS_UPPERCASE },
+ {"pf_remove_space", CV_TYPE_BOOL, &PF_REMOVE_SPACE },
+ {"pf_insert_space", CV_TYPE_BOOL, &PF_INSERT_SPACE },
+ {"pf_only_one_space", CV_TYPE_BOOL, &PF_ONLY_ONE_SPACE },
+
+ {"playlist_name", CV_TYPE_STRING, &PLAYLIST_NAME },
+ {"playlist_use_mask_name", CV_TYPE_BOOL, &PLAYLIST_USE_MASK_NAME },
+ {"playlist_use_dir_name", CV_TYPE_BOOL, &PLAYLIST_USE_DIR_NAME },
+ {"playlist_only_selected_files", CV_TYPE_BOOL, &PLAYLIST_ONLY_SELECTED_FILES },
+ {"playlist_full_path", CV_TYPE_BOOL, &PLAYLIST_FULL_PATH },
+ {"playlist_relative_path", CV_TYPE_BOOL, &PLAYLIST_RELATIVE_PATH },
+ {"playlist_create_in_parent_dir", CV_TYPE_BOOL, &PLAYLIST_CREATE_IN_PARENT_DIR },
+ {"playlist_use_dos_separator", CV_TYPE_BOOL, &PLAYLIST_USE_DOS_SEPARATOR },
+ {"playlist_content_none", CV_TYPE_BOOL, &PLAYLIST_CONTENT_NONE },
+ {"playlist_content_filename", CV_TYPE_BOOL, &PLAYLIST_CONTENT_FILENAME },
+ {"playlist_content_mask", CV_TYPE_BOOL, &PLAYLIST_CONTENT_MASK },
+ {"playlist_content_mask_value", CV_TYPE_STRING, &PLAYLIST_CONTENT_MASK_VALUE },
+ {"playlist_window_x", CV_TYPE_INT, &PLAYLIST_WINDOW_X },
+ {"playlist_window_y", CV_TYPE_INT, &PLAYLIST_WINDOW_Y },
+ {"playlist_window_width", CV_TYPE_INT, &PLAYLIST_WINDOW_WIDTH },
+ {"playlist_window_height", CV_TYPE_INT, &PLAYLIST_WINDOW_HEIGHT },
+
+ {"load_file_run_scanner", CV_TYPE_BOOL, &LOAD_FILE_RUN_SCANNER },
+ {"load_file_window_x", CV_TYPE_INT, &LOAD_FILE_WINDOW_X },
+ {"load_file_window_y", CV_TYPE_INT, &LOAD_FILE_WINDOW_Y },
+ {"load_file_window_width", CV_TYPE_INT, &LOAD_FILE_WINDOW_WIDTH },
+ {"load_file_window_height", CV_TYPE_INT, &LOAD_FILE_WINDOW_HEIGHT },
+
+ {"cddb_server_name_automatic_search", CV_TYPE_STRING, &CDDB_SERVER_NAME_AUTOMATIC_SEARCH },
+ {"cddb_server_port_automatic_search", CV_TYPE_INT, &CDDB_SERVER_PORT_AUTOMATIC_SEARCH },
+ {"cddb_server_cgi_path_automatic_search", CV_TYPE_STRING, &CDDB_SERVER_CGI_PATH_AUTOMATIC_SEARCH },
+ {"cddb_server_name_automatic_search2", CV_TYPE_STRING, &CDDB_SERVER_NAME_AUTOMATIC_SEARCH2 },
+ {"cddb_server_port_automatic_search2", CV_TYPE_INT, &CDDB_SERVER_PORT_AUTOMATIC_SEARCH2 },
+ {"cddb_server_cgi_path_automatic_search2", CV_TYPE_STRING, &CDDB_SERVER_CGI_PATH_AUTOMATIC_SEARCH2 },
+ {"cddb_server_name_manual_search", CV_TYPE_STRING, &CDDB_SERVER_NAME_MANUAL_SEARCH },
+ {"cddb_server_port_manual_search", CV_TYPE_INT, &CDDB_SERVER_PORT_MANUAL_SEARCH },
+ {"cddb_server_cgi_path_manual_search", CV_TYPE_STRING, &CDDB_SERVER_CGI_PATH_MANUAL_SEARCH },
+ {"cddb_local_path", CV_TYPE_STRING, &CDDB_LOCAL_PATH },
+ {"cddb_use_proxy", CV_TYPE_INT, &CDDB_USE_PROXY },
+ {"cddb_proxy_name", CV_TYPE_STRING, &CDDB_PROXY_NAME },
+ {"cddb_proxy_port", CV_TYPE_INT, &CDDB_PROXY_PORT },
+ {"cddb_proxy_user_name", CV_TYPE_STRING, &CDDB_PROXY_USER_NAME },
+ {"cddb_proxy_user_password", CV_TYPE_STRING, &CDDB_PROXY_USER_PASSWORD },
+ {"set_cddb_window_position", CV_TYPE_BOOL, &SET_CDDB_WINDOW_POSITION },
+ {"cddb_window_x", CV_TYPE_INT, &CDDB_WINDOW_X },
+ {"cddb_window_y", CV_TYPE_INT, &CDDB_WINDOW_Y },
+ {"cddb_window_height", CV_TYPE_INT, &CDDB_WINDOW_HEIGHT },
+ {"cddb_window_width", CV_TYPE_INT, &CDDB_WINDOW_WIDTH },
+ {"cddb_pane_handle_position", CV_TYPE_INT, &CDDB_PANE_HANDLE_POSITION },
+
+ {"cddb_follow_file", CV_TYPE_BOOL, &CDDB_FOLLOW_FILE },
+ {"cddb_use_dlm", CV_TYPE_BOOL, &CDDB_USE_DLM },
+ {"cddb_use_local_access", CV_TYPE_BOOL, &CDDB_USE_LOCAL_ACCESS },
+
+ {"cddb_search_in_all_fields", CV_TYPE_BOOL, &CDDB_SEARCH_IN_ALL_FIELDS },
+ {"cddb_search_in_artist_field", CV_TYPE_BOOL, &CDDB_SEARCH_IN_ARTIST_FIELD },
+ {"cddb_search_in_title_field", CV_TYPE_BOOL, &CDDB_SEARCH_IN_TITLE_FIELD },
+ {"cddb_search_in_track_name_field", CV_TYPE_BOOL, &CDDB_SEARCH_IN_TRACK_NAME_FIELD },
+ {"cddb_search_in_other_field", CV_TYPE_BOOL, &CDDB_SEARCH_IN_OTHER_FIELD },
+ {"cddb_show_categories", CV_TYPE_BOOL, &CDDB_SHOW_CATEGORIES },
+
+ {"cddb_search_in_all_categories", CV_TYPE_BOOL, &CDDB_SEARCH_IN_ALL_CATEGORIES },
+ {"cddb_search_in_blues_categories", CV_TYPE_BOOL, &CDDB_SEARCH_IN_BLUES_CATEGORY },
+ {"cddb_search_in_classical_categories", CV_TYPE_BOOL, &CDDB_SEARCH_IN_CLASSICAL_CATEGORY },
+ {"cddb_search_in_country_categories", CV_TYPE_BOOL, &CDDB_SEARCH_IN_COUNTRY_CATEGORY },
+ {"cddb_search_in_folk_categories", CV_TYPE_BOOL, &CDDB_SEARCH_IN_FOLK_CATEGORY },
+ {"cddb_search_in_jazz_categories", CV_TYPE_BOOL, &CDDB_SEARCH_IN_JAZZ_CATEGORY },
+ {"cddb_search_in_misc_categories", CV_TYPE_BOOL, &CDDB_SEARCH_IN_MISC_CATEGORY },
+ {"cddb_search_in_newage_categories", CV_TYPE_BOOL, &CDDB_SEARCH_IN_NEWAGE_CATEGORY },
+ {"cddb_search_in_reggae_categories", CV_TYPE_BOOL, &CDDB_SEARCH_IN_REGGAE_CATEGORY },
+ {"cddb_search_in_rock_categories", CV_TYPE_BOOL, &CDDB_SEARCH_IN_ROCK_CATEGORY },
+ {"cddb_search_in_soundtrack_categories", CV_TYPE_BOOL, &CDDB_SEARCH_IN_SOUNDTRACK_CATEGORY },
+
+ {"cddb_set_to_all_fields", CV_TYPE_BOOL, &CDDB_SET_TO_ALL_FIELDS },
+ {"cddb_set_to_title", CV_TYPE_BOOL, &CDDB_SET_TO_TITLE },
+ {"cddb_set_to_artist", CV_TYPE_BOOL, &CDDB_SET_TO_ARTIST },
+ {"cddb_set_to_album", CV_TYPE_BOOL, &CDDB_SET_TO_ALBUM },
+ {"cddb_set_to_year", CV_TYPE_BOOL, &CDDB_SET_TO_YEAR },
+ {"cddb_set_to_track", CV_TYPE_BOOL, &CDDB_SET_TO_TRACK },
+ {"cddb_set_to_track_total", CV_TYPE_BOOL, &CDDB_SET_TO_TRACK_TOTAL },
+ {"cddb_set_to_genre", CV_TYPE_BOOL, &CDDB_SET_TO_GENRE },
+ {"cddb_set_to_file_name", CV_TYPE_BOOL, &CDDB_SET_TO_FILE_NAME },
+
+ {"cddb_run_scanner", CV_TYPE_BOOL, &CDDB_RUN_SCANNER },
+
+ {"set_search_window_position", CV_TYPE_BOOL, &SET_SEARCH_WINDOW_POSITION },
+ {"search_window_x", CV_TYPE_INT, &SEARCH_WINDOW_X },
+ {"search_window_y", CV_TYPE_INT, &SEARCH_WINDOW_Y },
+ {"search_window_height", CV_TYPE_INT, &SEARCH_WINDOW_HEIGHT },
+ {"search_window_width", CV_TYPE_INT, &SEARCH_WINDOW_WIDTH },
+ {"search_in_filename", CV_TYPE_BOOL, &SEARCH_IN_FILENAME },
+ {"search_in_tag", CV_TYPE_BOOL, &SEARCH_IN_TAG },
+ {"search_case_sensitive", CV_TYPE_BOOL, &SEARCH_CASE_SENSITIVE },
+
+ {"scan_tag_default_mask", CV_TYPE_STRING, &SCAN_TAG_DEFAULT_MASK },
+ {"rename_file_default_mask", CV_TYPE_STRING, &RENAME_FILE_DEFAULT_MASK },
+ {"rename_directory_default_mask", CV_TYPE_STRING, &RENAME_DIRECTORY_DEFAULT_MASK },
+ {"rename_directory_with_mask", CV_TYPE_BOOL, &RENAME_DIRECTORY_WITH_MASK },
+
+ {"options_notebook_page", CV_TYPE_INT, &OPTIONS_NOTEBOOK_PAGE },
+ {"options_window_height", CV_TYPE_INT, &OPTIONS_WINDOW_HEIGHT },
+ {"options_window_width", CV_TYPE_INT, &OPTIONS_WINDOW_WIDTH }
+
+};
+
+
+
+
+/*************
+ * Functions *
+ *************/
+
+/*
+ * Define and Load default values into config variables
+ */
+void Init_Config_Variables (void)
+{
+
+ /*
+ * Common
+ */
+ LOAD_ON_STARTUP = 1;
+ DEFAULT_PATH_TO_MP3 = g_strdup(HOME_VARIABLE);
+ BROWSE_SUBDIR = 1;
+#ifdef WIN32
+ BROWSE_HIDDEN_DIR = 1;
+#else
+ BROWSE_HIDDEN_DIR = 0;
+#endif
+ OPEN_SELECTED_BROWSER_NODE = 1;
+
+ /*
+ * Misc
+ */
+ SET_MAIN_WINDOW_POSITION = 1; // Set it to '0' if problem with some Windows Manager
+ MAIN_WINDOW_X = -1; // '-1' lets the Windows Manager to place the window
+ MAIN_WINDOW_Y = -1;
+ MAIN_WINDOW_WIDTH = 1040;
+ MAIN_WINDOW_HEIGHT = -1;
+ PANE_HANDLE_POSITION1 = 660;
+ PANE_HANDLE_POSITION2 = 360;
+ PANE_HANDLE_POSITION3 = 300;
+ PANE_HANDLE_POSITION4 = 300;
+ SHOW_HEADER_INFO = 1;
+ CHANGED_FILES_DISPLAYED_TO_RED = 1;
+ CHANGED_FILES_DISPLAYED_TO_BOLD = 0;
+
+ DATE_AUTO_COMPLETION = 1;
+ NUMBER_TRACK_FORMATED = 1;
+ NUMBER_TRACK_FORMATED_SPIN_BUTTON = 2;
+ OGG_TAG_WRITE_XMMS_COMMENT = 1;
+ SET_FOCUS_TO_SAME_TAG_FIELD = 1;
+ SET_FOCUS_TO_FIRST_TAG_FIELD = 0;
+ SORTING_FILE_MODE = SORTING_BY_ASCENDING_FILENAME;
+#ifdef WIN32
+ SORTING_FILE_CASE_SENSITIVE = 1;
+#else
+ SORTING_FILE_CASE_SENSITIVE = 0;
+#endif
+
+ MESSAGE_BOX_POSITION_NONE = 0;
+ MESSAGE_BOX_POSITION_CENTER = 0;
+ MESSAGE_BOX_POSITION_MOUSE = 0;
+ MESSAGE_BOX_POSITION_CENTER_ON_PARENT = 1;
+
+#ifdef WIN32
+ AUDIO_FILE_PLAYER = ET_Win32_Get_Audio_File_Player();
+#else
+ AUDIO_FILE_PLAYER = g_strdup("xmms -p");
+#endif
+
+ /*
+ * File Settings
+ */
+ REPLACE_ILLEGAL_CHARACTERS_IN_FILENAME = 1;
+ FILENAME_EXTENSION_LOWER_CASE = 1;
+ FILENAME_EXTENSION_UPPER_CASE = 0;
+ FILENAME_EXTENSION_NO_CHANGE = 0;
+ PRESERVE_MODIFICATION_TIME = 1;
+
+ FILENAME_CHARACTER_SET_OTHER = 1;
+ FILENAME_CHARACTER_SET_APPROXIMATE = 0;
+ FILENAME_CHARACTER_SET_DISCARD = 0;
+
+ /*
+ * Tag Settings
+ */
+ WRITE_ID3_TAGS_IN_FLAC_FILE = 0;
+ STRIP_TAG_WHEN_EMPTY_FIELDS = 1;
+ CONVERT_OLD_ID3V2_TAG_VERSION = 1;
+ USE_NON_STANDARD_ID3_READING_CHARACTER_SET = 0;
+ FILE_READING_ID3V1V2_CHARACTER_SET = g_strdup("UTF-8");
+ FILE_WRITING_ID3V2_WRITE_TAG = 1;
+ FILE_WRITING_ID3V2_VERSION_4 = 1;
+ FILE_WRITING_ID3V2_USE_CRC32 = 0;
+ FILE_WRITING_ID3V2_USE_COMPRESSION = 0;
+ FILE_WRITING_ID3V2_USE_UNICODE_CHARACTER_SET = 1;
+ FILE_WRITING_ID3V2_UNICODE_CHARACTER_SET = g_strdup("UTF-8");
+ FILE_WRITING_ID3V2_NO_UNICODE_CHARACTER_SET = g_strdup("ISO-8859-1");
+ FILE_WRITING_ID3V2_ICONV_OPTIONS_NO = 1;
+ FILE_WRITING_ID3V2_ICONV_OPTIONS_TRANSLIT = 0;
+ FILE_WRITING_ID3V2_ICONV_OPTIONS_IGNORE = 0;
+ FILE_WRITING_ID3V1_WRITE_TAG = 1;
+ FILE_WRITING_ID3V1_CHARACTER_SET = g_strdup("ISO-8859-1");
+ FILE_WRITING_ID3V1_ICONV_OPTIONS_NO = 1;
+ FILE_WRITING_ID3V1_ICONV_OPTIONS_TRANSLIT = 0;
+ FILE_WRITING_ID3V1_ICONV_OPTIONS_IGNORE = 0;
+
+ /*
+ * Scanner
+ */
+ SCANNER_TYPE = SCANNER_FILL_TAG;
+ SCAN_MASK_EDITOR_BUTTON = 0;
+ SCAN_LEGEND_BUTTON = 0;
+ FTS_CONVERT_UNDERSCORE_AND_P20_INTO_SPACE = 1;
+ FTS_CONVERT_SPACE_INTO_UNDERSCORE = 0;
+ RFS_CONVERT_UNDERSCORE_AND_P20_INTO_SPACE = 1;
+ RFS_CONVERT_SPACE_INTO_UNDERSCORE = 0;
+ PFS_DONT_UPPER_SOME_WORDS = 0;
+ OVERWRITE_TAG_FIELD = 1;
+ SET_DEFAULT_COMMENT = 0;
+ DEFAULT_COMMENT = g_strdup("Tagged with EasyTAG");
+ SET_CRC32_COMMENT = 0;
+ OPEN_SCANNER_WINDOW_ON_STARTUP = 0;
+ SCANNER_WINDOW_ON_TOP = 1;
+ SET_SCANNER_WINDOW_POSITION = 1; // Set it to '0' if problem with some Windows Manager
+ SCANNER_WINDOW_X = -1;
+ SCANNER_WINDOW_Y = -1;
+
+ /*
+ * Confirmation
+ */
+ CONFIRM_BEFORE_EXIT = 1;
+ CONFIRM_WRITE_TAG = 1;
+ CONFIRM_RENAME_FILE = 1;
+ CONFIRM_DELETE_FILE = 1;
+ CONFIRM_WRITE_PLAYLIST = 1;
+
+ /*
+ * Scanner window
+ */
+ PROCESS_FILENAME_FIELD = 0;
+ PROCESS_TITLE_FIELD = 1;
+ PROCESS_ARTIST_FIELD = 1;
+ PROCESS_ALBUM_FIELD = 1;
+ PROCESS_GENRE_FIELD = 1;
+ PROCESS_COMMENT_FIELD = 1;
+ PROCESS_COMPOSER_FIELD = 1;
+ PROCESS_ORIG_ARTIST_FIELD = 1;
+ PROCESS_COPYRIGHT_FIELD = 1;
+ PROCESS_URL_FIELD = 1;
+ PROCESS_ENCODED_BY_FIELD = 1;
+
+ PF_CONVERT_INTO_SPACE = 1;
+ PF_CONVERT_SPACE = 0;
+ PF_CONVERT = 0;
+ PF_CONVERT_ALL_UPPERCASE = 0;
+ PF_CONVERT_ALL_DOWNCASE = 0;
+ PF_CONVERT_FIRST_LETTER_UPPERCASE = 0;
+ PF_CONVERT_FIRST_LETTERS_UPPERCASE = 1;
+ PF_REMOVE_SPACE = 0;
+ PF_INSERT_SPACE = 0;
+ PF_ONLY_ONE_SPACE = 1;
+
+ /*
+ * Playlist window
+ */
+ PLAYLIST_NAME = g_strdup("playlist_%a_-_%b");
+ PLAYLIST_USE_MASK_NAME = 0;
+ PLAYLIST_USE_DIR_NAME = 1;
+ PLAYLIST_ONLY_SELECTED_FILES = 1;
+ PLAYLIST_FULL_PATH = 0;
+ PLAYLIST_RELATIVE_PATH = 1;
+ PLAYLIST_CREATE_IN_PARENT_DIR = 0;
+ PLAYLIST_USE_DOS_SEPARATOR = 0;
+ PLAYLIST_CONTENT_NONE = 0;
+ PLAYLIST_CONTENT_FILENAME = 1;
+ PLAYLIST_CONTENT_MASK = 0;
+ PLAYLIST_CONTENT_MASK_VALUE = g_strdup("%n/%l - %a - %b - %t");
+
+ PLAYLIST_WINDOW_X = -1;
+ PLAYLIST_WINDOW_Y = -1;
+ PLAYLIST_WINDOW_WIDTH = -1;
+ PLAYLIST_WINDOW_HEIGHT = -1;
+
+ /*
+ * Load File window
+ */
+ LOAD_FILE_RUN_SCANNER = 0;
+ LOAD_FILE_WINDOW_X = -1;
+ LOAD_FILE_WINDOW_Y = -1;
+ LOAD_FILE_WINDOW_WIDTH = -1;
+ LOAD_FILE_WINDOW_HEIGHT = -1;
+
+ /*
+ * CDDB window
+ */
+ CDDB_SERVER_NAME_AUTOMATIC_SEARCH = g_strdup("freedb.freedb.org");
+ CDDB_SERVER_PORT_AUTOMATIC_SEARCH = 80;
+ CDDB_SERVER_CGI_PATH_AUTOMATIC_SEARCH = g_strdup("/~cddb/cddb.cgi");
+ CDDB_SERVER_NAME_AUTOMATIC_SEARCH2 = g_strdup("freedb.musicbrainz.org");
+ CDDB_SERVER_PORT_AUTOMATIC_SEARCH2 = 80;
+ CDDB_SERVER_CGI_PATH_AUTOMATIC_SEARCH2 = g_strdup("/~cddb/cddb.cgi");
+ CDDB_SERVER_NAME_MANUAL_SEARCH = g_strdup("www.gnudb.org");
+ CDDB_SERVER_PORT_MANUAL_SEARCH = 80;
+ CDDB_SERVER_CGI_PATH_MANUAL_SEARCH = g_strdup("/~cddb/cddb.cgi");
+ CDDB_LOCAL_PATH = NULL;
+ CDDB_USE_PROXY = 0;
+ CDDB_PROXY_NAME = g_strdup("localhost");
+ CDDB_PROXY_PORT = 8080;
+ CDDB_PROXY_USER_NAME = NULL;
+ CDDB_PROXY_USER_PASSWORD = NULL;
+
+ SET_CDDB_WINDOW_POSITION = 1; // Set it to '0' if problem with some Windows Manager
+ CDDB_WINDOW_X = -1;
+ CDDB_WINDOW_Y = -1;
+ CDDB_WINDOW_WIDTH = 660;
+ CDDB_WINDOW_HEIGHT = 470;
+ CDDB_PANE_HANDLE_POSITION = 350;
+
+ CDDB_FOLLOW_FILE = 1;
+ CDDB_USE_DLM = 0;
+ CDDB_USE_LOCAL_ACCESS = 0;
+
+ CDDB_SEARCH_IN_ALL_FIELDS = 0;
+ CDDB_SEARCH_IN_ARTIST_FIELD = 1;
+ CDDB_SEARCH_IN_TITLE_FIELD = 1;
+ CDDB_SEARCH_IN_TRACK_NAME_FIELD = 0;
+ CDDB_SEARCH_IN_OTHER_FIELD = 0;
+ CDDB_SHOW_CATEGORIES = 0;
+
+ CDDB_SEARCH_IN_ALL_CATEGORIES = 1;
+ CDDB_SEARCH_IN_BLUES_CATEGORY = 0;
+ CDDB_SEARCH_IN_CLASSICAL_CATEGORY = 0;
+ CDDB_SEARCH_IN_COUNTRY_CATEGORY = 0;
+ CDDB_SEARCH_IN_FOLK_CATEGORY = 0;
+ CDDB_SEARCH_IN_JAZZ_CATEGORY = 0;
+ CDDB_SEARCH_IN_MISC_CATEGORY = 1;
+ CDDB_SEARCH_IN_NEWAGE_CATEGORY = 1;
+ CDDB_SEARCH_IN_REGGAE_CATEGORY = 0;
+ CDDB_SEARCH_IN_ROCK_CATEGORY = 1;
+ CDDB_SEARCH_IN_SOUNDTRACK_CATEGORY = 0;
+
+ CDDB_SET_TO_ALL_FIELDS = 1;
+ CDDB_SET_TO_TITLE = 1;
+ CDDB_SET_TO_ARTIST = 0;
+ CDDB_SET_TO_ALBUM = 0;
+ CDDB_SET_TO_YEAR = 0;
+ CDDB_SET_TO_TRACK = 1;
+ CDDB_SET_TO_TRACK_TOTAL = 1;
+ CDDB_SET_TO_GENRE = 0;
+ CDDB_SET_TO_FILE_NAME = 1;
+
+ CDDB_RUN_SCANNER = 0;
+
+ /*
+ * Search window
+ */
+ SET_SEARCH_WINDOW_POSITION = 1; // Set it to '0' if problem with some Windows Manager
+ SEARCH_WINDOW_X = -1;
+ SEARCH_WINDOW_Y = -1;
+ SEARCH_WINDOW_HEIGHT = 350;
+ SEARCH_WINDOW_WIDTH = 650;
+ SEARCH_IN_FILENAME = 1;
+ SEARCH_IN_TAG = 1;
+ SEARCH_CASE_SENSITIVE = 0;
+
+ /*
+ * Masks
+ */
+ SCAN_TAG_DEFAULT_MASK = NULL;
+ RENAME_FILE_DEFAULT_MASK = NULL;
+ RENAME_DIRECTORY_DEFAULT_MASK = NULL;
+ RENAME_DIRECTORY_WITH_MASK = 0;
+
+ /*
+ * Other parameters
+ */
+ OPTIONS_NOTEBOOK_PAGE = 0;
+ OPTIONS_WINDOW_HEIGHT = 300;
+ OPTIONS_WINDOW_WIDTH = 400;
+
+}
+
+
+
+/*
+ * Function called when pressing the "Save" button of the preferences window.
+ * Save into the config variables the settings of each tab of the Preferences window...
+ * If settings needs to be "shown/applied" to the corresponding window, we do it
+ */
+void Apply_Changes_Of_Preferences_Window (void)
+{
+ gchar *temp;
+ int active;
+
+ if (OptionsWindow)
+ {
+ /* Common */
+ LOAD_ON_STARTUP = GTK_TOGGLE_BUTTON(LoadOnStartup)->active;
+ if (DEFAULT_PATH_TO_MP3) g_free(DEFAULT_PATH_TO_MP3);
+ DEFAULT_PATH_TO_MP3 = g_strdup(gtk_entry_get_text(GTK_ENTRY(GTK_BIN(DefaultPathToMp3)->child))); // Saved in UTF-8
+//#ifdef WIN32
+// ET_Win32_Path_Replace_Backslashes(DEFAULT_PATH_TO_MP3);
+//#endif
+ BROWSE_SUBDIR = GTK_TOGGLE_BUTTON(BrowseSubdir)->active;
+ BROWSE_HIDDEN_DIR = GTK_TOGGLE_BUTTON(BrowseHiddendir)->active;
+ OPEN_SELECTED_BROWSER_NODE = GTK_TOGGLE_BUTTON(OpenSelectedBrowserNode)->active;
+
+ /* User interface */
+ SHOW_HEADER_INFO = GTK_TOGGLE_BUTTON(ShowHeaderInfos)->active;
+ // We reload the list if the selected style have changed
+ if (CHANGED_FILES_DISPLAYED_TO_RED != GTK_TOGGLE_BUTTON(ChangedFilesDisplayedToRed)->active)
+ {
+ CHANGED_FILES_DISPLAYED_TO_RED = GTK_TOGGLE_BUTTON(ChangedFilesDisplayedToRed)->active;
+ CHANGED_FILES_DISPLAYED_TO_BOLD = GTK_TOGGLE_BUTTON(ChangedFilesDisplayedToBold)->active;
+ Browser_List_Refresh_Whole_List();
+ }
+
+ /* Misc */
+ DATE_AUTO_COMPLETION = GTK_TOGGLE_BUTTON(DateAutoCompletion)->active;
+ NUMBER_TRACK_FORMATED = GTK_TOGGLE_BUTTON(NumberTrackFormated)->active;
+ NUMBER_TRACK_FORMATED_SPIN_BUTTON = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(NumberTrackFormatedSpinButton));
+ OGG_TAG_WRITE_XMMS_COMMENT = GTK_TOGGLE_BUTTON(OggTagWriteXmmsComment)->active;
+ SORTING_FILE_CASE_SENSITIVE = GTK_TOGGLE_BUTTON(SortingFileCaseSensitive)->active;
+ SET_FOCUS_TO_SAME_TAG_FIELD = GTK_TOGGLE_BUTTON(SetFocusToSameTagField)->active;
+ SET_FOCUS_TO_FIRST_TAG_FIELD = GTK_TOGGLE_BUTTON(SetFocusToFirstTagField)->active;
+
+ SORTING_FILE_MODE = gtk_combo_box_get_active(GTK_COMBO_BOX(SortingFileCombo));
+
+ MESSAGE_BOX_POSITION_NONE = GTK_TOGGLE_BUTTON(MessageBoxPositionNone)->active;
+ MESSAGE_BOX_POSITION_CENTER = GTK_TOGGLE_BUTTON(MessageBoxPositionCenter)->active;
+ MESSAGE_BOX_POSITION_MOUSE = GTK_TOGGLE_BUTTON(MessageBoxPositionMouse)->active;
+ MESSAGE_BOX_POSITION_CENTER_ON_PARENT = GTK_TOGGLE_BUTTON(MessageBoxPositionCenterOnParent)->active;
+
+ if (AUDIO_FILE_PLAYER) g_free(AUDIO_FILE_PLAYER);
+ AUDIO_FILE_PLAYER = g_strdup(gtk_entry_get_text(GTK_ENTRY(GTK_BIN(FilePlayerCombo)->child)));
+
+ /* File Settings */
+ REPLACE_ILLEGAL_CHARACTERS_IN_FILENAME = GTK_TOGGLE_BUTTON(ReplaceIllegalCharactersInFilename)->active;
+ FILENAME_EXTENSION_LOWER_CASE = GTK_TOGGLE_BUTTON(FilenameExtensionLowerCase)->active;
+ FILENAME_EXTENSION_UPPER_CASE = GTK_TOGGLE_BUTTON(FilenameExtensionUpperCase)->active;
+ FILENAME_EXTENSION_NO_CHANGE = GTK_TOGGLE_BUTTON(FilenameExtensionNoChange)->active;
+ PRESERVE_MODIFICATION_TIME = GTK_TOGGLE_BUTTON(PreserveModificationTime)->active;
+
+ FILENAME_CHARACTER_SET_OTHER = GTK_TOGGLE_BUTTON(FilenameCharacterSetOther)->active;
+ FILENAME_CHARACTER_SET_APPROXIMATE = GTK_TOGGLE_BUTTON(FilenameCharacterSetApproximate)->active;
+ FILENAME_CHARACTER_SET_DISCARD = GTK_TOGGLE_BUTTON(FilenameCharacterSetDiscard)->active;
+
+ /* Tag Settings */
+ WRITE_ID3_TAGS_IN_FLAC_FILE = GTK_TOGGLE_BUTTON(WriteId3TagsInFlacFiles)->active;
+ STRIP_TAG_WHEN_EMPTY_FIELDS = GTK_TOGGLE_BUTTON(StripTagWhenEmptyFields)->active;
+ CONVERT_OLD_ID3V2_TAG_VERSION = GTK_TOGGLE_BUTTON(ConvertOldId3v2TagVersion)->active;
+ USE_NON_STANDARD_ID3_READING_CHARACTER_SET = GTK_TOGGLE_BUTTON(UseNonStandardId3ReadingCharacterSet)->active;
+
+#ifdef ENABLE_ID3LIB
+ active = gtk_combo_box_get_active(GTK_COMBO_BOX(FileWritingId3v2VersionCombo));
+ FILE_WRITING_ID3V2_VERSION_4 = !active;
+#else
+ FILE_WRITING_ID3V2_VERSION_4 = 1;
+#endif
+ temp = Get_Active_Combo_Box_Item(GTK_COMBO_BOX(FileReadingId3v1v2CharacterSetCombo));
+ FILE_READING_ID3V1V2_CHARACTER_SET = Charset_Get_Name_From_Title(temp);
+ g_free(temp);
+
+ FILE_WRITING_ID3V2_WRITE_TAG = GTK_TOGGLE_BUTTON(FileWritingId3v2WriteTag)->active;
+ FILE_WRITING_ID3V2_USE_CRC32 = GTK_TOGGLE_BUTTON(FileWritingId3v2UseCrc32)->active;
+ FILE_WRITING_ID3V2_USE_COMPRESSION = GTK_TOGGLE_BUTTON(FileWritingId3v2UseCompression)->active;
+ FILE_WRITING_ID3V2_USE_UNICODE_CHARACTER_SET = GTK_TOGGLE_BUTTON(FileWritingId3v2UseUnicodeCharacterSet)->active;
+
+ active = gtk_combo_box_get_active(GTK_COMBO_BOX(FileWritingId3v2UnicodeCharacterSetCombo));
+ FILE_WRITING_ID3V2_UNICODE_CHARACTER_SET = (active == 1) ? "UTF-16" : "UTF-8";
+
+ temp = Get_Active_Combo_Box_Item(GTK_COMBO_BOX(FileWritingId3v2NoUnicodeCharacterSetCombo));
+ FILE_WRITING_ID3V2_NO_UNICODE_CHARACTER_SET = Charset_Get_Name_From_Title(temp);
+ g_free(temp);
+
+ FILE_WRITING_ID3V2_ICONV_OPTIONS_NO = GTK_TOGGLE_BUTTON(FileWritingId3v2IconvOptionsNo)->active;
+ FILE_WRITING_ID3V2_ICONV_OPTIONS_TRANSLIT = GTK_TOGGLE_BUTTON(FileWritingId3v2IconvOptionsTranslit)->active;
+ FILE_WRITING_ID3V2_ICONV_OPTIONS_IGNORE = GTK_TOGGLE_BUTTON(FileWritingId3v2IconvOptionsIgnore)->active;
+
+ FILE_WRITING_ID3V1_WRITE_TAG = GTK_TOGGLE_BUTTON(FileWritingId3v1WriteTag)->active;
+ temp = Get_Active_Combo_Box_Item(GTK_COMBO_BOX(FileWritingId3v1CharacterSetCombo));
+ FILE_WRITING_ID3V1_CHARACTER_SET = Charset_Get_Name_From_Title(temp);
+ g_free(temp);
+
+ FILE_WRITING_ID3V1_ICONV_OPTIONS_NO = GTK_TOGGLE_BUTTON(FileWritingId3v1IconvOptionsNo)->active;
+ FILE_WRITING_ID3V1_ICONV_OPTIONS_TRANSLIT = GTK_TOGGLE_BUTTON(FileWritingId3v1IconvOptionsTranslit)->active;
+ FILE_WRITING_ID3V1_ICONV_OPTIONS_IGNORE = GTK_TOGGLE_BUTTON(FileWritingId3v1IconvOptionsIgnore)->active;
+
+ /* Scanner */
+ // Fill Tag Scanner
+ FTS_CONVERT_UNDERSCORE_AND_P20_INTO_SPACE = GTK_TOGGLE_BUTTON(FTSConvertUnderscoreAndP20IntoSpace)->active;
+ FTS_CONVERT_SPACE_INTO_UNDERSCORE = GTK_TOGGLE_BUTTON(FTSConvertSpaceIntoUnderscore)->active;
+ // Rename File Scanner
+ RFS_CONVERT_UNDERSCORE_AND_P20_INTO_SPACE = GTK_TOGGLE_BUTTON(RFSConvertUnderscoreAndP20IntoSpace)->active;
+ RFS_CONVERT_SPACE_INTO_UNDERSCORE = GTK_TOGGLE_BUTTON(RFSConvertSpaceIntoUnderscore)->active;
+ // Process File Scanner
+ PFS_DONT_UPPER_SOME_WORDS = GTK_TOGGLE_BUTTON(PFSDontUpperSomeWords)->active;
+
+ OVERWRITE_TAG_FIELD = GTK_TOGGLE_BUTTON(OverwriteTagField)->active;
+ SET_DEFAULT_COMMENT = GTK_TOGGLE_BUTTON(SetDefaultComment)->active;
+ if (DEFAULT_COMMENT) g_free(DEFAULT_COMMENT);
+ DEFAULT_COMMENT = g_strdup(gtk_entry_get_text(GTK_ENTRY(GTK_BIN(DefaultComment)->child)));
+ SET_CRC32_COMMENT = GTK_TOGGLE_BUTTON(Crc32Comment)->active;
+
+ OPEN_SCANNER_WINDOW_ON_STARTUP = GTK_TOGGLE_BUTTON(OpenScannerWindowOnStartup)->active;
+ SCANNER_WINDOW_ON_TOP = GTK_TOGGLE_BUTTON(ScannerWindowOnTop)->active;
+
+ /* CDDB */
+ if (CDDB_SERVER_NAME_AUTOMATIC_SEARCH) g_free(CDDB_SERVER_NAME_AUTOMATIC_SEARCH);
+ CDDB_SERVER_NAME_AUTOMATIC_SEARCH = g_strdup(gtk_entry_get_text(GTK_ENTRY(GTK_BIN(CddbServerNameAutomaticSearch)->child)));
+ CDDB_SERVER_PORT_AUTOMATIC_SEARCH = atoi(gtk_entry_get_text(GTK_ENTRY(CddbServerPortAutomaticSearch)));
+ if (CDDB_SERVER_CGI_PATH_AUTOMATIC_SEARCH) g_free(CDDB_SERVER_CGI_PATH_AUTOMATIC_SEARCH);
+ CDDB_SERVER_CGI_PATH_AUTOMATIC_SEARCH = g_strdup(gtk_entry_get_text(GTK_ENTRY(CddbServerCgiPathAutomaticSearch)));
+
+ if (CDDB_SERVER_NAME_AUTOMATIC_SEARCH2) g_free(CDDB_SERVER_NAME_AUTOMATIC_SEARCH2);
+ CDDB_SERVER_NAME_AUTOMATIC_SEARCH2 = g_strdup(gtk_entry_get_text(GTK_ENTRY(GTK_BIN(CddbServerNameAutomaticSearch2)->child)));
+ CDDB_SERVER_PORT_AUTOMATIC_SEARCH2 = atoi(gtk_entry_get_text(GTK_ENTRY(CddbServerPortAutomaticSearch2)));
+ if (CDDB_SERVER_CGI_PATH_AUTOMATIC_SEARCH2) g_free(CDDB_SERVER_CGI_PATH_AUTOMATIC_SEARCH2);
+ CDDB_SERVER_CGI_PATH_AUTOMATIC_SEARCH2 = g_strdup(gtk_entry_get_text(GTK_ENTRY(CddbServerCgiPathAutomaticSearch2)));
+
+ if (CDDB_SERVER_NAME_MANUAL_SEARCH) g_free(CDDB_SERVER_NAME_MANUAL_SEARCH);
+ CDDB_SERVER_NAME_MANUAL_SEARCH = g_strdup(gtk_entry_get_text(GTK_ENTRY(GTK_BIN(CddbServerNameManualSearch)->child)));
+ CDDB_SERVER_PORT_MANUAL_SEARCH = atoi(gtk_entry_get_text(GTK_ENTRY(CddbServerPortManualSearch)));
+ if (CDDB_SERVER_CGI_PATH_MANUAL_SEARCH) g_free(CDDB_SERVER_CGI_PATH_MANUAL_SEARCH);
+ CDDB_SERVER_CGI_PATH_MANUAL_SEARCH = g_strdup(gtk_entry_get_text(GTK_ENTRY(CddbServerCgiPathManualSearch)));
+
+ if (CDDB_LOCAL_PATH) g_free(CDDB_LOCAL_PATH);
+ CDDB_LOCAL_PATH = g_strdup(gtk_entry_get_text(GTK_ENTRY(GTK_BIN(CddbLocalPath)->child)));
+
+ CDDB_USE_PROXY = GTK_TOGGLE_BUTTON(CddbUseProxy)->active;
+ if (CDDB_PROXY_NAME) g_free(CDDB_PROXY_NAME);
+ CDDB_PROXY_NAME = g_strdup(gtk_entry_get_text(GTK_ENTRY(CddbProxyName)));
+ CDDB_PROXY_PORT = atoi(gtk_entry_get_text(GTK_ENTRY(CddbProxyPort)));
+ if (CDDB_PROXY_USER_NAME) g_free(CDDB_PROXY_USER_NAME);
+ CDDB_PROXY_USER_NAME = g_strdup(gtk_entry_get_text(GTK_ENTRY(CddbProxyUserName)));
+ if (CDDB_PROXY_USER_PASSWORD) g_free(CDDB_PROXY_USER_PASSWORD);
+ CDDB_PROXY_USER_PASSWORD = g_strdup(gtk_entry_get_text(GTK_ENTRY(CddbProxyUserPassword)));
+
+ CDDB_FOLLOW_FILE = GTK_TOGGLE_BUTTON(CddbFollowFile)->active;
+ CDDB_USE_DLM = GTK_TOGGLE_BUTTON(CddbUseDLM)->active;
+
+ /* Confirmation */
+ CONFIRM_BEFORE_EXIT = GTK_TOGGLE_BUTTON(ConfirmBeforeExit)->active;
+ CONFIRM_WRITE_TAG = GTK_TOGGLE_BUTTON(ConfirmWriteTag)->active;
+ CONFIRM_RENAME_FILE = GTK_TOGGLE_BUTTON(ConfirmRenameFile)->active;
+ CONFIRM_DELETE_FILE = GTK_TOGGLE_BUTTON(ConfirmDeleteFile)->active;
+ CONFIRM_WRITE_PLAYLIST = GTK_TOGGLE_BUTTON(ConfirmWritePlayList)->active;
+
+ /* Parameters and variables of Scanner Window are in "scan.c" file */
+ /* Parameters and variables of Cddb Window are in "cddb.c" file */
+ }
+
+ /*
+ * Changes to apply to :
+ */
+ if (MainWindow)
+ {
+ if (SHOW_HEADER_INFO) gtk_widget_show_all(HeaderInfosTable);
+ else gtk_widget_hide_all(HeaderInfosTable);
+
+ /* Update state of check-menu-item into main/popup menu to browse subdirs */
+ Check_Menu_Item_Update_Browse_Subdir();
+
+ /* Update state of check-menu-item into main/popup menu to show hidden directories */
+ Check_Menu_Item_Update_Browse_Hidden_Dir();
+
+ /* Reload if number of character changed for track list */
+ //Load_Track_List_To_UI();
+
+ /* Reload directory, in case we have changed BROWSE_HIDDEN_DIR */
+ // FIX ME : commented as it reloads files...
+ //Browser_Tree_Rebuild(NULL);
+ }
+
+ if (ScannerWindow)
+ {
+ if (SCANNER_WINDOW_ON_TOP)
+ gtk_window_set_transient_for(GTK_WINDOW(ScannerWindow),GTK_WINDOW(MainWindow));
+ else
+ gtk_window_set_transient_for(GTK_WINDOW(ScannerWindow),NULL);
+ }
+
+}
+
+/*
+ * Save into the config variables the settings of each window
+ * - Position/size of the window
+ * - Specific options in the window
+ */
+void Apply_Changes_Of_UI (void)
+{
+ /*
+ * Changes in user interface
+ */
+
+ // Configuration of the main window (see easytag.c) - Function also called when destroying the window
+ MainWindow_Apply_Changes();
+
+ // Configuration of the preference window (see prefs.c) - Function also called when destroying the window
+ OptionsWindow_Apply_Changes();
+
+ // Configuration of the scanner window (see scan.c) - Function also called when destroying the window
+ ScannerWindow_Apply_Changes();
+
+ // Configuration of the cddb window (see cddb.c) - Function also called when destroying the window
+ Cddb_Window_Apply_Changes();
+
+ // Configuration of the playlist window (see misc.c) - Function also called when destroying the window
+ Write_Playlist_Window_Apply_Changes();
+
+ // Configuration of the search_file window (see misc.c) - Function also called when destroying the window
+ Search_File_Window_Apply_Changes();
+
+ // Configuration of the load_filename window (see misc.c) - Function also called when destroying the window
+ Load_Filename_Window_Apply_Changes();
+
+}
+
+void Save_Changes_Of_UI (void)
+{
+ Apply_Changes_Of_UI();
+ Save_Config_To_File();
+}
+
+void Save_Changes_Of_Preferences_Window (void)
+{
+ Apply_Changes_Of_Preferences_Window();
+ Save_Config_To_File();
+
+ Statusbar_Message(_("Configuration saved"),TRUE);
+}
+
+
+
+/*
+ * Write the config file
+ */
+void Save_Config_To_File (void)
+{
+ gchar *file_path = NULL;
+ FILE *file;
+
+ /* The file to write */
+ if (!HOME_VARIABLE) return;
+ file_path = g_strconcat(HOME_VARIABLE,
+ HOME_VARIABLE[strlen(HOME_VARIABLE)-1]!=G_DIR_SEPARATOR ? G_DIR_SEPARATOR_S : "",
+ CONFIG_FILE,NULL);
+
+ if ( Create_Easytag_Directory()==0 || (file=fopen(file_path,"w+"))==0 )
+ {
+ Log_Print(_("ERROR: Can't write config file: %s (%s)"),file_path,g_strerror(errno));
+ }else
+ {
+ gint ConfigVarListLen = sizeof(Config_Variables)/sizeof(tConfigVariable);
+ gint i;
+ gchar *data = NULL;
+
+ for (i=0; i<ConfigVarListLen; i++)
+ {
+ switch (Config_Variables[i].type)
+ {
+ case CV_TYPE_INT:
+ {
+ data = g_strdup_printf("%s=%i\n",Config_Variables[i].name,
+ *(int *)Config_Variables[i].pointer);
+ fwrite(data,strlen(data),1,file);
+ //g_print("# (type:%d) %s",Config_Variables[i].type,data);
+ g_free(data);
+ break;
+ }
+ case CV_TYPE_BOOL:
+ {
+ data = g_strdup_printf("%s=%i\n",Config_Variables[i].name,
+ ( *(int *)Config_Variables[i].pointer ? 1 : 0 ));
+ fwrite(data,strlen(data),1,file);
+ //g_print("# (type:%d) %s",Config_Variables[i].type,data);
+ g_free(data);
+ break;
+ }
+ case CV_TYPE_STRING:
+ {
+ /* Doesn't write datum if empty */
+ if ( (*(char **)Config_Variables[i].pointer)==NULL ) break;
+
+ data = g_strdup_printf("%s=%s\n",Config_Variables[i].name,
+ *(char **)Config_Variables[i].pointer);
+ fwrite(data,strlen(data),1,file);
+ //g_print("# (type:%d) %s",Config_Variables[i].type,data);
+ g_free(data);
+ break;
+ }
+ default:
+ {
+ Log_Print("ERROR: Can't save: type of config variable not supported "
+ "for '%s'!",Config_Variables[i].name);
+ break;
+ }
+ }
+ }
+ fclose(file);
+ }
+ g_free(file_path);
+
+ //Display_Config();
+}
+
+
+/*
+ * Parse lines read (line as <var_description>=<value>) and load the values
+ * into the corresponding config variables.
+ */
+void Set_Config (gchar *line)
+{
+ gchar *var_descriptor;
+ gchar *var_value;
+ gint ConfigVarListLen;
+ gint i;
+
+ if (*line=='\n' || *line=='#') return;
+
+ /* Cut string */
+ var_descriptor = (gchar*)strtok(line,"=");
+ var_value = (gchar*)strtok(NULL,"=");
+ //g_print("\nstr1:'%s',\t str2:'%s'",var_descriptor,var_value);
+
+ ConfigVarListLen = sizeof(Config_Variables)/sizeof(tConfigVariable);
+ for (i=0; i<ConfigVarListLen; i++)
+ {
+ if (Config_Variables[i].name!=NULL && var_descriptor
+ && !strcmp(Config_Variables[i].name,var_descriptor))
+ {
+ switch (Config_Variables[i].type)
+ {
+ case CV_TYPE_INT:
+ {
+ *(int *)Config_Variables[i].pointer = strtol(var_value, NULL, 10);
+ break;
+ }
+
+ case CV_TYPE_BOOL:
+ {
+ if (strtol(var_value, NULL, 10))
+ *(int *)Config_Variables[i].pointer = 1;
+ else
+ *(int *)Config_Variables[i].pointer = 0;
+ break;
+ }
+
+ case CV_TYPE_STRING:
+ {
+ if (!var_value)
+ {
+ *(char **)Config_Variables[i].pointer = NULL;
+ //g_print("\nConfig File Warning: Field of '%s' has no value!\n",var_descriptor);
+ } else
+ {
+ if ( *(char **)Config_Variables[i].pointer != NULL )
+ g_free(*(char **)Config_Variables[i].pointer);
+ *(char **)Config_Variables[i].pointer = g_malloc(strlen(var_value)+1);
+ strcpy( *(char **)Config_Variables[i].pointer,var_value );
+ }
+ break;
+ }
+
+ default:
+ {
+ Log_Print("ERROR: Can't read: type of config variable not supported "
+ "for '%s'!",Config_Variables[i].name);
+ break;
+ }
+ }
+ }
+ }
+}
+
+
+/*
+ * Read config from config file
+ */
+void Read_Config (void)
+{
+ gchar *file_path = NULL;
+ FILE *file;
+ gchar buffer[MAX_STRING_LEN];
+
+ /* The file to read */
+ if (!HOME_VARIABLE) return;
+ file_path = g_strconcat(HOME_VARIABLE,
+ HOME_VARIABLE[strlen(HOME_VARIABLE)-1]!=G_DIR_SEPARATOR?G_DIR_SEPARATOR_S:"",
+ CONFIG_FILE,NULL);
+
+ if ( (file=fopen(file_path,"r"))==0 )
+ {
+ Log_Print(_("Can't open configuration file '%s' (%s)"),file_path,g_strerror(errno));
+ Log_Print(_("Loading default configuration..."));
+ }else
+ {
+ while (fgets(buffer,sizeof(buffer),file))
+ {
+ if (buffer[strlen(buffer)-1]=='\n')
+ buffer[strlen(buffer)-1]='\0';
+ Set_Config(buffer);
+ }
+ fclose(file);
+
+ // Force this configuration! - Disabled as it is boring for russian people
+ //USE_ISO_8859_1_CHARACTER_SET_TRANSLATION = 1;
+ //USE_CHARACTER_SET_TRANSLATION = 0;
+ }
+ g_free(file_path);
+}
+
+
+/*
+ * Display values in config variables
+ * For debuging only!
+ */
+void Display_Config (void)
+{
+ gchar *file_path = NULL;
+ FILE *file;
+
+ /* The file to write */
+ if (!HOME_VARIABLE) return;
+ file_path = g_strconcat(HOME_VARIABLE,
+ HOME_VARIABLE[strlen(HOME_VARIABLE)-1]!=G_DIR_SEPARATOR?G_DIR_SEPARATOR_S:"",
+ CONFIG_FILE,NULL);
+
+ if ( (file=fopen(file_path,"r"))==0 )
+ {
+ g_print(_("Can't open configuration file '%s' (%s)"),file_path,g_strerror(errno));
+ }else
+ {
+ gint ConfigVarListLen = sizeof(Config_Variables)/sizeof(tConfigVariable);
+ gint i;
+
+ g_print("\n## Current Config ##");
+ for (i=0; i<ConfigVarListLen; i++)
+ {
+ switch(Config_Variables[i].type)
+ {
+ case CV_TYPE_INT:
+ case CV_TYPE_BOOL:
+ {
+ g_print("\n%d: %s=%d",i,Config_Variables[i].name,
+ *(int*)Config_Variables[i].pointer);
+ break;
+ }
+ case CV_TYPE_STRING:
+ {
+ g_print("\n%d: %s=%s",i,Config_Variables[i].name,
+ *(char**)Config_Variables[i].pointer);
+ break;
+ }
+ default:
+ {
+ g_print("NOT IMPLEMENTED (Save_Config)!! \n\a");
+ break;
+ }
+ }
+ }
+ g_print("\n## End Current Config ##\n");
+ fclose(file);
+ }
+ g_free(file_path);
+}
+
+
+
+
+/*
+ * Create the main directory with empty history files
+ */
+gboolean Setting_Create_Files (void)
+{
+ gchar *home_path = NULL;
+ gchar *file_path = NULL;
+ FILE *file;
+
+ /* The file to write */
+ if (!HOME_VARIABLE)
+ return FALSE;
+
+ if ( Create_Easytag_Directory()==FALSE )
+ return FALSE;
+
+ home_path = g_strconcat(HOME_VARIABLE,
+ HOME_VARIABLE[strlen(HOME_VARIABLE)-1]!=G_DIR_SEPARATOR?G_DIR_SEPARATOR_S:"",
+ NULL);
+
+ file_path = g_strconcat(home_path,CONFIG_FILE,NULL);
+ if ( (file=fopen(file_path,"a+")) != NULL )
+ fclose(file);
+ else
+ Log_Print(_("Can't create or open file '%s' (%s)"),CONFIG_FILE,g_strerror(errno));
+ g_free(file_path);
+
+ file_path = g_strconcat(home_path,SCAN_TAG_MASKS_FILE,NULL);
+ if ( (file=fopen(file_path,"a+")) != NULL )
+ fclose(file);
+ else
+ Log_Print(_("Can't create or open file '%s' (%s)"),SCAN_TAG_MASKS_FILE,g_strerror(errno));
+ g_free(file_path);
+
+ file_path = g_strconcat(home_path,RENAME_FILE_MASKS_FILE,NULL);
+ if ( (file=fopen(file_path,"a+")) != NULL )
+ fclose(file);
+ else
+ Log_Print(_("Can't create or open file '%s' (%s)"),RENAME_FILE_MASKS_FILE,g_strerror(errno));
+ g_free(file_path);
+
+ file_path = g_strconcat(home_path,RENAME_DIRECTORY_MASKS_FILE,NULL);
+ if ( (file=fopen(file_path,"a+")) != NULL )
+ fclose(file);
+ else
+ Log_Print(_("Can't create or open file '%s' (%s)"),RENAME_DIRECTORY_MASKS_FILE,g_strerror(errno));
+ g_free(file_path);
+
+ file_path = g_strconcat(home_path,DEFAULT_PATH_TO_MP3_HISTORY_FILE,NULL);
+ if ( (file=fopen(file_path,"a+")) != NULL )
+ fclose(file);
+ else
+ Log_Print(_("Can't create or open file '%s' (%s)"),DEFAULT_PATH_TO_MP3_HISTORY_FILE,g_strerror(errno));
+ g_free(file_path);
+
+ file_path = g_strconcat(home_path,DEFAULT_TAG_COMMENT_HISTORY_FILE,NULL);
+ if ( (file=fopen(file_path,"a+")) != NULL )
+ fclose(file);
+ else
+ Log_Print(_("Can't create or open file '%s' (%s)"),DEFAULT_TAG_COMMENT_HISTORY_FILE,g_strerror(errno));
+ g_free(file_path);
+
+ file_path = g_strconcat(home_path,PATH_ENTRY_HISTORY_FILE,NULL);
+ if ( (file=fopen(file_path,"a+")) != NULL )
+ fclose(file);
+ else
+ Log_Print(_("Can't create or open file '%s' (%s)"),PATH_ENTRY_HISTORY_FILE,g_strerror(errno));
+ g_free(file_path);
+
+ file_path = g_strconcat(home_path,PLAY_LIST_NAME_MASKS_FILE,NULL);
+ if ( (file=fopen(file_path,"a+")) != NULL )
+ fclose(file);
+ else
+ Log_Print(_("Can't create or open file '%s' (%s)"),PLAY_LIST_NAME_MASKS_FILE,g_strerror(errno));
+ g_free(file_path);
+
+ file_path = g_strconcat(home_path,RUN_PROGRAM_WITH_DIRECTORY_HISTORY_FILE,NULL);
+ if ( (file=fopen(file_path,"a+")) != NULL )
+ fclose(file);
+ else
+ Log_Print(_("Can't create or open file '%s' (%s)"),RUN_PROGRAM_WITH_DIRECTORY_HISTORY_FILE,g_strerror(errno));
+ g_free(file_path);
+
+ file_path = g_strconcat(home_path,RUN_PROGRAM_WITH_FILE_HISTORY_FILE,NULL);
+ if ( (file=fopen(file_path,"a+")) != NULL )
+ fclose(file);
+ else
+ Log_Print(_("Can't create or open file '%s' (%s)"),RUN_PROGRAM_WITH_FILE_HISTORY_FILE,g_strerror(errno));
+ g_free(file_path);
+
+ file_path = g_strconcat(home_path,AUDIO_FILE_PLAYER_HISTORY_FILE,NULL);
+ if ( (file=fopen(file_path,"a+")) != NULL )
+ fclose(file);
+ else
+ Log_Print(_("Can't create or open file '%s' (%s)"),AUDIO_FILE_PLAYER_HISTORY_FILE,g_strerror(errno));
+ g_free(file_path);
+
+ file_path = g_strconcat(home_path,SEARCH_FILE_HISTORY_FILE,NULL);
+ if ( (file=fopen(file_path,"a+")) != NULL )
+ fclose(file);
+ else
+ Log_Print(_("Can't create or open file '%s' (%s)"),SEARCH_FILE_HISTORY_FILE,g_strerror(errno));
+ g_free(file_path);
+
+ file_path = g_strconcat(home_path,FILE_TO_LOAD_HISTORY_FILE,NULL);
+ if ( (file=fopen(file_path,"a+")) != NULL )
+ fclose(file);
+ else
+ Log_Print(_("Can't create or open file '%s' (%s)"),FILE_TO_LOAD_HISTORY_FILE,g_strerror(errno));
+ g_free(file_path);
+
+ file_path = g_strconcat(home_path,PLAYLIST_CONTENT_MASKS_FILE,NULL);
+ if ( (file=fopen(file_path,"a+")) != NULL )
+ fclose(file);
+ else
+ Log_Print(_("Can't create or open file '%s' (%s)"),PLAYLIST_CONTENT_MASKS_FILE,g_strerror(errno));
+ g_free(file_path);
+
+ file_path = g_strconcat(home_path,CDDB_SEARCH_STRING_HISTORY_FILE,NULL);
+ if ( (file=fopen(file_path,"a+")) != NULL )
+ fclose(file);
+ else
+ Log_Print(_("Can't create or open file '%s' (%s)"),CDDB_SEARCH_STRING_HISTORY_FILE,g_strerror(errno));
+ g_free(file_path);
+
+ file_path = g_strconcat(home_path,CDDB_SEARCH_STRING_IN_RESULT_HISTORY_FILE,NULL);
+ if ( (file=fopen(file_path,"a+")) != NULL )
+ fclose(file);
+ else
+ Log_Print(_("Can't create or open file '%s' (%s)"),CDDB_SEARCH_STRING_IN_RESULT_HISTORY_FILE,g_strerror(errno));
+ g_free(file_path);
+
+ file_path = g_strconcat(home_path,CDDB_LOCAL_PATH_HISTORY_FILE,NULL);
+ if ( (file=fopen(file_path,"a+")) != NULL )
+ fclose(file);
+ else
+ Log_Print(_("Can't create or open file '%s' (%s)"),CDDB_LOCAL_PATH_HISTORY_FILE,g_strerror(errno));
+ g_free(file_path);
+
+
+ g_free(home_path);
+
+ return TRUE;
+}
+
+
+
+/*
+ * Save the contents of a list store to a file
+ */
+void Save_List_Store_To_File (gchar *filename, GtkListStore *liststore, gint colnum)
+{
+ gchar *file_path = NULL;
+ FILE *file;
+ gchar *data = NULL;
+ gchar *text;
+ GtkTreeIter iter;
+
+ if (!gtk_tree_model_get_iter_first(GTK_TREE_MODEL(liststore), &iter))
+ return;
+
+ /* The file to write */
+ if (!HOME_VARIABLE) return;
+ file_path = g_strconcat(HOME_VARIABLE,
+ HOME_VARIABLE[strlen(HOME_VARIABLE)-1]!=G_DIR_SEPARATOR?G_DIR_SEPARATOR_S:"",
+ filename,NULL);
+
+ if ( Create_Easytag_Directory()==0 || (file=fopen(file_path,"w+"))==NULL )
+ {
+ Log_Print(_("ERROR: Can't write list to file: %s (%s)"),file_path,g_strerror(errno));
+ }else
+ {
+ do
+ {
+ gtk_tree_model_get(GTK_TREE_MODEL(liststore), &iter, colnum, &text, -1);
+ data = g_strdup_printf("%s\n",text);
+ g_free(text);
+
+ if (data)
+ {
+ fwrite(data,strlen(data),1,file);
+ g_free(data);
+ }
+ } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(liststore), &iter));
+ fclose(file);
+ }
+ g_free(file_path);
+}
+
+/*
+ * Populate a list store with data from a file passed in as first parameter
+ */
+gboolean Populate_List_Store_From_File (gchar *filename, GtkListStore *liststore, gint text_column)
+{
+
+ gchar *file_path = NULL;
+ FILE *file;
+ gchar buffer[MAX_STRING_LEN];
+ GtkTreeIter iter;
+ gboolean entries_set = FALSE;
+
+ /* The file to write */
+ if (!filename || !HOME_VARIABLE) return FALSE;
+ file_path = g_strconcat(HOME_VARIABLE,
+ HOME_VARIABLE[strlen(HOME_VARIABLE)-1]!=G_DIR_SEPARATOR?G_DIR_SEPARATOR_S:"",
+ filename,NULL);
+
+ if ( (file=fopen(file_path,"r"))==NULL )
+ {
+ Log_Print(_("Can't open file '%s' (%s)"),file_path,g_strerror(errno));
+ }else
+ {
+ gchar *data = NULL;
+
+ while(fgets(buffer,sizeof(buffer),file))
+ {
+ if (buffer[strlen(buffer)-1]=='\n')
+ buffer[strlen(buffer)-1]='\0';
+ if (g_utf8_validate(buffer, -1, NULL))
+ data = g_strdup(buffer);
+ else
+ data = convert_to_utf8(buffer);
+
+ if (data && g_utf8_strlen(data, -1) > 0)
+ {
+ gtk_list_store_append(liststore, &iter);
+ gtk_list_store_set(liststore, &iter, text_column, data, -1);
+ entries_set = TRUE;
+ }
+ g_free(data);
+ }
+ fclose(file);
+ }
+ g_free(file_path);
+ return entries_set;
+}
+
+
+/*
+ * Functions for writing and reading list of 'Fill Tag' masks
+ */
+void Load_Scan_Tag_Masks_List (GtkListStore *liststore, gint colnum, gchar **fallback)
+{
+ gint i = 0;
+ GtkTreeIter iter;
+
+ if (!Populate_List_Store_From_File(SCAN_TAG_MASKS_FILE, liststore, colnum))
+ {
+ // Fall back to defaults
+ Log_Print(_("Loading default 'Fill Tag' masks..."));
+
+ while(fallback[i])
+ {
+ gtk_list_store_append(liststore, &iter);
+ gtk_list_store_set(liststore, &iter, colnum, fallback[i], -1);
+ i++;
+ }
+ }
+}
+
+void Save_Scan_Tag_Masks_List (GtkListStore *liststore, gint colnum)
+{
+ Save_List_Store_To_File(SCAN_TAG_MASKS_FILE, liststore, colnum);
+}
+
+
+/*
+ * Functions for writing and reading list of 'Rename File' masks
+ */
+void Load_Rename_File_Masks_List (GtkListStore *liststore, gint colnum, gchar **fallback)
+{
+ gint i = 0;
+ GtkTreeIter iter;
+
+ if (!Populate_List_Store_From_File(RENAME_FILE_MASKS_FILE, liststore, colnum))
+ {
+ // Fall back to defaults
+ Log_Print(_("Loading default 'Rename File' masks..."));
+
+ while(fallback[i])
+ {
+ gtk_list_store_append(liststore, &iter);
+ gtk_list_store_set(liststore, &iter, colnum, fallback[i], -1);
+ i++;
+ }
+ }
+}
+
+void Save_Rename_File_Masks_List (GtkListStore *liststore, gint colnum)
+{
+ Save_List_Store_To_File(RENAME_FILE_MASKS_FILE, liststore, colnum);
+}
+
+/*
+ * Functions for writing and reading list of 'Rename Directory' masks
+ */
+void Load_Rename_Directory_Masks_List (GtkListStore *liststore, gint colnum, gchar **fallback)
+{
+ gint i = 0;
+ GtkTreeIter iter;
+
+ if (!Populate_List_Store_From_File(RENAME_DIRECTORY_MASKS_FILE, liststore, colnum))
+ {
+ // Fall back to defaults
+ Log_Print(_("Loading default 'Rename Directory' masks..."));
+
+ while(fallback[i])
+ {
+ gtk_list_store_append(liststore, &iter);
+ gtk_list_store_set(liststore, &iter, colnum, fallback[i], -1);
+ i++;
+ }
+ }
+}
+
+void Save_Rename_Directory_Masks_List (GtkListStore *liststore, gint colnum)
+{
+ Save_List_Store_To_File(RENAME_DIRECTORY_MASKS_FILE, liststore, colnum);
+}
+
+
+
+
+/*
+ * Functions for writing and reading list of 'DefaultPathToMp3' combobox
+ */
+void Load_Default_Path_To_MP3_List (GtkListStore *liststore, gint colnum)
+{
+ Populate_List_Store_From_File(DEFAULT_PATH_TO_MP3_HISTORY_FILE, liststore, colnum);
+}
+void Save_Default_Path_To_MP3_List (GtkListStore *liststore, gint colnum)
+{
+ Save_List_Store_To_File(DEFAULT_PATH_TO_MP3_HISTORY_FILE, liststore, colnum);
+}
+
+/*
+ * Functions for writing and reading list of 'DefaultComment' combobox
+ */
+void Load_Default_Tag_Comment_Text_List (GtkListStore *liststore, gint colnum)
+{
+ Populate_List_Store_From_File(DEFAULT_TAG_COMMENT_HISTORY_FILE, liststore, colnum);
+}
+void Save_Default_Tag_Comment_Text_List (GtkListStore *liststore, gint colnum)
+{
+ Save_List_Store_To_File(DEFAULT_TAG_COMMENT_HISTORY_FILE, liststore, colnum);
+}
+
+/*
+ * Functions for writing and reading list of 'BrowserEntry' combobox
+ */
+void Load_Path_Entry_List (GtkListStore *liststore, gint colnum)
+{
+ Populate_List_Store_From_File(PATH_ENTRY_HISTORY_FILE, liststore, colnum);
+}
+void Save_Path_Entry_List (GtkListStore *liststore, gint colnum)
+{
+ Save_List_Store_To_File(PATH_ENTRY_HISTORY_FILE, liststore, colnum);
+}
+
+/*
+ * Functions for writing and reading list of 'PlayListNameCombo' combobox
+ */
+void Load_Play_List_Name_List (GtkListStore *liststore, gint colnum)
+{
+ Populate_List_Store_From_File(PLAY_LIST_NAME_MASKS_FILE, liststore, colnum);
+}
+void Save_Play_List_Name_List (GtkListStore *liststore, gint colnum)
+{
+ Save_List_Store_To_File(PLAY_LIST_NAME_MASKS_FILE, liststore, colnum);
+}
+
+/*
+ * Functions for writing and reading list of combobox to run program (tree browser)
+ */
+void Load_Run_Program_With_Directory_List (GtkListStore *liststore, gint colnum)
+{
+ Populate_List_Store_From_File(RUN_PROGRAM_WITH_DIRECTORY_HISTORY_FILE, liststore, colnum);
+}
+void Save_Run_Program_With_Directory_List (GtkListStore *liststore, gint colnum)
+{
+ Save_List_Store_To_File(RUN_PROGRAM_WITH_DIRECTORY_HISTORY_FILE, liststore, colnum);
+}
+
+/*
+ * Functions for writing and reading list of combobox to run program (file browser)
+ */
+void Load_Run_Program_With_File_List (GtkListStore *liststore, gint colnum)
+{
+ Populate_List_Store_From_File(RUN_PROGRAM_WITH_FILE_HISTORY_FILE, liststore, colnum);
+}
+void Save_Run_Program_With_File_List (GtkListStore *liststore, gint colnum)
+{
+ Save_List_Store_To_File(RUN_PROGRAM_WITH_FILE_HISTORY_FILE, liststore, colnum);
+}
+
+/*
+ * Functions for writing and reading list of combobox to run file audio player
+ */
+void Load_Audio_File_Player_List (GtkListStore *liststore, gint colnum)
+{
+ Populate_List_Store_From_File(AUDIO_FILE_PLAYER_HISTORY_FILE, liststore, colnum);
+}
+void Save_Audio_File_Player_List (GtkListStore *liststore, gint colnum)
+{
+ Save_List_Store_To_File(AUDIO_FILE_PLAYER_HISTORY_FILE, liststore, colnum);
+}
+
+/*
+ * Functions for writing and reading list of combobox to search a string into file (tag or filename)
+ */
+void Load_Search_File_List (GtkListStore *liststore, gint colnum)
+{
+ Populate_List_Store_From_File(SEARCH_FILE_HISTORY_FILE, liststore, colnum);
+}
+void Save_Search_File_List (GtkListStore *liststore, gint colnum)
+{
+ Save_List_Store_To_File(SEARCH_FILE_HISTORY_FILE, liststore, colnum);
+}
+
+/*
+ * Functions for writing and reading list of combobox of path of file to load to rename files
+ */
+void Load_File_To_Load_List (GtkListStore *liststore, gint colnum)
+{
+ Populate_List_Store_From_File(FILE_TO_LOAD_HISTORY_FILE, liststore, colnum);
+}
+void Save_File_To_Load_List (GtkListStore *liststore, gint colnum)
+{
+ Save_List_Store_To_File(FILE_TO_LOAD_HISTORY_FILE, liststore, colnum);
+}
+
+/*
+ * Functions for writing and reading list of combobox of playlist content
+ */
+void Load_Playlist_Content_Mask_List (GtkListStore *liststore, gint colnum)
+{
+ Populate_List_Store_From_File(PLAYLIST_CONTENT_MASKS_FILE, liststore, colnum);
+}
+void Save_Playlist_Content_Mask_List (GtkListStore *liststore, gint colnum)
+{
+ Save_List_Store_To_File(PLAYLIST_CONTENT_MASKS_FILE, liststore, colnum);
+}
+
+/*
+ * Functions for writing and reading list of combobox of cddb search string
+ */
+void Load_Cddb_Search_String_List (GtkListStore *liststore, gint colnum)
+{
+ Populate_List_Store_From_File(CDDB_SEARCH_STRING_HISTORY_FILE, liststore, colnum);
+}
+void Save_Cddb_Search_String_List (GtkListStore *liststore, gint colnum)
+{
+ Save_List_Store_To_File(CDDB_SEARCH_STRING_HISTORY_FILE, liststore, colnum);
+}
+
+/*
+ * Functions for writing and reading list of combobox of cddb search string in result list
+ */
+void Load_Cddb_Search_String_In_Result_List (GtkListStore *liststore, gint colnum)
+{
+ Populate_List_Store_From_File(CDDB_SEARCH_STRING_IN_RESULT_HISTORY_FILE, liststore, colnum);
+}
+void Save_Cddb_Search_String_In_Result_List (GtkListStore *liststore, gint colnum)
+{
+ Save_List_Store_To_File(CDDB_SEARCH_STRING_IN_RESULT_HISTORY_FILE, liststore, colnum);
+}
+
+/*
+ * Functions for writing and reading list of 'CddbLocalPath3' combobox
+ */
+void Load_Cddb_Local_Path_List (GtkListStore *liststore, gint colnum)
+{
+ Populate_List_Store_From_File(CDDB_LOCAL_PATH_HISTORY_FILE, liststore, colnum);
+}
+void Save_Cddb_Local_Path_List (GtkListStore *liststore, gint colnum)
+{
+ Save_List_Store_To_File(CDDB_LOCAL_PATH_HISTORY_FILE, liststore, colnum);
+}
+
+
+
+
+
+
+
+/*
+ * Create the directory used by EasyTAG to store files for each user.
+ * If the directory already exists, does nothing and returns 1.
+ * If unable to create the directory, returns 0.
+ */
+gboolean Create_Easytag_Directory (void)
+{
+ gchar *easytag_path = NULL;
+ DIR *dir;
+
+ if (!HOME_VARIABLE)
+ {
+ Log_Print(_("ERROR: The environment variable HOME is not defined!"));
+ return FALSE;
+ }
+
+ /* Directory to create (if doesn't exists) with absolute path
+ * Note for NetBSD : avoid passing a trailing slash to mkdir() */
+ easytag_path = g_strconcat(HOME_VARIABLE,
+ HOME_VARIABLE[strlen(HOME_VARIABLE)-1]!=G_DIR_SEPARATOR?G_DIR_SEPARATOR_S:"",
+ EASYTAG_DIR,
+ //EASYTAG_DIR[strlen(EASYTAG_DIR)-1]!=G_DIR_SEPARATOR?G_DIR_SEPARATOR_S:"",
+ NULL);
+
+ if ( (dir=opendir(easytag_path)) == NULL )
+ {
+ if ( (mkdir(easytag_path,S_IRWXU|S_IXGRP|S_IRGRP)) == -1)
+ {
+ Log_Print(_("ERROR: Can't create directory '%s' (%s)!"),easytag_path,g_strerror(errno));
+ return FALSE;
+ }
+ }else
+ {
+ closedir(dir);
+ }
+
+ g_free(easytag_path);
+
+ return TRUE;
+}
diff --git a/src/setting.h b/src/setting.h
new file mode 100755
index 0000000..7a47a2d
--- /dev/null
+++ b/src/setting.h
@@ -0,0 +1,375 @@
+/* config.h - 2000/06/21 */
+/*
+ * EasyTAG - Tag editor for MP3 and Ogg Vorbis files
+ * Copyright (C) 2000-2003 Jerome Couderc <easytag@gmail.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef __CONFIG_H__
+#define __CONFIG_H__
+
+#include <gtk/gtk.h>
+
+/***************
+ * Declaration *
+ ***************/
+
+typedef enum
+{
+ CV_TYPE_STRING=0,
+ CV_TYPE_INT,
+ CV_TYPE_BOOL
+} Config_Variable_Type;
+
+
+typedef struct _tConfigVariable tConfigVariable;
+struct _tConfigVariable
+{
+ char *name; /* Variable name written in config file */
+ Config_Variable_Type type; /* Variable type: Integer, Alphabetic, ... */
+ void *pointer; /* Pointer to our variable */
+};
+
+
+/*
+ * Config varariables
+ */
+/* Common */
+gint LOAD_ON_STARTUP;
+gchar *DEFAULT_PATH_TO_MP3;
+gint BROWSER_LINE_STYLE;
+gint BROWSER_EXPANDER_STYLE;
+gint BROWSE_SUBDIR;
+gint BROWSE_HIDDEN_DIR;
+gint OPEN_SELECTED_BROWSER_NODE;
+
+/* Misc */
+// User Interface
+gint SET_MAIN_WINDOW_POSITION;
+gint MAIN_WINDOW_X;
+gint MAIN_WINDOW_Y;
+gint MAIN_WINDOW_HEIGHT;
+gint MAIN_WINDOW_WIDTH;
+gint PANE_HANDLE_POSITION1;
+gint PANE_HANDLE_POSITION2;
+gint PANE_HANDLE_POSITION3;
+gint PANE_HANDLE_POSITION4;
+gint SHOW_HEADER_INFO;
+
+gint CHANGED_FILES_DISPLAYED_TO_RED;
+gint CHANGED_FILES_DISPLAYED_TO_BOLD;
+
+gint SORTING_FILE_MODE;
+gint SORTING_FILE_CASE_SENSITIVE;
+
+gint MESSAGE_BOX_POSITION_NONE;
+gint MESSAGE_BOX_POSITION_CENTER;
+gint MESSAGE_BOX_POSITION_MOUSE;
+gint MESSAGE_BOX_POSITION_CENTER_ON_PARENT;
+
+gchar *AUDIO_FILE_PLAYER;
+
+/* File Settings */
+gint REPLACE_ILLEGAL_CHARACTERS_IN_FILENAME;
+gint FILENAME_EXTENSION_LOWER_CASE;
+gint FILENAME_EXTENSION_UPPER_CASE;
+gint FILENAME_EXTENSION_NO_CHANGE;
+gint PRESERVE_MODIFICATION_TIME;
+
+gint FILENAME_CHARACTER_SET_OTHER;
+gint FILENAME_CHARACTER_SET_APPROXIMATE;
+gint FILENAME_CHARACTER_SET_DISCARD;
+
+/* Tag Settings */
+gint WRITE_ID3_TAGS_IN_FLAC_FILE;
+gint STRIP_TAG_WHEN_EMPTY_FIELDS;
+gint CONVERT_OLD_ID3V2_TAG_VERSION;
+
+gint FILE_WRITING_ID3V2_VERSION_4;
+gint USE_NON_STANDARD_ID3_READING_CHARACTER_SET;
+gchar *FILE_READING_ID3V1V2_CHARACTER_SET;
+
+gint FILE_WRITING_ID3V2_WRITE_TAG;
+gint FILE_WRITING_ID3V2_USE_CRC32;
+gint FILE_WRITING_ID3V2_USE_COMPRESSION;
+gint FILE_WRITING_ID3V2_USE_UNICODE_CHARACTER_SET;
+gchar *FILE_WRITING_ID3V2_UNICODE_CHARACTER_SET;
+gchar *FILE_WRITING_ID3V2_NO_UNICODE_CHARACTER_SET;
+gint FILE_WRITING_ID3V2_ICONV_OPTIONS_NO;
+gint FILE_WRITING_ID3V2_ICONV_OPTIONS_TRANSLIT;
+gint FILE_WRITING_ID3V2_ICONV_OPTIONS_IGNORE;
+
+gint FILE_WRITING_ID3V1_WRITE_TAG;
+gchar *FILE_WRITING_ID3V1_CHARACTER_SET;
+gint FILE_WRITING_ID3V1_ICONV_OPTIONS_NO;
+gint FILE_WRITING_ID3V1_ICONV_OPTIONS_TRANSLIT;
+gint FILE_WRITING_ID3V1_ICONV_OPTIONS_IGNORE;
+
+gint DATE_AUTO_COMPLETION;
+gint NUMBER_TRACK_FORMATED;
+gint NUMBER_TRACK_FORMATED_SPIN_BUTTON;
+gint OGG_TAG_WRITE_XMMS_COMMENT;
+gint SET_FOCUS_TO_SAME_TAG_FIELD;
+gint SET_FOCUS_TO_FIRST_TAG_FIELD;
+
+
+/* Scanner */
+gint SCANNER_TYPE;
+gint SCAN_MASK_EDITOR_BUTTON;
+gint SCAN_LEGEND_BUTTON;
+gint FTS_CONVERT_UNDERSCORE_AND_P20_INTO_SPACE;
+gint FTS_CONVERT_SPACE_INTO_UNDERSCORE;
+gint RFS_CONVERT_UNDERSCORE_AND_P20_INTO_SPACE;
+gint RFS_CONVERT_SPACE_INTO_UNDERSCORE;
+gint PFS_DONT_UPPER_SOME_WORDS;
+gint OVERWRITE_TAG_FIELD;
+gint SET_DEFAULT_COMMENT;
+gchar *DEFAULT_COMMENT;
+gint SET_CRC32_COMMENT;
+gint OPEN_SCANNER_WINDOW_ON_STARTUP;
+gint SCANNER_WINDOW_ON_TOP;
+gint SET_SCANNER_WINDOW_POSITION;
+gint SCANNER_WINDOW_X;
+gint SCANNER_WINDOW_Y;
+
+/* Confirmation */
+gint CONFIRM_BEFORE_EXIT;
+gint CONFIRM_WRITE_TAG;
+gint CONFIRM_RENAME_FILE;
+gint CONFIRM_WRITE_PLAYLIST;
+gint CONFIRM_DELETE_FILE;
+
+/* Scanner window */
+gint PROCESS_FILENAME_FIELD;
+gint PROCESS_TITLE_FIELD;
+gint PROCESS_ARTIST_FIELD;
+gint PROCESS_ALBUM_FIELD;
+gint PROCESS_GENRE_FIELD;
+gint PROCESS_COMMENT_FIELD;
+gint PROCESS_COMPOSER_FIELD;
+gint PROCESS_ORIG_ARTIST_FIELD;
+gint PROCESS_COPYRIGHT_FIELD;
+gint PROCESS_URL_FIELD;
+gint PROCESS_ENCODED_BY_FIELD;
+gint PF_CONVERT_INTO_SPACE;
+gint PF_CONVERT_SPACE;
+gint PF_CONVERT;
+gint PF_CONVERT_ALL_UPPERCASE;
+gint PF_CONVERT_ALL_DOWNCASE;
+gint PF_CONVERT_FIRST_LETTER_UPPERCASE;
+gint PF_CONVERT_FIRST_LETTERS_UPPERCASE;
+gint PF_REMOVE_SPACE;
+gint PF_INSERT_SPACE;
+gint PF_ONLY_ONE_SPACE;
+
+/* Playlist window */
+gchar *PLAYLIST_NAME;
+gint PLAYLIST_USE_MASK_NAME;
+gint PLAYLIST_USE_DIR_NAME;
+gint PLAYLIST_ONLY_SELECTED_FILES;
+gint PLAYLIST_FULL_PATH;
+gint PLAYLIST_RELATIVE_PATH;
+gint PLAYLIST_CREATE_IN_PARENT_DIR;
+gint PLAYLIST_USE_DOS_SEPARATOR;
+gint PLAYLIST_CONTENT_NONE;
+gint PLAYLIST_CONTENT_FILENAME;
+gint PLAYLIST_CONTENT_MASK;
+gchar *PLAYLIST_CONTENT_MASK_VALUE;
+
+gint PLAYLIST_WINDOW_X;
+gint PLAYLIST_WINDOW_Y;
+gint PLAYLIST_WINDOW_WIDTH;
+gint PLAYLIST_WINDOW_HEIGHT;
+
+/* "Load filenames from txt" window */
+gint LOAD_FILE_RUN_SCANNER;
+
+gint LOAD_FILE_WINDOW_X;
+gint LOAD_FILE_WINDOW_Y;
+gint LOAD_FILE_WINDOW_WIDTH;
+gint LOAD_FILE_WINDOW_HEIGHT;
+
+/* CDDB in preferences window */
+gchar *CDDB_SERVER_NAME_AUTOMATIC_SEARCH;
+gint CDDB_SERVER_PORT_AUTOMATIC_SEARCH;
+gchar *CDDB_SERVER_CGI_PATH_AUTOMATIC_SEARCH;
+gchar *CDDB_SERVER_NAME_AUTOMATIC_SEARCH2;
+gint CDDB_SERVER_PORT_AUTOMATIC_SEARCH2;
+gchar *CDDB_SERVER_CGI_PATH_AUTOMATIC_SEARCH2;
+gchar *CDDB_SERVER_NAME_MANUAL_SEARCH;
+gint CDDB_SERVER_PORT_MANUAL_SEARCH;
+gchar *CDDB_SERVER_CGI_PATH_MANUAL_SEARCH;
+gchar *CDDB_LOCAL_PATH;
+gint CDDB_USE_PROXY;
+gchar *CDDB_PROXY_NAME;
+gint CDDB_PROXY_PORT;
+gchar *CDDB_PROXY_USER_NAME;
+gchar *CDDB_PROXY_USER_PASSWORD;
+
+gint SET_CDDB_WINDOW_POSITION;
+gint CDDB_WINDOW_X;
+gint CDDB_WINDOW_Y;
+gint CDDB_WINDOW_HEIGHT;
+gint CDDB_WINDOW_WIDTH;
+gint CDDB_PANE_HANDLE_POSITION;
+
+gint CDDB_FOLLOW_FILE;
+gint CDDB_USE_DLM;
+gint CDDB_USE_LOCAL_ACCESS;
+
+/* CDDB window */
+gint CDDB_SEARCH_IN_ALL_FIELDS;
+gint CDDB_SEARCH_IN_ARTIST_FIELD;
+gint CDDB_SEARCH_IN_TITLE_FIELD;
+gint CDDB_SEARCH_IN_TRACK_NAME_FIELD;
+gint CDDB_SEARCH_IN_OTHER_FIELD;
+
+gint CDDB_SHOW_CATEGORIES;
+
+gint CDDB_SEARCH_IN_ALL_CATEGORIES;
+gint CDDB_SEARCH_IN_BLUES_CATEGORY;
+gint CDDB_SEARCH_IN_CLASSICAL_CATEGORY;
+gint CDDB_SEARCH_IN_COUNTRY_CATEGORY;
+gint CDDB_SEARCH_IN_FOLK_CATEGORY;
+gint CDDB_SEARCH_IN_JAZZ_CATEGORY;
+gint CDDB_SEARCH_IN_MISC_CATEGORY;
+gint CDDB_SEARCH_IN_NEWAGE_CATEGORY;
+gint CDDB_SEARCH_IN_REGGAE_CATEGORY;
+gint CDDB_SEARCH_IN_ROCK_CATEGORY;
+gint CDDB_SEARCH_IN_SOUNDTRACK_CATEGORY;
+
+gint CDDB_SET_TO_ALL_FIELDS;
+gint CDDB_SET_TO_TITLE;
+gint CDDB_SET_TO_ARTIST;
+gint CDDB_SET_TO_ALBUM;
+gint CDDB_SET_TO_YEAR;
+gint CDDB_SET_TO_TRACK;
+gint CDDB_SET_TO_TRACK_TOTAL;
+gint CDDB_SET_TO_GENRE;
+gint CDDB_SET_TO_FILE_NAME;
+
+gint CDDB_RUN_SCANNER;
+
+/* Search Window */
+gint SET_SEARCH_WINDOW_POSITION;
+gint SEARCH_WINDOW_X;
+gint SEARCH_WINDOW_Y;
+gint SEARCH_WINDOW_HEIGHT;
+gint SEARCH_WINDOW_WIDTH;
+gint SEARCH_IN_FILENAME;
+gint SEARCH_IN_TAG;
+gint SEARCH_CASE_SENSITIVE;
+
+/* Default mask */
+gchar *SCAN_TAG_DEFAULT_MASK;
+gchar *RENAME_FILE_DEFAULT_MASK;
+gchar *RENAME_DIRECTORY_DEFAULT_MASK;
+gint RENAME_DIRECTORY_WITH_MASK;
+
+
+/* Other parameters */
+gint OPTIONS_NOTEBOOK_PAGE;
+gint OPTIONS_WINDOW_HEIGHT;
+gint OPTIONS_WINDOW_WIDTH;
+
+
+
+/**************
+ * Prototypes *
+ **************/
+
+void Init_Config_Variables (void);
+void Read_Config (void);
+void Display_Config (void);
+
+void Apply_Changes_Of_Preferences_Window (void);
+void Apply_Changes_Of_UI (void);
+void Save_Changes_Of_Preferences_Window (void);
+void Save_Changes_Of_UI (void);
+void Save_Config_To_File (void);
+
+gboolean Create_Easytag_Directory (void);
+gboolean Setting_Create_Files (void);
+
+
+/* MasksList */
+void Load_Scan_Tag_Masks_List (GtkListStore *liststore, gint colnum, gchar **fallback);
+void Save_Scan_Tag_Masks_List (GtkListStore *liststore, gint colnum);
+
+/* RenameFileMasksList */
+void Load_Rename_File_Masks_List (GtkListStore *liststore, gint colnum, gchar **fallback);
+void Save_Rename_File_Masks_List (GtkListStore *liststore, gint colnum);
+
+/* RenameDirectoryMasksList 'RenameDirectoryMaskCombo' combobox */
+void Load_Rename_Directory_Masks_List (GtkListStore *liststore, gint colnum, gchar **fallback);
+void Save_Rename_Directory_Masks_List (GtkListStore *liststore, gint colnum);
+
+/* 'DefaultPathToMp3' combobox */
+void Load_Default_Path_To_MP3_List (GtkListStore *liststore, gint colnum);
+void Save_Default_Path_To_MP3_List (GtkListStore *liststore, gint colnum);
+
+/* 'DefaultComment' combobox */
+void Load_Default_Tag_Comment_Text_List (GtkListStore *liststore, gint colnum);
+void Save_Default_Tag_Comment_Text_List (GtkListStore *liststore, gint colnum);
+
+/* 'BrowserEntry' combobox */
+void Load_Path_Entry_List (GtkListStore *liststore, gint colnum);
+void Save_Path_Entry_List (GtkListStore *liststore, gint colnum);
+
+/* 'PlayListNameEntry' combobox */
+void Load_Play_List_Name_List (GtkListStore *liststore, gint colnum);
+void Save_Play_List_Name_List (GtkListStore *liststore, gint colnum);
+
+/* Run Program combobox (tree browser) */
+void Load_Run_Program_With_Directory_List (GtkListStore *liststore, gint colnum);
+void Save_Run_Program_With_Directory_List (GtkListStore *liststore, gint colnum);
+
+/* Run Program combobox (file browser) */
+void Load_Run_Program_With_File_List (GtkListStore *liststore, gint colnum);
+void Save_Run_Program_With_File_List (GtkListStore *liststore, gint colnum);
+
+/* 'FilePlayerEntry' combobox */
+void Load_Audio_File_Player_List (GtkListStore *liststore, gint colnum);
+void Save_Audio_File_Player_List (GtkListStore *liststore, gint colnum);
+
+/* 'SearchStringEntry' combobox */
+void Load_Search_File_List (GtkListStore *liststore, gint colnum);
+void Save_Search_File_List (GtkListStore *liststore, gint colnum);
+
+/* 'FileToLoad' combobox */
+void Load_File_To_Load_List (GtkListStore *liststore, gint colnum);
+void Save_File_To_Load_List (GtkListStore *liststore, gint colnum);
+
+/* 'PlayListContentMaskEntry' combobox */
+void Load_Playlist_Content_Mask_List (GtkListStore *liststore, gint colnum);
+void Save_Playlist_Content_Mask_List (GtkListStore *liststore, gint colnum);
+
+/* 'CddbSearchStringEntry' combobox */
+void Load_Cddb_Search_String_List (GtkListStore *liststore, gint colnum);
+void Save_Cddb_Search_String_List (GtkListStore *liststore, gint colnum);
+
+/* 'CddbSearchStringInResultEntry' combobox */
+void Load_Cddb_Search_String_In_Result_List (GtkListStore *liststore, gint colnum);
+void Save_Cddb_Search_String_In_Result_List (GtkListStore *liststore, gint colnum);
+
+/* 'CddbLocalPath' combobox */
+void Load_Cddb_Local_Path_List (GtkListStore *liststore, gint colnum);
+void Save_Cddb_Local_Path_List (GtkListStore *liststore, gint colnum);
+
+
+
+#endif /* __CONFIG_H__ */
diff --git a/src/ui_manager.h b/src/ui_manager.h
new file mode 100755
index 0000000..8d34a94
--- /dev/null
+++ b/src/ui_manager.h
@@ -0,0 +1,325 @@
+/*
+ * - bar.h : defines constant associated to the action "<menuitem action="
+ * - bar.c : Create_UI() does the actions
+ */
+
+static const gchar *ui_xml =
+"<ui>"
+
+/*
+ * Menu bar
+ */
+" <menubar name='MenuBar'>"
+" <menu action='FileMenu'>"
+
+" <menu action='SortTagMenu'>"
+" <menuitem action='SortTrackNumAsc' />"
+" <menuitem action='SortTrackNumDesc' />"
+" <separator />"
+" <menuitem action='SortTitleAsc' />"
+" <menuitem action='SortTitleDesc' />"
+" <separator />"
+" <menuitem action='SortArtistAsc' />"
+" <menuitem action='SortArtistDesc' />"
+" <separator />"
+" <menuitem action='SortAlbumAsc' />"
+" <menuitem action='SortAlbumDesc' />"
+" <separator />"
+" <menuitem action='SortYearAsc' />"
+" <menuitem action='SortYearDesc' />"
+" <separator />"
+" <menuitem action='SortYearAsc' />"
+" <menuitem action='SortYearDesc' />"
+" <separator />"
+" <menuitem action='SortGenreAsc' />"
+" <menuitem action='SortGenreDesc' />"
+" <separator />"
+" <menuitem action='SortCommentAsc' />"
+" <menuitem action='SortCommentDesc' />"
+" <separator />"
+" <menuitem action='SortComposerAsc' />"
+" <menuitem action='SortComposerDesc' />"
+" <separator />"
+" <menuitem action='SortOrigArtistAsc' />"
+" <menuitem action='SortOrigArtistDesc' />"
+" <separator />"
+" <menuitem action='SortCopyrightAsc' />"
+" <menuitem action='SortCopyrightDesc' />"
+" <separator />"
+" <menuitem action='SortUrlAsc' />"
+" <menuitem action='SortUrlDesc' />"
+" <separator />"
+" <menuitem action='SortEncodedByAsc' />"
+" <menuitem action='SortEncodedByDesc' />"
+" <separator />"
+" </menu>"
+
+" <menu action='SortPropMenu'>"
+" <menuitem action='SortFilenameAsc' />"
+" <menuitem action='SortFilenameDesc' />"
+" <separator />"
+" <menuitem action='SortDateAsc' />"
+" <menuitem action='SortDateDesc' />"
+" <separator />"
+" <menuitem action='SortTypeAsc' />"
+" <menuitem action='SortTypeDesc' />"
+" <separator />"
+" <menuitem action='SortSizeAsc' />"
+" <menuitem action='SortSizeDesc' />"
+" <separator />"
+" <menuitem action='SortDurationAsc' />"
+" <menuitem action='SortDurationDesc' />"
+" <separator />"
+" <menuitem action='SortBitrateAsc' />"
+" <menuitem action='SortBitrateDesc' />"
+" <separator />"
+" <menuitem action='SortSamplerateAsc' />"
+" <menuitem action='SortSamplerateDesc' />"
+" </menu>"
+
+" <menuitem action='OpenFile' />"
+" <separator />"
+
+" <menuitem action='SelAll' />"
+" <menuitem action='UnselAll' />"
+" <menuitem action='SelInv' />"
+" <separator />"
+
+" <menuitem action='DeleteFile' />"
+" <separator />"
+
+" <menuitem action='FirstFile' />"
+" <menuitem action='PreviousFile' />"
+" <menuitem action='NextFile' />"
+" <menuitem action='LastFile' />"
+" <separator />"
+
+" <menuitem action='ScanFile' />"
+" <menuitem action='RemoveTag' />"
+" <menuitem action='UndoFile' />"
+" <menuitem action='RedoFile' />"
+" <menuitem action='SaveFile' />"
+" <menuitem action='SaveFileForced' />"
+" <separator />"
+
+" <menuitem action='Undo' />"
+" <menuitem action='Redo' />"
+" <separator />"
+
+" <menuitem action='Quit' />"
+" </menu>"
+
+
+" <menu action='BrowserMenu'>"
+" <menuitem action='GoToHome' />"
+" <menuitem action='GoToDefaultPath' />"
+" <menuitem action='SetDefaultPath' />"
+" <separator />"
+
+" <menuitem action='ViewMode' />"
+" <menuitem action='RenameDir' />"
+" <menuitem action='ReloadDir' />"
+" <menuitem action='BrowseDir' />"
+" <separator />"
+
+" <menuitem action='BrowseSubdir' />"
+#ifndef WIN32
+" <menuitem action='BrowseHiddenDir' />"
+#endif
+" <separator />"
+
+" <menuitem action='CollapseTree' />"
+" <menuitem action='RefreshTree' />"
+" </menu>"
+
+" <menu action='ScannerMenu'>"
+" <menuitem action='FillTag' />"
+" <menuitem action='RenameFile' />"
+" <menuitem action='ProcessFields' />"
+" </menu>"
+
+" <menu action='MiscMenu'>"
+" <menuitem action='SearchFile' />"
+" <menuitem action='CDDBSearch' />"
+" <separator />"
+
+" <menuitem action='LoadFilenames' />"
+" <menuitem action='WritePlaylist' />"
+" <menuitem action='RunAudio' />"
+" </menu>"
+
+" <menu action='SettingsMenu'>"
+" <menuitem action='Preferences' />"
+" </menu>"
+
+" <menu action='HelpMenu'>"
+" <menuitem action='About' />"
+" </menu>"
+
+" </menubar>"
+
+
+/*
+ * Tool bar
+ */
+" <toolbar name='ToolBar'>"
+" <toolitem action='FirstFile'/>"
+" <toolitem action='PreviousFile'/>"
+" <toolitem action='NextFile'/>"
+" <toolitem action='LastFile'/>"
+" <separator />"
+
+" <toolitem action='ScanFile'/>"
+" <toolitem action='RemoveTag'/>"
+" <toolitem action='UndoFile'/>"
+" <toolitem action='RedoFile'/>"
+" <toolitem action='SaveFile'/>"
+" <separator />"
+
+" <toolitem action='ViewModeToggle'/>"
+" <toolitem action='SelAll'/>"
+" <toolitem action='SelInv'/>"
+" <separator />"
+
+" <toolitem action='SearchFile' />"
+" <toolitem action='CDDBSearch' />"
+" <toolitem action='WritePlaylist' />"
+" <separator />"
+
+" <toolitem action='Stop'/>"
+" <separator />"
+
+" <toolitem action='Quit'/>"
+" </toolbar>"
+
+
+/*
+ * Popup menus
+ */
+// Popup in file list
+" <popup name='FilePopup'>"
+" <menuitem action='SelAll' />"
+" <menuitem action='UnselAll' />"
+" <menuitem action='SelInv' />"
+" <separator />"
+" <menuitem action='RunAudio' />"
+" <separator />"
+" <menu action='ScannerSubpopup'>"
+" <menuitem action='FillTag' />"
+" <menuitem action='RenameFile' />"
+" <menuitem action='ProcessFields' />"
+" </menu>"
+" <menuitem action='CDDBSearchFile' />"
+" <menuitem action='SearchFile' />"
+" <menuitem action='DeleteFile' />"
+" <menuitem action='ReloadDir' />"
+" <menuitem action='OpenFile' />"
+" <separator />"
+
+" <menu action='SortTagMenu'>"
+" <menuitem action='SortTrackNumAsc' />"
+" <menuitem action='SortTrackNumDesc' />"
+" <separator />"
+" <menuitem action='SortTitleAsc' />"
+" <menuitem action='SortTitleDesc' />"
+" <separator />"
+" <menuitem action='SortArtistAsc' />"
+" <menuitem action='SortArtistDesc' />"
+" <separator />"
+" <menuitem action='SortAlbumAsc' />"
+" <menuitem action='SortAlbumDesc' />"
+" <separator />"
+" <menuitem action='SortYearAsc' />"
+" <menuitem action='SortYearDesc' />"
+" <separator />"
+" <menuitem action='SortYearAsc' />"
+" <menuitem action='SortYearDesc' />"
+" <separator />"
+" <menuitem action='SortGenreAsc' />"
+" <menuitem action='SortGenreDesc' />"
+" <separator />"
+" <menuitem action='SortCommentAsc' />"
+" <menuitem action='SortCommentDesc' />"
+" <separator />"
+" <menuitem action='SortComposerAsc' />"
+" <menuitem action='SortComposerDesc' />"
+" <separator />"
+" <menuitem action='SortOrigArtistAsc' />"
+" <menuitem action='SortOrigArtistDesc' />"
+" <separator />"
+" <menuitem action='SortCopyrightAsc' />"
+" <menuitem action='SortCopyrightDesc' />"
+" <separator />"
+" <menuitem action='SortUrlAsc' />"
+" <menuitem action='SortUrlDesc' />"
+" <separator />"
+" <menuitem action='SortEncodedByAsc' />"
+" <menuitem action='SortEncodedByDesc' />"
+" <separator />"
+" </menu>"
+
+" <menu action='SortPropMenu'>"
+" <menuitem action='SortFilenameAsc' />"
+" <menuitem action='SortFilenameDesc' />"
+" <separator />"
+" <menuitem action='SortDateAsc' />"
+" <menuitem action='SortDateDesc' />"
+" <separator />"
+" <menuitem action='SortTypeAsc' />"
+" <menuitem action='SortTypeDesc' />"
+" <separator />"
+" <menuitem action='SortSizeAsc' />"
+" <menuitem action='SortSizeDesc' />"
+" <separator />"
+" <menuitem action='SortDurationAsc' />"
+" <menuitem action='SortDurationDesc' />"
+" <separator />"
+" <menuitem action='SortBitrateAsc' />"
+" <menuitem action='SortBitrateDesc' />"
+" <separator />"
+" <menuitem action='SortSamplerateAsc' />"
+" <menuitem action='SortSamplerateDesc' />"
+" </menu>"
+" </popup>"
+
+// Popup in browser tree
+" <popup name='DirPopup'>"
+" <menuitem action='DirPopupRunAudio' />"
+" <separator />"
+" <menuitem action='GoToHome' />"
+" <menuitem action='GoToDefaultPath' />"
+" <menuitem action='SetDefaultPath' />"
+" <separator />"
+" <menuitem action='RenameDir' />"
+" <menuitem action='ReloadDir' />"
+" <menuitem action='BrowseDir' />"
+" <separator />"
+" <menuitem action='BrowseSubdir' />"
+#ifndef WIN32
+" <menuitem action='BrowseHiddenDir' />"
+#endif
+" <separator />"
+" <menuitem action='CollapseTree' />"
+" <menuitem action='RefreshTree' />"
+" </popup>"
+
+// Popup in browser artist list
+" <popup name='DirArtistPopup'>"
+" <menuitem action='ArtistRunAudio' />"
+//" <separator />"
+//" <menuitem action='ArtistOpenFile' />"
+" </popup>"
+
+// Popup in browser album list
+" <popup name='DirAlbumPopup'>"
+" <menuitem action='AlbumRunAudio' />"
+//" <separator />"
+//" <menuitem action='AlbumOpenFile' />"
+" </popup>"
+
+// Popup in Log list
+" <popup name='LogPopup'>"
+" <menuitem action='CleanLog' />"
+" </popup>"
+
+"</ui>";
diff --git a/src/vcedit.c b/src/vcedit.c
new file mode 100755
index 0000000..3e583bd
--- /dev/null
+++ b/src/vcedit.c
@@ -0,0 +1,629 @@
+/* This program is licensed under the GNU Library General Public License, version 2,
+ * a copy of which is included with this program (LICENCE.LGPL).
+ *
+ * (c) 2000-2001 Michael Smith <msmith@xiph.org>
+ *
+ *
+ * Comment editing backend, suitable for use by nice frontend interfaces.
+ *
+ * last modified: $Id: vcedit.c,v 1.23 2003/09/03 07:58:05 calc Exp $
+ */
+
+#include <config.h> // For definition of ENABLE_OGG
+
+#ifdef ENABLE_OGG
+
+#include <glib/gi18n-lib.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <ogg/ogg.h>
+#include <vorbis/codec.h>
+
+#include "vcedit.h"
+
+
+#define CHUNKSIZE 4096
+
+vcedit_state *vcedit_new_state(void)
+{
+ vcedit_state *state = malloc(sizeof(vcedit_state));
+ memset(state, 0, sizeof(vcedit_state));
+ state->oggtype = VCEDIT_IS_UNKNOWN;
+
+ return state;
+}
+
+char *vcedit_error(vcedit_state *state)
+{
+ return state->lasterror;
+}
+
+vorbis_comment *vcedit_comments(vcedit_state *state)
+{
+ return state->vc;
+}
+
+static void vcedit_clear_internals(vcedit_state *state)
+{
+ char *tmp;
+ if(state->vc)
+ {
+ vorbis_comment_clear(state->vc);
+ free(state->vc);
+ }
+ if(state->os)
+ {
+ ogg_stream_clear(state->os);
+ free(state->os);
+ }
+ if(state->oy)
+ {
+ ogg_sync_clear(state->oy);
+ free(state->oy);
+ }
+ if(state->vendor)
+ free(state->vendor);
+ if(state->mainbuf)
+ free(state->mainbuf);
+ if(state->bookbuf)
+ free(state->bookbuf);
+ if(state->vi) {
+ vorbis_info_clear(state->vi);
+ free(state->vi);
+ }
+
+ tmp = state->lasterror;
+ memset(state, 0, sizeof(*state));
+ state->lasterror = tmp;
+}
+
+void vcedit_clear(vcedit_state *state)
+{
+ if(state)
+ {
+ vcedit_clear_internals(state);
+ free(state);
+ }
+}
+
+/* Next two functions pulled straight from libvorbis, apart from one change
+ * - we don't want to overwrite the vendor string.
+ */
+static void _v_writestring(oggpack_buffer *o,char *s, int len)
+{
+ while(len--)
+ {
+ oggpack_write(o,*s++,8);
+ }
+}
+
+static int _commentheader_out(vcedit_state *state, ogg_packet *op)
+{
+ vorbis_comment *vc = state->vc;
+ char *vendor = state->vendor;
+ oggpack_buffer opb;
+
+ oggpack_writeinit(&opb);
+
+ if (state->oggtype == VCEDIT_IS_OGGVORBIS)
+ {
+ /* preamble */
+ oggpack_write(&opb,0x03,8);
+ _v_writestring(&opb,"vorbis", 6);
+ }
+
+ /* vendor */
+ oggpack_write(&opb,strlen(vendor),32);
+ _v_writestring(&opb,vendor, strlen(vendor));
+
+ /* comments */
+ oggpack_write(&opb,vc->comments,32);
+ if(vc->comments){
+ int i;
+ for(i=0;i<vc->comments;i++){
+ if(vc->user_comments[i]){
+ oggpack_write(&opb,vc->comment_lengths[i],32);
+ _v_writestring(&opb,vc->user_comments[i],
+ vc->comment_lengths[i]);
+ }else{
+ oggpack_write(&opb,0,32);
+ }
+ }
+ }
+ oggpack_write(&opb,1,1);
+
+ op->packet = _ogg_malloc(oggpack_bytes(&opb));
+ memcpy(op->packet, opb.buffer, oggpack_bytes(&opb));
+
+ op->bytes=oggpack_bytes(&opb);
+ op->b_o_s=0;
+ op->e_o_s=0;
+ op->granulepos=0;
+ if (state->oggtype == VCEDIT_IS_OGGVORBIS)
+ {
+ op->packetno = 1;
+ }
+
+ oggpack_writeclear(&opb);
+ return 0;
+}
+
+static int _blocksize(vcedit_state *s, ogg_packet *p)
+{
+ int this = vorbis_packet_blocksize(s->vi, p);
+ int ret = (this + s->prevW)/4;
+
+ if(!s->prevW)
+ {
+ s->prevW = this;
+ return 0;
+ }
+
+ s->prevW = this;
+ return ret;
+}
+
+static int _fetch_next_packet(vcedit_state *s, ogg_packet *p, ogg_page *page)
+{
+ int result;
+ char *buffer;
+ int bytes;
+
+ result = ogg_stream_packetout(s->os, p);
+
+ if(result > 0)
+ return 1;
+ else
+ {
+ if(s->eosin)
+ return 0;
+ while(ogg_sync_pageout(s->oy, page) <= 0)
+ {
+ buffer = ogg_sync_buffer(s->oy, CHUNKSIZE);
+ bytes = s->read(buffer,1, CHUNKSIZE, s->in);
+ ogg_sync_wrote(s->oy, bytes);
+ if(bytes == 0)
+ return 0;
+ }
+ if(ogg_page_eos(page))
+ s->eosin = 1;
+ else if(ogg_page_serialno(page) != s->serial)
+ {
+ s->eosin = 1;
+ s->extrapage = 1;
+ return 0;
+ }
+
+ ogg_stream_pagein(s->os, page);
+ return _fetch_next_packet(s, p, page);
+ }
+}
+
+/*
+ * Next functions pulled straight from libvorbis,
+ */
+static void _v_readstring(oggpack_buffer *o,char *buf,int bytes){
+ while(bytes--){
+ *buf++=oggpack_read(o,8);
+ }
+}
+
+/*
+ * Next functions pulled straight from libvorbis, apart from one change
+ * - disabled the EOP check
+ */
+static int _speex_unpack_comment(vorbis_comment *vc,oggpack_buffer *opb)
+{
+ int i;
+ int vendorlen=oggpack_read(opb,32);
+ if (vendorlen<0)
+ goto err_out;
+
+ vc->vendor=_ogg_calloc(vendorlen+1,1);
+ _v_readstring(opb,vc->vendor,vendorlen);
+
+ vc->comments=oggpack_read(opb,32);
+ if (vc->comments<0)
+ goto err_out;
+ vc->user_comments=_ogg_calloc(vc->comments+1,sizeof(*vc->user_comments));
+ vc->comment_lengths=_ogg_calloc(vc->comments+1, sizeof(*vc->comment_lengths));
+ for (i=0;i<vc->comments;i++){
+ int len=oggpack_read(opb,32);
+ if (len<0)
+ goto err_out;
+ vc->comment_lengths[i]=len;
+ vc->user_comments[i]=_ogg_calloc(len+1,1);
+ _v_readstring(opb,vc->user_comments[i],len);
+ }
+ //if(oggpack_read(opb,1)!=1)goto err_out; /* EOP check */
+ return(0);
+
+err_out:
+ vorbis_comment_clear(vc);
+ return(1);
+}
+
+int vcedit_open(vcedit_state *state, FILE *in)
+{
+ return vcedit_open_callbacks(state, (void *)in,
+ (vcedit_read_func)fread, (vcedit_write_func)fwrite);
+}
+
+int vcedit_open_callbacks(vcedit_state *state, void *in,
+ vcedit_read_func read_func, vcedit_write_func write_func)
+{
+
+ char *buffer;
+ int bytes,i;
+ int chunks = 0;
+ ogg_packet *header;
+ ogg_packet header_main;
+ ogg_packet header_comments;
+ ogg_packet header_codebooks;
+ ogg_page og;
+
+ state->in = in;
+ state->read = read_func;
+ state->write = write_func;
+
+ state->oy = malloc(sizeof(ogg_sync_state));
+ ogg_sync_init(state->oy);
+
+ while(1)
+ {
+ buffer = ogg_sync_buffer(state->oy, CHUNKSIZE);
+ bytes = state->read(buffer, 1, CHUNKSIZE, state->in);
+
+ ogg_sync_wrote(state->oy, bytes);
+
+ if(ogg_sync_pageout(state->oy, &og) == 1)
+ break;
+
+ if(chunks++ >= 10) /* Bail if we don't find data in the first 40 kB */
+ {
+ if(bytes<CHUNKSIZE)
+ state->lasterror = _("Input truncated or empty.");
+ else
+ state->lasterror = _("Input is not an Ogg bitstream.");
+ goto err;
+ }
+ }
+
+ state->serial = ogg_page_serialno(&og);
+
+ state->os = malloc(sizeof(ogg_stream_state));
+ ogg_stream_init(state->os, state->serial);
+
+ state->vi = malloc(sizeof(vorbis_info));
+ vorbis_info_init(state->vi);
+
+ state->vc = malloc(sizeof(vorbis_comment));
+ vorbis_comment_init(state->vc);
+
+ if(ogg_stream_pagein(state->os, &og) < 0)
+ {
+ state->lasterror = _("Error reading first page of Ogg bitstream.");
+ goto err;
+ }
+
+ if(ogg_stream_packetout(state->os, &header_main) != 1)
+ {
+ state->lasterror = _("Error reading initial header packet.");
+ goto err;
+ }
+
+ // We save the main header first, (it seems speex_packet_to_header munge's it??)
+ state->mainlen = header_main.bytes;
+ state->mainbuf = malloc(state->mainlen);
+ memcpy(state->mainbuf, header_main.packet, header_main.bytes);
+
+ state->oggtype = VCEDIT_IS_UNKNOWN;
+ int headerpackets = 0;
+ if(vorbis_synthesis_headerin(state->vi, state->vc, &header_main) == 0)
+ {
+ state->oggtype = VCEDIT_IS_OGGVORBIS;
+ }
+#ifdef ENABLE_SPEEX
+ else
+ {
+ // Done after "Ogg test" to avoid to display an error message in function
+ // speex_packet_to_header when the file is not Speex.
+ state->si = malloc(sizeof(SpeexHeader));
+ if((state->si = speex_packet_to_header((char*)(&header_main)->packet,(&header_main)->bytes)))
+ state->oggtype = VCEDIT_IS_SPEEX;
+ }
+#endif
+
+ if (state->oggtype == VCEDIT_IS_UNKNOWN)
+ {
+ state->lasterror = _("Ogg bitstream contains neither speex or vorbis data.");
+ goto err;
+ }
+
+ switch (state->oggtype)
+ {
+ case VCEDIT_IS_OGGVORBIS:
+ header = &header_comments;
+ headerpackets = 3;
+ break;
+#ifdef ENABLE_SPEEX
+ case VCEDIT_IS_SPEEX:
+ header = &header_comments;
+ headerpackets = 2 + state->si->extra_headers;
+ break;
+#endif
+ }
+ oggpack_buffer opb;
+ i = 1;
+ while(i<headerpackets)
+ {
+ while(i<headerpackets)
+ {
+ int result = ogg_sync_pageout(state->oy, &og);
+ if(result == 0) break; /* Too little data so far */
+ else if(result == 1)
+ {
+ ogg_stream_pagein(state->os, &og);
+ while(i< headerpackets)
+ {
+ result = ogg_stream_packetout(state->os, header);
+ if(result == 0) break;
+ if(result == -1)
+ {
+ state->lasterror = _("Corrupt secondary header.");
+ goto err;
+ }
+ switch (state->oggtype)
+ {
+ case VCEDIT_IS_OGGVORBIS:
+ vorbis_synthesis_headerin(state->vi, state->vc, header);
+ switch (i)
+ {
+ // 0 packet was the vorbis header
+ case 1:
+ header = &header_codebooks;
+ break;
+ case 2:
+ state->booklen = header->bytes;
+ state->bookbuf = malloc(state->booklen);
+ memcpy(state->bookbuf, header->packet,
+ header->bytes);
+ break;
+ }
+ break;
+ case VCEDIT_IS_SPEEX:
+ switch (i)
+ {
+ // 0 packet was the speex header
+ case 1:
+ oggpack_readinit(&opb,header->packet,header->bytes);
+ _speex_unpack_comment(state->vc,&opb);
+ break;
+ default:
+ state->lasterror = _("Need to save extra headers - TODO!!");
+ goto err;
+ break;
+ }
+ }
+ i++;
+ }
+ }
+ }
+
+ buffer = ogg_sync_buffer(state->oy, CHUNKSIZE);
+ bytes = state->read(buffer, 1, CHUNKSIZE, state->in);
+ if(bytes == 0 && i < 2)
+ {
+ state->lasterror = _("EOF before end of vorbis headers.");
+ goto err;
+ }
+ ogg_sync_wrote(state->oy, bytes);
+ }
+
+ /* Copy the vendor tag */
+ state->vendor = malloc(strlen(state->vc->vendor) +1);
+ strcpy(state->vendor, state->vc->vendor);
+
+ /* Headers are done! */
+ return 0;
+
+err:
+ vcedit_clear_internals(state);
+ return -1;
+}
+
+int vcedit_write(vcedit_state *state, void *out)
+{
+ ogg_stream_state streamout;
+ ogg_packet header_main;
+ ogg_packet header_comments;
+ ogg_packet header_codebooks;
+
+ ogg_page ogout, ogin;
+ ogg_packet op;
+ ogg_int64_t granpos = 0;
+ int result;
+ char *buffer;
+ int bytes;
+ int needflush=0, needout=0;
+
+ state->eosin = 0;
+ state->extrapage = 0;
+
+ header_main.bytes = state->mainlen;
+ header_main.packet = state->mainbuf;
+ header_main.b_o_s = 1;
+ header_main.e_o_s = 0;
+ header_main.granulepos = 0;
+
+ header_codebooks.bytes = state->booklen;
+ header_codebooks.packet = state->bookbuf;
+ header_codebooks.b_o_s = 0;
+ header_codebooks.e_o_s = 0;
+ header_codebooks.granulepos = 0;
+
+ ogg_stream_init(&streamout, state->serial);
+
+ _commentheader_out(state, &header_comments);
+
+ ogg_stream_packetin(&streamout, &header_main);
+ ogg_stream_packetin(&streamout, &header_comments);
+
+ if (state->oggtype != VCEDIT_IS_SPEEX)
+ ogg_stream_packetin(&streamout, &header_codebooks);
+
+ while((result = ogg_stream_flush(&streamout, &ogout)))
+ {
+ if(state->write(ogout.header,1,ogout.header_len, out) !=
+ (size_t) ogout.header_len)
+ goto cleanup;
+ if(state->write(ogout.body,1,ogout.body_len, out) !=
+ (size_t) ogout.body_len)
+ goto cleanup;
+ }
+
+ while(_fetch_next_packet(state, &op, &ogin))
+ {
+ if(needflush)
+ {
+ if(ogg_stream_flush(&streamout, &ogout))
+ {
+ if(state->write(ogout.header,1,ogout.header_len,
+ out) != (size_t) ogout.header_len)
+ goto cleanup;
+ if(state->write(ogout.body,1,ogout.body_len,
+ out) != (size_t) ogout.body_len)
+ goto cleanup;
+ }
+ }
+ else if(needout)
+ {
+ if(ogg_stream_pageout(&streamout, &ogout))
+ {
+ if(state->write(ogout.header,1,ogout.header_len,
+ out) != (size_t) ogout.header_len)
+ goto cleanup;
+ if(state->write(ogout.body,1,ogout.body_len,
+ out) != (size_t) ogout.body_len)
+ goto cleanup;
+ }
+ }
+
+ needflush=needout=0;
+
+ if (state->oggtype == VCEDIT_IS_OGGVORBIS)
+ {
+ int size;
+ size = _blocksize(state, &op);
+ granpos += size;
+
+ if(op.granulepos == -1)
+ {
+ op.granulepos = granpos;
+ ogg_stream_packetin(&streamout, &op);
+ }
+ else /* granulepos is set, validly. Use it, and force a flush to
+ account for shortened blocks (vcut) when appropriate */
+ {
+ if(granpos > op.granulepos)
+ {
+ granpos = op.granulepos;
+ ogg_stream_packetin(&streamout, &op);
+ needflush=1;
+ }
+ else
+ {
+ ogg_stream_packetin(&streamout, &op);
+ needout=1;
+ }
+ }
+ }
+ /* Don't know about granulepos for speex, will just assume the original
+ was appropriate. Not sure about the flushing?? */
+ else if (state->oggtype == VCEDIT_IS_SPEEX)
+ {
+ ogg_stream_packetin(&streamout, &op);
+ needout=1;
+ }
+ }
+
+ streamout.e_o_s = 1;
+ while(ogg_stream_flush(&streamout, &ogout))
+ {
+ if(state->write(ogout.header,1,ogout.header_len,
+ out) != (size_t) ogout.header_len)
+ goto cleanup;
+ if(state->write(ogout.body,1,ogout.body_len,
+ out) != (size_t) ogout.body_len)
+ goto cleanup;
+ }
+
+ if (state->extrapage)
+ {
+ if(state->write(ogin.header,1,ogin.header_len,
+ out) != (size_t) ogin.header_len)
+ goto cleanup;
+ if (state->write(ogin.body,1,ogin.body_len, out) !=
+ (size_t) ogin.body_len)
+ goto cleanup;
+ }
+
+ state->eosin=0; /* clear it, because not all paths to here do */
+ while(!state->eosin) /* We reached eos, not eof */
+ {
+ /* We copy the rest of the stream (other logical streams)
+ * through, a page at a time. */
+ while(1)
+ {
+ result = ogg_sync_pageout(state->oy, &ogout);
+ if(result==0)
+ break;
+ if(result<0)
+ state->lasterror = _("Corrupt or missing data, continuing...");
+ else
+ {
+ /* Don't bother going through the rest, we can just
+ * write the page out now */
+ if(state->write(ogout.header,1,ogout.header_len,
+ out) != (size_t) ogout.header_len) {
+ goto cleanup;
+ }
+ if(state->write(ogout.body,1,ogout.body_len, out) !=
+ (size_t) ogout.body_len) {
+ goto cleanup;
+ }
+ }
+ }
+ buffer = ogg_sync_buffer(state->oy, CHUNKSIZE);
+ bytes = state->read(buffer,1, CHUNKSIZE, state->in);
+ ogg_sync_wrote(state->oy, bytes);
+ if(bytes == 0)
+ {
+ state->eosin = 1;
+ break;
+ }
+ }
+
+
+cleanup:
+ ogg_stream_clear(&streamout);
+ ogg_packet_clear(&header_comments);
+
+ free(state->mainbuf);
+ free(state->bookbuf);
+ state->mainbuf = state->bookbuf = NULL;
+
+ if(!state->eosin)
+ {
+ state->lasterror =
+ _("Error writing stream to output. "
+ "Output stream may be corrupted or truncated.");
+ return -1;
+ }
+
+ return 0;
+}
+
+#endif /* ENABLE_OGG */
diff --git a/src/vcedit.h b/src/vcedit.h
new file mode 100755
index 0000000..5bc39dc
--- /dev/null
+++ b/src/vcedit.h
@@ -0,0 +1,75 @@
+/* This program is licensed under the GNU Library General Public License, version 2,
+ * a copy of which is included with this program (with filename LICENSE.LGPL).
+ *
+ * (c) 2000-2001 Michael Smith <msmith@xiph.org>
+ *
+ * VCEdit header.
+ *
+ * last modified: $ID:$
+ */
+
+#ifndef __VCEDIT_H
+#define __VCEDIT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdio.h>
+#include <ogg/ogg.h>
+#include <vorbis/codec.h>
+
+#ifdef ENABLE_SPEEX
+#include <speex/speex.h>
+#include <speex/speex_header.h>
+#endif
+
+#define VCEDIT_IS_UNKNOWN 0
+#define VCEDIT_IS_SPEEX 1
+#define VCEDIT_IS_OGGVORBIS 2
+
+typedef size_t (*vcedit_read_func)(void *, size_t, size_t, void *);
+typedef size_t (*vcedit_write_func)(const void *, size_t, size_t, void *);
+
+typedef struct {
+ ogg_sync_state *oy;
+ ogg_stream_state *os;
+
+ int oggtype; // Stream is Vorbis or Speex?
+ vorbis_comment *vc;
+ vorbis_info *vi;
+#ifdef ENABLE_SPEEX
+ SpeexHeader *si;
+#endif
+
+ vcedit_read_func read;
+ vcedit_write_func write;
+
+ void *in;
+ long serial;
+ unsigned char *mainbuf;
+ unsigned char *bookbuf;
+ int mainlen;
+ int booklen;
+ char *lasterror;
+ char *vendor;
+ int prevW;
+ int extrapage;
+ int eosin;
+} vcedit_state;
+
+extern vcedit_state *vcedit_new_state(void);
+extern void vcedit_clear(vcedit_state *state);
+extern vorbis_comment *vcedit_comments(vcedit_state *state);
+extern int vcedit_open(vcedit_state *state, FILE *in);
+extern int vcedit_open_callbacks(vcedit_state *state, void *in,
+ vcedit_read_func read_func, vcedit_write_func write_func);
+extern int vcedit_write(vcedit_state *state, void *out);
+extern char *vcedit_error(vcedit_state *state);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __VCEDIT_H */
+
diff --git a/src/wavpack_header.c b/src/wavpack_header.c
new file mode 100755
index 0000000..c7067b8
--- /dev/null
+++ b/src/wavpack_header.c
@@ -0,0 +1,114 @@
+/* wavpack_header.c - 2007/02/15 */
+/*
+ * EasyTAG - Tag editor for many file types
+ * Copyright (C) 2007 Maarten Maathuis (madman2003@gmail.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+
+#ifdef ENABLE_WAVPACK
+
+#include <gtk/gtk.h>
+#include <glib/gi18n-lib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <wavpack/wavpack.h>
+
+#include "easytag.h"
+#include "et_core.h"
+#include "misc.h"
+#include "charset.h"
+#include "wavpack_header.h"
+
+
+gboolean Wavpack_Header_Read_File_Info(gchar *filename, ET_File_Info *ETFileInfo)
+{
+ WavpackContext *wpc;
+
+ wpc = WavpackOpenFileInput(filename, NULL, 0, 0);
+
+ if ( wpc == NULL ) {
+ return FALSE;
+ }
+
+ ETFileInfo->version = WavpackGetVersion(wpc);
+ /* .wvc correction file not counted */
+ ETFileInfo->bitrate = WavpackGetAverageBitrate(wpc, 0)/1000;
+ ETFileInfo->samplerate = WavpackGetSampleRate(wpc);
+ ETFileInfo->mode = WavpackGetNumChannels(wpc);
+ ETFileInfo->size = WavpackGetFileSize(wpc);
+ ETFileInfo->duration = WavpackGetNumSamples(wpc)/ETFileInfo->samplerate;
+
+ WavpackCloseFile(wpc);
+
+ return TRUE;
+}
+
+
+gboolean Wavpack_Header_Display_File_Info_To_UI(gchar *filename_utf8, ET_File_Info *ETFileInfo)
+{
+ gchar *text;
+ gchar *time = NULL;
+ gchar *time1 = NULL;
+ gchar *size = NULL;
+ gchar *size1 = NULL;
+
+ /* Encoder version */
+ gtk_label_set_text(GTK_LABEL(VersionLabel),_("Encoder:"));
+ text = g_strdup_printf("%d",ETFileInfo->version);
+ gtk_label_set_text(GTK_LABEL(VersionValueLabel),text);
+ g_free(text);
+
+ /* Bitrate */
+ text = g_strdup_printf(_("%d kb/s"),ETFileInfo->bitrate);
+ gtk_label_set_text(GTK_LABEL(BitrateValueLabel),text);
+ g_free(text);
+
+ /* Samplerate */
+ text = g_strdup_printf(_("%d Hz"),ETFileInfo->samplerate);
+ gtk_label_set_text(GTK_LABEL(SampleRateValueLabel),text);
+ g_free(text);
+
+ /* Mode */
+ gtk_label_set_text(GTK_LABEL(ModeLabel),_("Channels:"));
+ text = g_strdup_printf("%d",ETFileInfo->mode);
+ gtk_label_set_text(GTK_LABEL(ModeValueLabel),text);
+ g_free(text);
+
+ /* Size */
+ size = Convert_Size(ETFileInfo->size);
+ size1 = Convert_Size(ETCore->ETFileDisplayedList_TotalSize);
+ text = g_strdup_printf("%s (%s)",size,size1);
+ gtk_label_set_text(GTK_LABEL(SizeValueLabel),text);
+ g_free(size);
+ g_free(size1);
+ g_free(text);
+
+ /* Duration */
+ time = Convert_Duration(ETFileInfo->duration);
+ time1 = Convert_Duration(ETCore->ETFileDisplayedList_TotalDuration);
+ text = g_strdup_printf("%s (%s)",time,time1);
+ gtk_label_set_text(GTK_LABEL(DurationValueLabel),text);
+ g_free(time);
+ g_free(time1);
+ g_free(text);
+
+ return TRUE;
+}
+
+#endif /* ENABLE_FLAC */
diff --git a/src/wavpack_header.h b/src/wavpack_header.h
new file mode 100755
index 0000000..c61e006
--- /dev/null
+++ b/src/wavpack_header.h
@@ -0,0 +1,41 @@
+/* wavpack_tag.c - 2007/02/15 */
+/*
+ * EasyTAG - Tag editor for many file types
+ * Copyright (C) 2007 Maarten Maathuis (madman2003@gmail.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef __WAVPACK_HEADER_H__
+#define __WAVPACK_HEADER_H__
+
+
+#include "et_core.h"
+
+/****************
+ * Declarations *
+ ****************/
+
+
+/**************
+ * Prototypes *
+ **************/
+
+gboolean Wavpack_Header_Read_File_Info (gchar *filename, ET_File_Info *ETFileInfo);
+gboolean Wavpack_Header_Display_File_Info_To_UI (gchar *filename_utf8, ET_File_Info *ETFileInfo);
+
+
+#endif /* __WAVPACK_HEADER_H__ */
diff --git a/src/wavpack_tag.c b/src/wavpack_tag.c
new file mode 100755
index 0000000..e33f78e
--- /dev/null
+++ b/src/wavpack_tag.c
@@ -0,0 +1,400 @@
+/* wavpack_tag.c - 2007/02/15 */
+/*
+ * EasyTAG - Tag editor for many file types
+ * Copyright (C) 2007 Maarten Maathuis (madman2003@gmail.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+
+#ifdef ENABLE_WAVPACK
+
+#include <gtk/gtk.h>
+#include <glib/gi18n-lib.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <wavpack/wavpack.h>
+
+#include "easytag.h"
+#include "vcedit.h"
+#include "et_core.h"
+#include "picture.h"
+//#include "setting.h"
+#include "charset.h"
+#include "wavpack_tag.h"
+
+
+/***************
+ * Declaration *
+ ***************/
+
+#define MULTIFIELD_SEPARATOR " - "
+
+/**************
+ * Prototypes *
+ **************/
+gboolean Wavpack_Tag_Write_File (FILE *file_in, gchar *filename_in, vcedit_state *state);
+
+
+/*************
+ * Functions *
+ *************/
+
+/*
+ * For the APEv2 tags, the following field names are officially supported and
+ * recommended by WavPack (although there are no restrictions on what field names
+ * may be used):
+ *
+ * Artist
+ * Title
+ * Album
+ * Track
+ * Year
+ * Genre
+ * Comment
+ * Cuesheet (note: may include replay gain info as remarks)
+ * Replay_Track_Gain
+ * Replay_Track_Peak
+ * Replay_Album_Gain
+ * Replay_Album_Peak
+ * Cover Art (Front)
+ * Cover Art (Back)
+ */
+
+/*
+ * Read tag data from a Wavpack file.
+ */
+gboolean Wavpack_Tag_Read_File_Tag (gchar *filename, File_Tag *FileTag)
+{
+ if (!filename || !FileTag)
+ return FALSE;
+
+ WavpackContext *wpc;
+ gchar *field, *field2;
+ guint length;
+
+ int open_flags = OPEN_TAGS;
+
+ wpc = WavpackOpenFileInput(filename, NULL, open_flags, 0);
+
+ if ( wpc == NULL ) {
+ return FALSE;
+ }
+
+ /*
+ * Title
+ */
+ field = g_malloc0(sizeof(char) * MAXLEN);
+ length = WavpackGetTagItem(wpc, "title", field, MAXLEN);
+
+ if ( length > 0 && FileTag->title == NULL ) {
+ FileTag->title = Try_To_Validate_Utf8_String(field);
+ }
+
+ free(field);
+
+ /*
+ * Artist
+ */
+ field = g_malloc0(sizeof(char) * MAXLEN);
+ length = WavpackGetTagItem(wpc, "artist", field, MAXLEN);
+
+ if ( length > 0 && FileTag->artist == NULL) {
+ FileTag->artist = Try_To_Validate_Utf8_String(field);
+ }
+
+ free(field);
+
+ /*
+ * Album
+ */
+ field = g_malloc0(sizeof(char) * MAXLEN);
+ length = WavpackGetTagItem(wpc, "album", field, MAXLEN);
+
+ if ( length > 0 && FileTag->album == NULL ) {
+ FileTag->album = Try_To_Validate_Utf8_String(field);
+ }
+
+ free(field);
+
+ /*
+ * Discnumber
+ */
+ field = g_malloc0(sizeof(char) * MAXLEN);
+ length = WavpackGetTagItem(wpc, "part", field, MAXLEN);
+
+ if ( length > 0 && FileTag->disc_number == NULL ) {
+ FileTag->disc_number = Try_To_Validate_Utf8_String(field);
+ }
+
+ free(field);
+
+ /*
+ * Year
+ */
+ field = g_malloc0(sizeof(char) * MAXLEN);
+ length = WavpackGetTagItem(wpc, "year", field, MAXLEN);
+
+ if ( length > 0 && FileTag->year == NULL ) {
+ FileTag->year = Try_To_Validate_Utf8_String(field);
+ }
+
+ free(field);
+
+ /*
+ * Tracknumber + tracktotal
+ */
+ field = g_malloc0(sizeof(char) * MAXLEN);
+ length = WavpackGetTagItem(wpc, "track", field, MAXLEN);
+ field2 = g_utf8_strchr(field, -1, '/');
+
+ /* Need to cut off the total tracks if present */
+ if (field2) {
+ *field2 = 0;
+ field2++;
+ }
+
+ if ( field2 && FileTag->track_total == NULL ) {
+ FileTag->track_total = Try_To_Validate_Utf8_String(field2);
+ }
+ if ( length > 0 && FileTag->track == NULL ) {
+ FileTag->track = Try_To_Validate_Utf8_String(field);
+ }
+
+ free(field);
+
+ /*
+ * Genre
+ */
+ field = g_malloc0(sizeof(char) * MAXLEN);
+ length = WavpackGetTagItem(wpc, "genre", field, MAXLEN);
+
+ if ( length > 0 && FileTag->genre == NULL ) {
+ FileTag->genre = Try_To_Validate_Utf8_String(field);
+ }
+
+ free(field);
+
+ /*
+ * Comment
+ */
+ field = g_malloc0(sizeof(char) * MAXLEN);
+ length = WavpackGetTagItem(wpc, "comment", field, MAXLEN);
+
+ if ( length > 0 && FileTag->comment == NULL ) {
+ FileTag->comment = Try_To_Validate_Utf8_String(field);
+ }
+
+ free(field);
+
+ /*
+ * Composer
+ */
+ field = g_malloc0(sizeof(char) * MAXLEN);
+ length = WavpackGetTagItem(wpc, "composer", field, MAXLEN);
+
+ if ( length > 0 && FileTag->composer == NULL ) {
+ FileTag->composer = Try_To_Validate_Utf8_String(field);
+ }
+
+ free(field);
+
+ /*
+ * Original artist
+ */
+ field = g_malloc0(sizeof(char) * MAXLEN);
+ length = WavpackGetTagItem(wpc, "original artist", field, MAXLEN);
+
+ if ( length > 0 && FileTag->orig_artist == NULL ) {
+ FileTag->orig_artist = Try_To_Validate_Utf8_String(field);
+ }
+
+ free(field);
+
+ /*
+ * Copyright
+ */
+ field = g_malloc0(sizeof(char) * MAXLEN);
+ length = WavpackGetTagItem(wpc, "copyright", field, MAXLEN);
+
+ if ( length > 0 && FileTag->copyright == NULL ) {
+ FileTag->copyright = Try_To_Validate_Utf8_String(field);
+ }
+
+ free(field);
+
+ /*
+ * URL
+ */
+ field = g_malloc0(sizeof(char) * MAXLEN);
+ length = WavpackGetTagItem(wpc, "copyright url", field, MAXLEN);
+
+ if ( length > 0 && FileTag->url == NULL ) {
+ FileTag->url = Try_To_Validate_Utf8_String(field);
+ }
+
+ free(field);
+
+ /*
+ * Encoded by
+ */
+ field = g_malloc0(sizeof(char) * MAXLEN);
+ length = WavpackGetTagItem(wpc, "encoded by", field, MAXLEN);
+
+ if ( length > 0 && FileTag->encoded_by == NULL ) {
+ FileTag->encoded_by = Try_To_Validate_Utf8_String(field);
+ }
+
+ free(field);
+
+ WavpackCloseFile(wpc);
+
+ return TRUE;
+}
+
+
+gboolean Wavpack_Tag_Write_File_Tag (ET_File *ETFile)
+{
+ if (!ETFile || !ETFile->FileTag)
+ return FALSE;
+
+ WavpackContext *wpc;
+
+ gchar *filename = ((File_Name *)((GList *)ETFile->FileNameCur)->data)->value;
+ File_Tag *FileTag = (File_Tag *)ETFile->FileTag->data;
+ gchar *buffer;
+
+ int open_flags = OPEN_EDIT_TAGS;
+
+ wpc = WavpackOpenFileInput(filename, NULL, open_flags, 0);
+
+ if ( wpc == NULL ) {
+ return FALSE;
+ }
+
+ /*
+ * Title
+ */
+ if (FileTag->title && WavpackAppendTagItem(wpc, "title", FileTag->title, strlen(FileTag->title)) == 0) {
+ return FALSE;
+ }
+
+ /*
+ * Artist
+ */
+ if (FileTag->artist && WavpackAppendTagItem(wpc, "artist", FileTag->artist, strlen(FileTag->artist)) == 0) {
+ return FALSE;
+ }
+
+ /*
+ * Album
+ */
+ if (FileTag->album && WavpackAppendTagItem(wpc, "album", FileTag->album, strlen(FileTag->album)) == 0) {
+ return FALSE;
+ }
+
+ /*
+ * Discnumber
+ */
+ if (FileTag->disc_number && WavpackAppendTagItem(wpc, "part", FileTag->disc_number, strlen(FileTag->disc_number)) == 0) {
+ return FALSE;
+ }
+
+ /*
+ * Year
+ */
+ if (FileTag->year && WavpackAppendTagItem(wpc, "year", FileTag->year, strlen(FileTag->year)) == 0) {
+ return FALSE;
+ }
+
+ /*
+ * Tracknumber + tracktotal
+ */
+ if (FileTag->track_total) {
+ buffer = g_strdup_printf("%s/%s", FileTag->track, FileTag->track_total);
+ if (FileTag->track && WavpackAppendTagItem(wpc, "track", buffer, strlen(buffer)) == 0) {
+ g_free(buffer);
+ return FALSE;
+ } else {
+ g_free(buffer);
+ }
+ } else {
+ if (FileTag->track && WavpackAppendTagItem(wpc, "track", FileTag->track, strlen(FileTag->track)) == 0) {
+ return FALSE;
+ }
+ }
+
+ /*
+ * Genre
+ */
+ if (FileTag->genre && WavpackAppendTagItem(wpc, "genre", FileTag->genre, strlen(FileTag->genre)) == 0) {
+ return FALSE;
+ }
+
+ /*
+ * Comment
+ */
+ if (FileTag->comment && WavpackAppendTagItem(wpc, "comment", FileTag->comment, strlen(FileTag->comment)) == 0) {
+ return FALSE;
+ }
+
+ /*
+ * Composer
+ */
+ if (FileTag->composer && WavpackAppendTagItem(wpc, "composer", FileTag->composer, strlen(FileTag->composer)) == 0) {
+ return FALSE;
+ }
+
+ /*
+ * Original artist
+ */
+ if (FileTag->orig_artist && WavpackAppendTagItem(wpc, "original artist", FileTag->orig_artist, strlen(FileTag->orig_artist)) == 0) {
+ return FALSE;
+ }
+
+ /*
+ * Copyright
+ */
+ if (FileTag->copyright && WavpackAppendTagItem(wpc, "copyright", FileTag->copyright, strlen(FileTag->copyright)) == 0) {
+ return FALSE;
+ }
+
+ /*
+ * URL
+ */
+ if (FileTag->url && WavpackAppendTagItem(wpc, "copyright url", FileTag->url, strlen(FileTag->url)) == 0) {
+ return FALSE;
+ }
+
+ /*
+ * Encoded by
+ */
+ if (FileTag->encoded_by && WavpackAppendTagItem(wpc, "encoded by", FileTag->encoded_by, strlen(FileTag->encoded_by)) == 0) {
+ return FALSE;
+ }
+
+ WavpackWriteTag(wpc);
+
+ WavpackCloseFile(wpc);
+
+ return TRUE;
+}
+
+
+#endif /* ENABLE_WAVPACK */
diff --git a/src/wavpack_tag.h b/src/wavpack_tag.h
new file mode 100755
index 0000000..0ace45b
--- /dev/null
+++ b/src/wavpack_tag.h
@@ -0,0 +1,42 @@
+/* wavpack_tag.c - 2007/02/15 */
+/*
+ * EasyTAG - Tag editor for many file types
+ * Copyright (C) 2007 Maarten Maathuis (madman2003@gmail.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef __WAVPACK_TAG_H__
+#define __WAVPACK_TAG_H__
+
+
+#include <glib.h>
+#include "et_core.h"
+
+/***************
+ * Declaration *
+ ***************/
+
+#define MAXLEN 1024
+
+/**************
+ * Prototypes *
+ **************/
+gboolean Wavpack_Tag_Read_File_Tag (gchar *filename, File_Tag *FileTag);
+gboolean Wavpack_Tag_Write_File_Tag (ET_File *ETFile);
+
+
+#endif /* __WAVPACK_TAG_H__ */
diff --git a/src/win32/easytag.rc b/src/win32/easytag.rc
new file mode 100755
index 0000000..cd8fd58
--- /dev/null
+++ b/src/win32/easytag.rc
@@ -0,0 +1,4 @@
+#include "resource.h"
+
+EASYTAG_ICON ICON "../pixmaps/easytag.ico"
+
diff --git a/src/win32/resource.h b/src/win32/resource.h
new file mode 100755
index 0000000..4cd30ad
--- /dev/null
+++ b/src/win32/resource.h
@@ -0,0 +1 @@
+#define EASYTAG_ICON 109
diff --git a/src/win32/win32dep.c b/src/win32/win32dep.c
new file mode 100755
index 0000000..a644adc
--- /dev/null
+++ b/src/win32/win32dep.c
@@ -0,0 +1,493 @@
+/*
+ * easytag
+ *
+ * File: win32dep.c
+ * Date: June, 2002
+ * Description: Windows dependant code for Easytag
+ * this code if largely taken from win32 gaim
+ *
+ * Copyright (C) 2002-2003, Herman Bloggs <hermanator12002@yahoo.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include <windows.h>
+#include <io.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <winuser.h>
+#include <shlobj.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/timeb.h>
+
+#include <gtk/gtk.h>
+#include <glib.h>
+#if GLIB_CHECK_VERSION(2,6,0)
+# include <glib/gstdio.h>
+#else
+# define g_fopen fopen
+# define g_unlink unlink
+#endif
+#include <gdk/gdkwin32.h>
+
+#include "resource.h"
+//#include "../log.h"
+
+#include <libintl.h>
+
+#include "win32dep.h"
+
+/*
+ * DEFINES & MACROS
+ */
+#define _(x) gettext(x)
+
+
+/*
+ * DATA STRUCTS
+ */
+
+/* For shfolder.dll */
+typedef HRESULT (CALLBACK* LPFNSHGETFOLDERPATHA)(HWND, int, HANDLE, DWORD, LPSTR);
+typedef HRESULT (CALLBACK* LPFNSHGETFOLDERPATHW)(HWND, int, HANDLE, DWORD, LPWSTR);
+
+typedef enum
+{
+ SHGFP_TYPE_CURRENT = 0, // current value for user, verify it exists
+ SHGFP_TYPE_DEFAULT = 1, // default value, may not exist
+} SHGFP_TYPE;
+
+/*
+ * LOCALS
+ */
+static char app_data_dir[MAX_PATH + 1] = "C:";
+static char install_dir[MAXPATHLEN];
+static char locale_dir[MAXPATHLEN];
+
+static void str_replace_char (gchar *str, gchar in_char, gchar out_char);
+
+/*
+ * GLOBALS
+ */
+HINSTANCE ET_exe_hInstance = 0;
+HINSTANCE ET_dll_hInstance = 0;
+
+/*
+ * PROTOS
+ */
+LPFNSHGETFOLDERPATHA MySHGetFolderPathA = NULL;
+LPFNSHGETFOLDERPATHW MySHGetFolderPathW = NULL;
+
+FARPROC ET_Win32_Find_And_Loadproc (char*, char*);
+char* ET_Win32_Data_Dir (void);
+
+
+/*
+ * PUBLIC CODE
+ */
+
+HINSTANCE ET_Win32_Hinstance (void)
+{
+ return ET_exe_hInstance;
+}
+
+/* Escape windows dir separators. This is needed when paths are saved,
+ and on being read back have their '\' chars used as an escape char.
+ Returns an allocated string which needs to be freed.
+*/
+char* ET_Win32_Escape_Dirsep (char* filename )
+{
+ int sepcount=0;
+ char* ret=NULL;
+ int cnt=0;
+
+ ret = filename;
+ while(*ret)
+ {
+ if(*ret == '\\')
+ sepcount++;
+ ret++;
+ }
+ ret = g_malloc0(strlen(filename) + sepcount + 1);
+ while(*filename)
+ {
+ ret[cnt] = *filename;
+ if(*filename == '\\')
+ ret[++cnt] = '\\';
+ filename++;
+ cnt++;
+ }
+ ret[cnt] = '\0';
+ return ret;
+}
+
+/* this is used by libmp4v2 : what is it doing here you think ? well...search! */
+int gettimeofday (struct timeval *t, void *foo)
+{
+ struct _timeb temp;
+
+ if (t != 0) {
+ _ftime(&temp);
+ t->tv_sec = temp.time; /* seconds since 1-1-1970 */
+ t->tv_usec = temp.millitm * 1000; /* microseconds */
+ }
+ return (0);
+}
+
+
+/* emulate the unix function */
+int mkstemp (char *template)
+{
+ int fd = -1;
+
+ char *str = mktemp(template);
+ if(str != NULL)
+ {
+ fd = open(str, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
+ }
+
+ return fd;
+}
+
+/* Determine whether the specified dll contains the specified procedure.
+ If so, load it (if not already loaded). */
+FARPROC ET_Win32_Find_And_Loadproc ( char* dllname, char* procedure )
+{
+ HMODULE hmod;
+ int did_load=0;
+ FARPROC proc = 0;
+
+ if(!(hmod=GetModuleHandle(dllname)))
+ {
+ //Log_Print(_("DLL '%s' not found. Try loading it..."), dllname);
+ g_printf(_("DLL '%s' not found. Try loading it..."), dllname);
+ g_print("\n");
+ if(!(hmod = LoadLibrary(dllname)))
+ {
+ //Log_Print(_("DLL '%s' could not be loaded"), dllname);
+ g_print(_("DLL '%s' could not be loaded"), dllname);
+ g_print("\n");
+ return NULL;
+ }
+ else
+ did_load = TRUE;
+ }
+
+ if((proc=GetProcAddress(hmod, procedure)))
+ {
+ //Log_Print(_("This version of '%s' contains '%s'"), dllname, procedure);
+ g_print(_("This version of '%s' contains '%s'"), dllname, procedure);
+ g_print("\n");
+ return proc;
+ }
+ else
+ {
+ //Log_Print(_("Function '%s' not found in dll '%s'"), procedure, dllname);
+ g_print(_("Function '%s' not found in dll '%s'"), procedure, dllname);
+ g_print("\n");
+ if(did_load)
+ {
+ /* unload dll */
+ FreeLibrary(hmod);
+ }
+ return NULL;
+ }
+}
+
+/* Determine Easytag Paths during Runtime */
+
+char* ET_Win32_Install_Dir (void)
+{
+ HMODULE hmod;
+ char* buf;
+
+ hmod = GetModuleHandle(NULL);
+ if ( hmod == 0 )
+ {
+ buf = g_win32_error_message( GetLastError() );
+ //Log_Print("GetModuleHandle error: %s\n", buf);
+ g_print("GetModuleHandle error: %s\n", buf);
+ g_free(buf);
+ return NULL;
+ }
+ if (GetModuleFileName( hmod, (char*)&install_dir, MAXPATHLEN ) == 0)
+ {
+ buf = g_win32_error_message( GetLastError() );
+ //Log_Print("GetModuleFileName error: %s\n", buf);
+ g_print("GetModuleFileName error: %s\n", buf);
+ g_free(buf);
+ return NULL;
+ }
+ buf = g_path_get_dirname( install_dir );
+ strcpy( (char*)&install_dir, buf );
+ g_free( buf );
+
+ return (char*)&install_dir;
+}
+
+
+char* ET_Win32_Locale_Dir (void)
+{
+ strcpy(locale_dir, ET_Win32_Install_Dir());
+ g_strlcat(locale_dir, G_DIR_SEPARATOR_S "locale", sizeof(locale_dir));
+ return (char*)&locale_dir;
+}
+
+char* ET_Win32_Data_Dir (void)
+{
+ return (char*)&app_data_dir;
+}
+
+
+/* Miscellaneous */
+
+gboolean ET_Win32_Read_Reg_String (HKEY key, char* sub_key, char* val_name, LPBYTE data, LPDWORD data_len)
+{
+ HKEY hkey;
+ gboolean ret = FALSE;
+
+ if(ERROR_SUCCESS == RegOpenKeyEx(key, sub_key, 0, KEY_QUERY_VALUE, &hkey))
+ {
+ if (ERROR_SUCCESS == RegQueryValueEx(hkey, val_name, 0, NULL, data, data_len))
+ ret = TRUE;
+ RegCloseKey(key);
+ }
+ return ret;
+}
+
+/* find a default player executable */
+char* ET_Win32_Get_Audio_File_Player (void)
+{
+ DWORD len = 256;
+ char key_value[256];
+
+ char *player;
+
+ if(ET_Win32_Read_Reg_String(HKEY_CURRENT_USER, "Software\\foobar2000", "InstallDir", key_value, &len))
+ {
+ player = g_strconcat(key_value, "\\foobar2000.exe", NULL);
+ }
+ else if(ET_Win32_Read_Reg_String(HKEY_CURRENT_USER, "Software\\Winamp", "", key_value, &len))
+ {
+ player = g_strconcat(key_value, "\\winamp.exe", NULL);
+ }
+ else
+ {
+ player = g_strdup("");
+ }
+
+ //Log_Print(_("Audio player: '%s'"), player);
+ g_print(_("Audio player: '%s'"), player);
+ g_print("\n");
+
+ return player;
+}
+
+
+void ET_Win32_Notify_Uri (const char *uri)
+{
+ SHELLEXECUTEINFO sinfo;
+
+ memset(&sinfo, 0, sizeof(sinfo));
+ sinfo.cbSize = sizeof(sinfo);
+ sinfo.fMask = SEE_MASK_CLASSNAME;
+ sinfo.lpVerb = "open";
+ sinfo.lpFile = uri;
+ sinfo.nShow = SW_SHOWNORMAL;
+ sinfo.lpClass = "http";
+
+ /* We'll allow whatever URI schemes are supported by the
+ default http browser.
+ */
+ if(!ShellExecuteEx(&sinfo))
+ //Log_Print("Error opening URI: %s error: %d\n", uri, (int)sinfo.hInstApp);
+ g_print("Error opening URI: %s error: %d\n", uri, (int)sinfo.hInstApp);
+}
+
+
+
+void str_replace_char (gchar *str, gchar in_char, gchar out_char)
+{
+ while(*str)
+ {
+ if(*str == in_char)
+ *str = out_char;
+ str++;
+ }
+}
+
+
+
+/* Remove trailing '/' if any */
+void ET_Win32_Path_Remove_Trailing_Slash (gchar *path)
+{
+ int path_len = strlen(path);
+
+ if(path_len > 3 && path[path_len - 1] == '/')
+ {
+ path[path_len - 1] = '\0';
+ }
+}
+
+/* Remove trailing '\' if any, but not when 'C:\' */
+void ET_Win32_Path_Remove_Trailing_Backslash (gchar *path)
+{
+ int path_len = strlen(path);
+
+ if(path_len > 3 && path[path_len - 1] == '\\')
+ {
+ path[path_len - 1] = '\0';
+ }
+}
+
+void ET_Win32_Path_Replace_Backslashes (gchar *path)
+{
+ str_replace_char(path, '\\', '/');
+}
+
+void ET_Win32_Path_Replace_Slashes (gchar *path)
+{
+ str_replace_char(path, '/', '\\');
+}
+
+void ET_Win32_Init (HINSTANCE hint)
+{
+ WORD wVersionRequested;
+ WSADATA wsaData;
+ char *newenv;
+
+ ET_exe_hInstance = hint;
+
+ /* Winsock init */
+ wVersionRequested = MAKEWORD( 2, 2 );
+ WSAStartup( wVersionRequested, &wsaData );
+
+ /* Confirm that the winsock DLL supports 2.2 */
+ /* Note that if the DLL supports versions greater than
+ 2.2 in addition to 2.2, it will still return 2.2 in
+ wVersion since that is the version we requested. */
+ if ( LOBYTE( wsaData.wVersion ) != 2
+ || HIBYTE( wsaData.wVersion ) != 2 )
+ {
+ g_print("Could not find a usable WinSock DLL. Oh well.\n");
+ WSACleanup();
+ }
+
+ /* Set app data dir, used by easytag_home_dir */
+ newenv = (char*)g_getenv("EASYTAGHOME");
+ if(!newenv)
+ {
+#if GLIB_CHECK_VERSION(2,6,0)
+ if ((MySHGetFolderPathW = (LPFNSHGETFOLDERPATHW) ET_Win32_Find_And_Loadproc("shfolder.dll", "SHGetFolderPathW")))
+ {
+ wchar_t utf_16_dir[MAX_PATH +1];
+ char *temp;
+ MySHGetFolderPathW(NULL, CSIDL_APPDATA, NULL,
+ SHGFP_TYPE_CURRENT, utf_16_dir);
+ temp = g_utf16_to_utf8(utf_16_dir, -1, NULL, NULL, NULL);
+ g_strlcpy(app_data_dir, temp, sizeof(app_data_dir));
+ g_free(temp);
+ } else if ((MySHGetFolderPathA = (LPFNSHGETFOLDERPATHA) ET_Win32_Find_And_Loadproc("shfolder.dll", "SHGetFolderPathA")))
+ {
+ char locale_dir[MAX_PATH + 1];
+ char *temp;
+ MySHGetFolderPathA(NULL, CSIDL_APPDATA, NULL,
+ SHGFP_TYPE_CURRENT, locale_dir);
+ temp = g_locale_to_utf8(locale_dir, -1, NULL, NULL, NULL);
+ g_strlcpy(app_data_dir, temp, sizeof(app_data_dir));
+ g_free(temp);
+ }
+#else
+ if ((MySHGetFolderPathA = (LPFNSHGETFOLDERPATHA) ET_Win32_Find_And_Loadproc("shfolder.dll", "SHGetFolderPathA")))
+ {
+ MySHGetFolderPathA(NULL, CSIDL_APPDATA, NULL,
+ SHGFP_TYPE_CURRENT, app_data_dir);
+ }
+#endif
+ else
+ {
+ strcpy(app_data_dir, "C:");
+ }
+ }
+ else
+ {
+ g_strlcpy(app_data_dir, newenv, sizeof(app_data_dir));
+ }
+
+ //ET_Win32_Path_Replace_Backslashes(app_data_dir);
+
+ //Log_Print(_("EasyTAG settings dir: '%s'"), app_data_dir);
+ g_print(_("EasyTAG settings dir: '%s'"), app_data_dir);
+ g_print("\n");
+
+ newenv = g_strdup_printf("HOME=%s", app_data_dir);
+
+ if (putenv(newenv)<0)
+ g_print("putenv failed\n");
+ g_free(newenv);
+
+}
+
+/* Windows Cleanup */
+
+void ET_Win32_Cleanup (void)
+{
+ /* winsock cleanup */
+ WSACleanup();
+}
+
+/* DLL initializer */
+BOOL WINAPI DllMain ( HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved )
+{
+ ET_dll_hInstance = hinstDLL;
+ return TRUE;
+}
+
+
+/*
+ * Copyright (C) 2002 Manuel Novoa III
+ * Copyright (C) 2000-2005 Erik Andersen <andersen@uclibc.org>
+ *
+ * Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball.
+ */
+
+#include <string.h>
+#include <ctype.h>
+
+char *strcasestr(const char *s1, const char *s2)
+{
+ register const char *s = s1;
+ register const char *p = s2;
+
+ do {
+ if (!*p) {
+ return (char *) s1;;
+ }
+ if ((*p == *s)
+ || (tolower(*((unsigned char *)p)) == tolower(*((unsigned char *)s)))
+ ) {
+ ++p;
+ ++s;
+ } else {
+ p = s2;
+ if (!*s) {
+ return NULL;
+ }
+ s = ++s1;
+ }
+ } while (1);
+} \ No newline at end of file
diff --git a/src/win32/win32dep.h b/src/win32/win32dep.h
new file mode 100755
index 0000000..7e5e1ae
--- /dev/null
+++ b/src/win32/win32dep.h
@@ -0,0 +1,84 @@
+/*
+ * easytag
+ *
+ * File: win32dep.h
+ *
+ * Copyright (C) 2002-2003, Herman Bloggs <hermanator12002@yahoo.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#ifndef _WIN32DEP_H_
+#define _WIN32DEP_H_
+
+
+#include <winsock2.h>
+#include <process.h>
+#include <gtk/gtk.h>
+#include <gdk/gdkevents.h>
+
+#define lstat stat
+#define mkdir(a,b) mkdir(a)
+#define chown(a,b,c)
+#define chmod(a,b)
+
+//#define strcasestr(haystack, needle) strstr(haystack, needle)
+char *strcasestr(const char *s1, const char *s2);
+
+
+
+/*
+ * PROTOS
+ */
+
+/**
+ ** win32dep.c
+ **/
+/* Windows helper functions */
+extern int mkstemp(char *template);
+
+extern HINSTANCE ET_Win32_Hinstance (void);
+extern gboolean ET_Win32_Read_Reg_String (HKEY key, char* sub_key, char* val_name, LPBYTE data, LPDWORD data_len);
+extern char* ET_Win32_Escape_Dirsep (char*);
+
+/* Determine ET paths */
+extern char* ET_Win32_Install_Dir (void);
+extern char* ET_Win32_Locale_Dir (void);
+extern char* ET_Win32_Data_Dir (void);
+
+/* Misc */
+extern char * ET_Win32_Get_Audio_File_Player (void);
+extern void ET_Win32_Notify_Uri (const char *uri);
+
+/* init / cleanup */
+extern void ET_Win32_Init (HINSTANCE);
+extern void ET_Win32_Cleanup (void);
+
+extern void ET_Win32_Path_Remove_Trailing_Slash (gchar *path);
+extern void ET_Win32_Path_Remove_Trailing_Backslash (gchar *path);
+extern void ET_Win32_Path_Replace_Backslashes (gchar *path);
+extern void ET_Win32_Path_Replace_Slashes (gchar *path);
+
+/*
+ * MACROS
+ */
+
+/*
+ * ET specific
+ */
+#define DATADIR ET_Win32_Install_Dir()
+#define LOCALE ET_Win32_Locale_Dir()
+
+#endif /* _WIN32DEP_H_ */
diff --git a/src/win32/win_easytag.c b/src/win32/win_easytag.c
new file mode 100755
index 0000000..cd6628d
--- /dev/null
+++ b/src/win32/win_easytag.c
@@ -0,0 +1,557 @@
+/*
+ * Code taken from GAIM 2.0.0b5
+ *
+ */
+/*
+ * win_easytag.c
+ *
+ * Date: June, 2002
+ * Description: Entry point for win32 easytag, and various win32 dependant
+ * routines.
+ *
+ * EasyTAG is the legal property of its developers, whose names are too numerous
+ * to list here. Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef _WIN32_WINNT
+#define _WIN32_WINNT 0x501
+#endif
+#include <windows.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#define WIN32_PROXY_REGKEY "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings"
+
+/* These will hopefully be in the win32api next time it is updated - at which point, we'll remove them */
+#ifndef LANG_PERSIAN
+#define LANG_PERSIAN 0x29
+#endif
+#ifndef LANG_BOSNIAN
+#define SUBLANG_BOSNIAN_BOSNIA_HERZEGOVINA_LATIN 0x05
+#define SUBLANG_BOSNIAN_BOSNIA_HERZEGOVINA_CYRILLIC 0x08
+#endif
+#ifndef SUBLANG_CROATIAN_BOSNIA_HERZEGOVINA_LATIN
+#define SUBLANG_CROATIAN_BOSNIA_HERZEGOVINA_LATIN 0x04
+#endif
+#ifndef LANG_XHOSA
+#define LANG_XHOSA 0x34
+#endif
+
+
+typedef int (CALLBACK* LPFNEASYTAGMAIN)(HINSTANCE, int, char**);
+typedef void (CALLBACK* LPFNSETDLLDIRECTORY)(LPCTSTR);
+typedef BOOL (CALLBACK* LPFNATTACHCONSOLE)(DWORD);
+
+/*
+ * PROTOTYPES
+ */
+static LPFNEASYTAGMAIN easytag_main = NULL;
+static LPFNSETDLLDIRECTORY MySetDllDirectory = NULL;
+
+
+static const char *get_win32_error_message(DWORD err) {
+ static char err_msg[512];
+
+ FormatMessage(
+ FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL, err,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPTSTR) &err_msg, sizeof(err_msg), NULL);
+
+ return err_msg;
+}
+
+static BOOL read_reg_string(HKEY key, char* sub_key, char* val_name, LPBYTE data, LPDWORD data_len) {
+ HKEY hkey;
+ BOOL ret = FALSE;
+ LONG retv;
+
+ if (ERROR_SUCCESS == (retv = RegOpenKeyEx(key, sub_key, 0,
+ KEY_QUERY_VALUE, &hkey))) {
+ if (ERROR_SUCCESS == (retv = RegQueryValueEx(hkey, val_name,
+ NULL, NULL, data, data_len)))
+ ret = TRUE;
+ else {
+ const char *err_msg = get_win32_error_message(retv);
+
+ printf("Could not read reg key '%s' subkey '%s' value: '%s'. Message: (%ld) %s\n",
+ ((key == HKEY_LOCAL_MACHINE) ? "HKLM" :
+ (key == HKEY_CURRENT_USER) ? "HKCU" :
+ "???"),
+ sub_key, val_name, retv, err_msg);
+ }
+ RegCloseKey(hkey);
+ }
+ else {
+ TCHAR szBuf[80];
+
+ FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, retv, 0,
+ (LPTSTR) &szBuf, sizeof(szBuf), NULL);
+ printf("Could not open reg subkey: '%s'. Error: (%ld) %s\n",
+ sub_key, retv, szBuf);
+ }
+
+ return ret;
+}
+
+static void dll_prep() {
+ char path[MAX_PATH + 1];
+ HMODULE hmod;
+ HKEY hkey;
+ char gtkpath[MAX_PATH + 1];
+ DWORD plen;
+
+ plen = sizeof(gtkpath);
+ hkey = HKEY_CURRENT_USER;
+ if (!read_reg_string(hkey, "SOFTWARE\\GTK\\2.0", "Path",
+ (LPBYTE) &gtkpath, &plen)) {
+ hkey = HKEY_LOCAL_MACHINE;
+ if (!read_reg_string(hkey, "SOFTWARE\\GTK\\2.0", "Path",
+ (LPBYTE) &gtkpath, &plen)) {
+ printf("GTK+ Path Registry Key not found. "
+ "Assuming GTK+ is in the PATH.\n");
+ return;
+ }
+ }
+
+ /* this value is replaced during a successful RegQueryValueEx() */
+ plen = sizeof(path);
+ /* Determine GTK+ dll path .. */
+ if (!read_reg_string(hkey, "SOFTWARE\\GTK\\2.0", "DllPath",
+ (LPBYTE) &path, &plen)) {
+ strcpy(path, gtkpath);
+ strcat(path, "\\bin");
+ }
+
+ printf("GTK+ path found: %s\n", path);
+
+ if ((hmod = GetModuleHandle("kernel32.dll"))) {
+ MySetDllDirectory = (LPFNSETDLLDIRECTORY) GetProcAddress(
+ hmod, "SetDllDirectoryA");
+ if (!MySetDllDirectory)
+ printf("SetDllDirectory not supported\n");
+ } else
+ printf("Error getting kernel32.dll module handle\n");
+
+ /* For Windows XP SP1+ / Server 2003 we use SetDllDirectory to avoid dll hell */
+ if (MySetDllDirectory) {
+ printf("Using SetDllDirectory\n");
+ MySetDllDirectory(path);
+ }
+
+ /* For the rest, we set the current directory and make sure
+ * SafeDllSearch is set to 0 where needed. */
+ else {
+ OSVERSIONINFO osinfo;
+
+ printf("Setting current directory to GTK+ dll directory\n");
+ SetCurrentDirectory(path);
+ /* For Windows 2000 (SP3+) / WinXP (No SP):
+ * If SafeDllSearchMode is set to 1, Windows system directories are
+ * searched for dlls before the current directory. Therefore we set it
+ * to 0.
+ */
+ osinfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+ GetVersionEx(&osinfo);
+ if ((osinfo.dwMajorVersion == 5 &&
+ osinfo.dwMinorVersion == 0 &&
+ strcmp(osinfo.szCSDVersion, "Service Pack 3") >= 0) ||
+ (osinfo.dwMajorVersion == 5 &&
+ osinfo.dwMinorVersion == 1 &&
+ strcmp(osinfo.szCSDVersion, "") >= 0)
+ ) {
+ DWORD regval = 1;
+ DWORD reglen = sizeof(DWORD);
+
+ printf("Using Win2k (SP3+) / WinXP (No SP)... Checking SafeDllSearch\n");
+ read_reg_string(HKEY_LOCAL_MACHINE,
+ "System\\CurrentControlSet\\Control\\Session Manager",
+ "SafeDllSearchMode",
+ (LPBYTE) &regval,
+ &reglen);
+
+ if (regval != 0) {
+ printf("Trying to set SafeDllSearchMode to 0\n");
+ regval = 0;
+ if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+ "System\\CurrentControlSet\\Control\\Session Manager",
+ 0, KEY_SET_VALUE, &hkey
+ ) == ERROR_SUCCESS) {
+ if (RegSetValueEx(hkey,
+ "SafeDllSearchMode", 0,
+ REG_DWORD, (LPBYTE) &regval,
+ sizeof(DWORD)
+ ) != ERROR_SUCCESS)
+ printf("Error writing SafeDllSearchMode. Error: %u\n",
+ (UINT) GetLastError());
+ RegCloseKey(hkey);
+ } else
+ printf("Error opening Session Manager key for writing. Error: %u\n",
+ (UINT) GetLastError());
+ } else
+ printf("SafeDllSearchMode is set to 0\n");
+ }/*end else*/
+ }
+}
+
+static char* lcid_to_posix(LCID lcid) {
+ char *posix = NULL;
+ int lang_id = PRIMARYLANGID(lcid);
+ int sub_id = SUBLANGID(lcid);
+
+ switch (lang_id) {
+ case LANG_ARABIC: posix = "ar"; break;
+ case LANG_AZERI: posix = "az"; break;
+ case LANG_BENGALI: posix = "bn"; break;
+ case LANG_BULGARIAN: posix = "bg"; break;
+ case LANG_CATALAN: posix = "ca"; break;
+ case LANG_CHINESE:
+ switch (sub_id) {
+ case SUBLANG_CHINESE_SIMPLIFIED:
+ posix = "zh_CN"; break;
+ case SUBLANG_CHINESE_TRADITIONAL:
+ posix = "zh_TW"; break;
+ default:
+ posix = "zh"; break;
+ }
+ break;
+ case LANG_CZECH: posix = "cs"; break;
+ case LANG_DANISH: posix = "da"; break;
+ case LANG_ESTONIAN: posix = "et"; break;
+ case LANG_PERSIAN: posix = "fa"; break;
+ case LANG_GERMAN: posix = "de"; break;
+ case LANG_GREEK: posix = "el"; break;
+ case LANG_ENGLISH:
+ switch (sub_id) {
+ case SUBLANG_ENGLISH_UK:
+ posix = "en_GB"; break;
+ case SUBLANG_ENGLISH_AUS:
+ posix = "en_AU"; break;
+ case SUBLANG_ENGLISH_CAN:
+ posix = "en_CA"; break;
+ default:
+ posix = "en"; break;
+ }
+ break;
+ case LANG_SPANISH: posix = "es"; break;
+ case LANG_BASQUE: posix = "eu"; break;
+ case LANG_FINNISH: posix = "fi"; break;
+ case LANG_FRENCH: posix = "fr"; break;
+ case LANG_GALICIAN: posix = "gl"; break;
+ case LANG_GUJARATI: posix = "gu"; break;
+ case LANG_HEBREW: posix = "he"; break;
+ case LANG_HINDI: posix = "hi"; break;
+ case LANG_HUNGARIAN: posix = "hu"; break;
+ case LANG_ICELANDIC: break;
+ case LANG_ITALIAN: posix = "it"; break;
+ case LANG_JAPANESE: posix = "ja"; break;
+ case LANG_GEORGIAN: posix = "ka"; break;
+ case LANG_KOREAN: posix = "ko"; break;
+ case LANG_LITHUANIAN: posix = "lt"; break;
+ case LANG_MACEDONIAN: posix = "mk"; break;
+ case LANG_DUTCH: posix = "nl"; break;
+ case LANG_NEPALI: posix = "ne"; break;
+ case LANG_NORWEGIAN:
+ switch (sub_id) {
+ case SUBLANG_NORWEGIAN_BOKMAL:
+ posix = "nb"; break;
+ case SUBLANG_NORWEGIAN_NYNORSK:
+ posix = "nn"; break;
+ }
+ break;
+ case LANG_PUNJABI: posix = "pa"; break;
+ case LANG_POLISH: posix = "pl"; break;
+ case LANG_PORTUGUESE:
+ switch (sub_id) {
+ case SUBLANG_PORTUGUESE_BRAZILIAN:
+ posix = "pt_BR"; break;
+ default:
+ posix = "pt"; break;
+ }
+ break;
+ case LANG_ROMANIAN: posix = "ro"; break;
+ case LANG_RUSSIAN: posix = "ru"; break;
+ /* LANG_CROATIAN == LANG_SERBIAN == LANG_BOSNIAN */
+ case LANG_SERBIAN:
+ switch (sub_id) {
+ case SUBLANG_SERBIAN_LATIN:
+ posix = "sr@Latn"; break;
+ case SUBLANG_SERBIAN_CYRILLIC:
+ posix = "sr"; break;
+ case SUBLANG_BOSNIAN_BOSNIA_HERZEGOVINA_CYRILLIC:
+ case SUBLANG_BOSNIAN_BOSNIA_HERZEGOVINA_LATIN:
+ posix = "bs"; break;
+ case SUBLANG_CROATIAN_BOSNIA_HERZEGOVINA_LATIN:
+ posix = "hr"; break;
+ }
+ break;
+ case LANG_SLOVAK: posix = "sk"; break;
+ case LANG_SLOVENIAN: posix = "sl"; break;
+ case LANG_ALBANIAN: posix = "sq"; break;
+ case LANG_SWEDISH: posix = "sv"; break;
+ case LANG_TAMIL: posix = "ta"; break;
+ case LANG_TELUGU: posix = "te"; break;
+ case LANG_THAI: posix = "th"; break;
+ case LANG_TURKISH: posix = "tr"; break;
+ case LANG_UKRAINIAN: posix = "uk"; break;
+ case LANG_VIETNAMESE: posix = "vi"; break;
+ case LANG_XHOSA: posix = "xh"; break;
+ case LANG_URDU: break;
+ case LANG_INDONESIAN: break;
+ case LANG_BELARUSIAN: break;
+ case LANG_LATVIAN: break;
+ case LANG_ARMENIAN: break;
+ case LANG_AFRIKAANS: break;
+ case LANG_FAEROESE: break;
+ case LANG_MALAY: break;
+ case LANG_KAZAK: break;
+ case LANG_KYRGYZ: break;
+ case LANG_SWAHILI: break;
+ case LANG_UZBEK: break;
+ case LANG_TATAR: break;
+ case LANG_ORIYA: break;
+ case LANG_KANNADA: break;
+ case LANG_MALAYALAM: break;
+ case LANG_ASSAMESE: break;
+ case LANG_MARATHI: break;
+ case LANG_SANSKRIT: break;
+ case LANG_MONGOLIAN: break;
+ case LANG_KONKANI: break;
+ case LANG_MANIPURI: break;
+ case LANG_SINDHI: break;
+ case LANG_SYRIAC: break;
+ case LANG_KASHMIRI: break;
+ case LANG_DIVEHI: break;
+ }
+
+ /* Deal with exceptions */
+ if (posix == NULL) {
+ switch (lcid) {
+ case 0x0455: posix = "my_MM"; break; /* Myanmar (Burmese) */
+ case 9999: posix = "ku"; break; /* Kurdish (from NSIS) */
+ }
+ }
+
+ return posix;
+}
+
+/* Determine and set Eastytag locale as follows (in order of priority):
+ - Check EASYTAGLANG env var
+ - Check NSIS Installer Language reg value
+ - Use default user locale
+*/
+static const char *get_locale() {
+ const char *locale = NULL;
+ LCID lcid;
+ char data[10];
+ DWORD datalen = 10;
+
+ /* Check if user set EASYTAGLANG env var */
+ if ((locale = getenv("EASYTAGLANG"))) {
+ printf("Variable EASYTAGLANG defined: '%s'\n",locale);
+ return locale;
+ }else
+ printf("Variable EASYTAGLANG not defined\n");
+
+ if (read_reg_string(HKEY_CURRENT_USER, "SOFTWARE\\easytag",
+ "Installer Language", (LPBYTE) &data, &datalen)) {
+ if ((locale = lcid_to_posix(atoi(data))))
+ return locale;
+ }
+
+ lcid = GetUserDefaultLCID();
+ if ((locale = lcid_to_posix(lcid)))
+ return locale;
+
+ return "en";
+}
+
+static void set_locale() {
+ const char *locale = NULL;
+ char envstr[25];
+
+ locale = get_locale();
+
+ snprintf(envstr, 25, "LANG=%s", locale);
+ printf("Setting locale: %s\n", envstr);
+ putenv(envstr);
+}
+
+#if 0
+#define WM_FOCUS_REQUEST (WM_APP + 13)
+
+static BOOL set_running() {
+ HANDLE h;
+
+ if ((h = CreateMutex(NULL, FALSE, "easytag_is_running"))) {
+ if (GetLastError() == ERROR_ALREADY_EXISTS) {
+ HWND msg_win;
+
+ if((msg_win = FindWindow(TEXT("WineasytagMsgWinCls"), NULL)))
+ if(SendMessage(msg_win, WM_FOCUS_REQUEST, (WPARAM) NULL, (LPARAM) NULL))
+ return FALSE;
+
+ /* If we get here, the focus request wasn't successful */
+
+ MessageBox(NULL,
+ "An instance of EasyTAG is already running",
+ NULL, MB_OK | MB_TOPMOST);
+
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+#endif
+
+static void set_proxy() {
+ DWORD regval = 1;
+ DWORD reglen = sizeof(DWORD);
+
+ /* If the proxy server environment variables are already set,
+ * we shouldn't override them */
+ if (getenv("HTTP_PROXY") || getenv("http_proxy") || getenv("HTTPPROXY"))
+ return;
+
+ if (read_reg_string(HKEY_CURRENT_USER, WIN32_PROXY_REGKEY,
+ "ProxyEnable",
+ (LPBYTE) &regval, &reglen) && (regval & 1)) {
+ char proxy_server[2048];
+ char *c = NULL;
+ reglen = sizeof(proxy_server);
+
+ if (!read_reg_string(HKEY_CURRENT_USER, WIN32_PROXY_REGKEY,
+ "ProxyServer", (LPBYTE) &proxy_server, &reglen))
+ return;
+
+ if ((reglen > strlen("http="))
+ && (c = strstr(proxy_server, "http="))) {
+ char *d;
+ c += strlen("http=");
+ d = strchr(c, ';');
+ if (d) {
+ *d = '\0';
+ }
+ /* c now points the proxy server (and port) */
+ }
+
+ if (c) {
+ const char envstr_prefix[] = "HTTP_PROXY=http://";
+ char envstr[sizeof(envstr_prefix) + strlen(c) + 1];
+ snprintf(envstr, sizeof(envstr), "%s%s",
+ envstr_prefix, c);
+ printf("Setting HTTP Proxy: %s\n", envstr);
+ putenv(envstr);
+ }
+ }
+
+}
+
+#ifdef __GNUC__
+# ifndef _stdcall
+# define _stdcall __attribute__((stdcall))
+# endif
+#endif
+
+int _stdcall
+WinMain (struct HINSTANCE__ *hInstance, struct HINSTANCE__ *hPrevInstance,
+ char *lpszCmdLine, int nCmdShow) {
+ char errbuf[512];
+ HMODULE hmod;
+
+ /* If debug or help or version flag used, create console for output */
+ if (strstr(lpszCmdLine, "-d") || strstr(lpszCmdLine, "-h") || strstr(lpszCmdLine, "-v")) {
+ /* If stdout hasn't been redirected to a file, alloc a console
+ * (_istty() doesn't work for stuff using the GUI subsystem) */
+ if (_fileno(stdout) == -1) {
+ LPFNATTACHCONSOLE MyAttachConsole = NULL;
+ if ((hmod = GetModuleHandle("kernel32.dll"))) {
+ MyAttachConsole =
+ (LPFNATTACHCONSOLE)
+ GetProcAddress(hmod, "AttachConsole");
+ }
+ if ((MyAttachConsole && MyAttachConsole(ATTACH_PARENT_PROCESS))
+ || AllocConsole()) {
+ freopen("CONOUT$", "w", stdout);
+ freopen("CONOUT$", "w", stderr);
+ }
+ }
+ }
+#if 0
+ /* Load exception handler if we have it */
+ if (GetModuleFileName(NULL, gaimdir, MAX_PATH) != 0) {
+ char *tmp = gaimdir;
+ char *prev = NULL;
+
+ while ((tmp = strchr(tmp, '\\'))) {
+ prev = tmp;
+ tmp++;
+ }
+
+ if (prev) {
+ prev[0] = '\0';
+ strcat(gaimdir, "\\exchndl.dll");
+ if (LoadLibrary(gaimdir))
+ printf("Loaded exchndl.dll\n");
+ }
+ } else {
+ DWORD dw = GetLastError();
+ const char *err_msg = get_win32_error_message(dw);
+ snprintf(errbuf, 512,
+ "Error getting module filename. Error: (%u) %s",
+ (UINT) dw, err_msg);
+ printf(errbuf);
+ MessageBox(NULL, errbuf, NULL, MB_OK | MB_TOPMOST);
+ }
+#endif
+
+ dll_prep();
+
+ set_locale();
+ /* If help or version flag used, do not check Mutex */
+ //if (!strstr(lpszCmdLine, "-h") && !strstr(lpszCmdLine, "-v"))
+ // if (!getenv("GAIM_MULTI_INST") && !wgaim_set_running())
+ // return 0;
+
+ set_proxy();
+
+ /* Now we are ready for EasyTag .. */
+ if ((hmod = LoadLibrary("easytag.dll"))) {
+ easytag_main = (LPFNEASYTAGMAIN) GetProcAddress(hmod, "easytag_main");
+ }
+
+ if (!easytag_main) {
+ DWORD dw = GetLastError();
+ BOOL mod_not_found = (dw == ERROR_MOD_NOT_FOUND || dw == ERROR_DLL_NOT_FOUND);
+ const char *err_msg = get_win32_error_message(dw);
+
+ snprintf(errbuf, 512, "Error loading 'easytag.dll'. Error: (%u) %s%s%s",
+ (UINT) dw, err_msg,
+ mod_not_found ? "\n" : "",
+ mod_not_found ? "This probably means that GTK+ can't be found." : "");
+ printf(errbuf);
+ MessageBox(NULL, errbuf, TEXT("Error"), MB_OK | MB_TOPMOST);
+
+ return 0;
+ }
+
+ return easytag_main (hInstance, __argc, __argv);
+}