diff options
Diffstat (limited to 'grecord/src')
-rw-r--r-- | grecord/src/Makefile.am | 30 | ||||
-rw-r--r-- | grecord/src/Makefile.in | 667 | ||||
-rw-r--r-- | grecord/src/gsr-window.c | 2759 | ||||
-rw-r--r-- | grecord/src/gsr-window.h | 61 | ||||
-rw-r--r-- | grecord/src/mate-recorder.c | 232 | ||||
-rw-r--r-- | grecord/src/ui.xml | 41 |
6 files changed, 3790 insertions, 0 deletions
diff --git a/grecord/src/Makefile.am b/grecord/src/Makefile.am new file mode 100644 index 0000000..58800fa --- /dev/null +++ b/grecord/src/Makefile.am @@ -0,0 +1,30 @@ +## Process this file with automake to produce Makefile.in + +AM_CPPFLAGS = \ + -DMATELOCALEDIR=\""$(prefix)/$(DATADIRNAME)/locale"\" \ + -I$(top_srcdir) \ + -I$(top_srcdir)/grecord/src/recent-files/ \ + $(GSR_CFLAGS) \ + $(DISABLE_DEPRECATED) \ + -DDATADIR=\""$(datadir)"\" \ + -DGSR_DATADIR=\""$(datadir)/mate-sound-recorder"\" \ + -DGSR_UIDIR=\""$(datadir)/mate-sound-recorder/ui/"\" + +bin_PROGRAMS = mate-sound-recorder + +mate_sound_recorder_SOURCES = \ + mate-recorder.c \ + gsr-window.c \ + gsr-window.h + +uidir = $(datadir)/mate-sound-recorder/ui +ui_DATA = ui.xml + +EXTRA_DIST = $(ui_DATA) + +mate_sound_recorder_LDADD = \ + -lm \ + $(GSR_LIBS) -lgstinterfaces-@GST_MAJORMINOR@ \ + $(top_builddir)/profiles/libmate-media-profiles.la + +-include $(top_srcdir)/git.mk diff --git a/grecord/src/Makefile.in b/grecord/src/Makefile.in new file mode 100644 index 0000000..b6db1f8 --- /dev/null +++ b/grecord/src/Makefile.in @@ -0,0 +1,667 @@ +# Makefile.in generated by automake 1.11.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009 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@ + + +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +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 = mate-sound-recorder$(EXEEXT) +subdir = grecord/src +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/as-ac-expand.m4 \ + $(top_srcdir)/m4/as-compiler-flag.m4 \ + $(top_srcdir)/m4/as-version.m4 $(top_srcdir)/m4/intltool.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ + $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ + $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/m4/mate-doc-utils.m4 $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(SHELL) $(top_srcdir)/build-aux/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(uidir)" +PROGRAMS = $(bin_PROGRAMS) +am_mate_sound_recorder_OBJECTS = mate-recorder.$(OBJEXT) \ + gsr-window.$(OBJEXT) +mate_sound_recorder_OBJECTS = $(am_mate_sound_recorder_OBJECTS) +am__DEPENDENCIES_1 = +mate_sound_recorder_DEPENDENCIES = $(am__DEPENDENCIES_1) \ + $(top_builddir)/profiles/libmate-media-profiles.la +AM_V_lt = $(am__v_lt_$(V)) +am__v_lt_ = $(am__v_lt_$(AM_DEFAULT_VERBOSITY)) +am__v_lt_0 = --silent +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/build-aux/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_$(V)) +am__v_CC_ = $(am__v_CC_$(AM_DEFAULT_VERBOSITY)) +am__v_CC_0 = @echo " CC " $@; +AM_V_at = $(am__v_at_$(V)) +am__v_at_ = $(am__v_at_$(AM_DEFAULT_VERBOSITY)) +am__v_at_0 = @ +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_$(V)) +am__v_CCLD_ = $(am__v_CCLD_$(AM_DEFAULT_VERBOSITY)) +am__v_CCLD_0 = @echo " CCLD " $@; +AM_V_GEN = $(am__v_GEN_$(V)) +am__v_GEN_ = $(am__v_GEN_$(AM_DEFAULT_VERBOSITY)) +am__v_GEN_0 = @echo " GEN " $@; +SOURCES = $(mate_sound_recorder_SOURCES) +DIST_SOURCES = $(mate_sound_recorder_SOURCES) +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +DATA = $(ui_DATA) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +ALL_LINGUAS = @ALL_LINGUAS@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CATALOGS = @CATALOGS@ +CATOBJEXT = @CATOBJEXT@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DATADIRNAME = @DATADIRNAME@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DISABLE_DEPRECATED = @DISABLE_DEPRECATED@ +DISTCHECK_CONFIGURE_FLAGS = @DISTCHECK_CONFIGURE_FLAGS@ +DLLTOOL = @DLLTOOL@ +DOC_USER_FORMATS = @DOC_USER_FORMATS@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GETTEXT_PACKAGE = @GETTEXT_PACKAGE@ +GLADEUI_CATALOG_DIR = @GLADEUI_CATALOG_DIR@ +GLADEUI_CFLAGS = @GLADEUI_CFLAGS@ +GLADEUI_LIBS = @GLADEUI_LIBS@ +GLADEUI_MODULE_DIR = @GLADEUI_MODULE_DIR@ +GLIB_GENMARSHAL = @GLIB_GENMARSHAL@ +GMOFILES = @GMOFILES@ +GMP_CFLAGS = @GMP_CFLAGS@ +GMP_LIBS = @GMP_LIBS@ +GMSGFMT = @GMSGFMT@ +GREP = @GREP@ +GSR_CFLAGS = @GSR_CFLAGS@ +GSR_LIBS = @GSR_LIBS@ +GSTMIXER_CFLAGS = @GSTMIXER_CFLAGS@ +GSTMIXER_LIBS = @GSTMIXER_LIBS@ +GSTPROPS_CFLAGS = @GSTPROPS_CFLAGS@ +GSTPROPS_LIBS = @GSTPROPS_LIBS@ +GST_MAJORMINOR = @GST_MAJORMINOR@ +HAVE_PULSEAUDIO = @HAVE_PULSEAUDIO@ +HAVE_SOUND_THEME = @HAVE_SOUND_THEME@ +HELP_DIR = @HELP_DIR@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INSTOBJEXT = @INSTOBJEXT@ +INTLLIBS = @INTLLIBS@ +INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@ +INTLTOOL_MERGE = @INTLTOOL_MERGE@ +INTLTOOL_PERL = @INTLTOOL_PERL@ +INTLTOOL_UPDATE = @INTLTOOL_UPDATE@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MATECC_DESKTOP_DIR = @MATECC_DESKTOP_DIR@ +MATECONFTOOL = @MATECONFTOOL@ +MATECONF_SCHEMA_CONFIG_SOURCE = @MATECONF_SCHEMA_CONFIG_SOURCE@ +MATECONF_SCHEMA_FILE_DIR = @MATECONF_SCHEMA_FILE_DIR@ +MKDIR_P = @MKDIR_P@ +MKINSTALLDIRS = @MKINSTALLDIRS@ +MSGFMT = @MSGFMT@ +MSGFMT_OPTS = @MSGFMT_OPTS@ +MSGMERGE = @MSGMERGE@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OMF_DIR = @OMF_DIR@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PACKAGE_VERSION_MAJOR = @PACKAGE_VERSION_MAJOR@ +PACKAGE_VERSION_MICRO = @PACKAGE_VERSION_MICRO@ +PACKAGE_VERSION_MINOR = @PACKAGE_VERSION_MINOR@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +POFILES = @POFILES@ +POSUB = @POSUB@ +PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@ +PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@ +PROGRAMS_GSTPROPS = @PROGRAMS_GSTPROPS@ +PULSEAUDIO_CFLAGS = @PULSEAUDIO_CFLAGS@ +PULSEAUDIO_LIBS = @PULSEAUDIO_LIBS@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SOUNDTHEME_CFLAGS = @SOUNDTHEME_CFLAGS@ +SOUNDTHEME_LIBS = @SOUNDTHEME_LIBS@ +SOUND_THEME_CFLAGS = @SOUND_THEME_CFLAGS@ +SOUND_THEME_LIBS = @SOUND_THEME_LIBS@ +STRIP = @STRIP@ +USE_NLS = @USE_NLS@ +VERSION = @VERSION@ +VOLUME_CONTROL_CFLAGS = @VOLUME_CONTROL_CFLAGS@ +VOLUME_CONTROL_LIBS = @VOLUME_CONTROL_LIBS@ +WARN_CFLAGS = @WARN_CFLAGS@ +WARN_CXXFLAGS = @WARN_CXXFLAGS@ +XGETTEXT = @XGETTEXT@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +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@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +AM_CPPFLAGS = \ + -DMATELOCALEDIR=\""$(prefix)/$(DATADIRNAME)/locale"\" \ + -I$(top_srcdir) \ + -I$(top_srcdir)/grecord/src/recent-files/ \ + $(GSR_CFLAGS) \ + $(DISABLE_DEPRECATED) \ + -DDATADIR=\""$(datadir)"\" \ + -DGSR_DATADIR=\""$(datadir)/mate-sound-recorder"\" \ + -DGSR_UIDIR=\""$(datadir)/mate-sound-recorder/ui/"\" + +mate_sound_recorder_SOURCES = \ + mate-recorder.c \ + gsr-window.c \ + gsr-window.h + +uidir = $(datadir)/mate-sound-recorder/ui +ui_DATA = ui.xml +EXTRA_DIST = $(ui_DATA) +mate_sound_recorder_LDADD = \ + -lm \ + $(GSR_LIBS) -lgstinterfaces-@GST_MAJORMINOR@ \ + $(top_builddir)/profiles/libmate-media-profiles.la + +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 ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign grecord/src/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign grecord/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 +$(am__aclocal_m4_deps): +install-binPROGRAMS: $(bin_PROGRAMS) + @$(NORMAL_INSTALL) + test -z "$(bindir)" || $(MKDIR_P) "$(DESTDIR)$(bindir)" + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + for p in $$list; do echo "$$p $$p"; done | \ + sed 's/$(EXEEXT)$$//' | \ + while read p p1; do if test -f $$p || test -f $$p1; \ + then echo "$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n;h' -e 's|.*|.|' \ + -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ + sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) files[d] = files[d] " " $$1; \ + else { print "f", $$3 "/" $$4, $$1; } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-binPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ + -e 's/$$/$(EXEEXT)/' `; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(bindir)" && rm -f $$files + +clean-binPROGRAMS: + @list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list +mate-sound-recorder$(EXEEXT): $(mate_sound_recorder_OBJECTS) $(mate_sound_recorder_DEPENDENCIES) + @rm -f mate-sound-recorder$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(mate_sound_recorder_OBJECTS) $(mate_sound_recorder_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gsr-window.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mate-recorder.Po@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@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@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@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@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@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 +install-uiDATA: $(ui_DATA) + @$(NORMAL_INSTALL) + test -z "$(uidir)" || $(MKDIR_P) "$(DESTDIR)$(uidir)" + @list='$(ui_DATA)'; test -n "$(uidir)" || list=; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(uidir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(uidir)" || exit $$?; \ + done + +uninstall-uiDATA: + @$(NORMAL_UNINSTALL) + @list='$(ui_DATA)'; test -n "$(uidir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + test -n "$$files" || exit 0; \ + echo " ( cd '$(DESTDIR)$(uidir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(uidir)" && rm -f $$files + +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; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + set x; \ + 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; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + 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; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__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)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$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 $(PROGRAMS) $(DATA) +installdirs: + for dir in "$(DESTDIR)$(bindir)" "$(DESTDIR)$(uidir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +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) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_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-binPROGRAMS clean-generic clean-libtool mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-uiDATA + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-binPROGRAMS + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +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-binPROGRAMS uninstall-uiDATA + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-binPROGRAMS \ + clean-generic clean-libtool ctags distclean distclean-compile \ + distclean-generic distclean-libtool distclean-tags distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-binPROGRAMS install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip install-uiDATA 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-binPROGRAMS uninstall-uiDATA + + +-include $(top_srcdir)/git.mk + +# 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/grecord/src/gsr-window.c b/grecord/src/gsr-window.c new file mode 100644 index 0000000..4d50a44 --- /dev/null +++ b/grecord/src/gsr-window.c @@ -0,0 +1,2759 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Authors: Iain Holmes <[email protected]> + * Johan Dahlin <[email protected]> + * Tim-Philipp Müller <tim centricular net> + * + * Copyright 2002 Iain Holmes + * + * 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. + * + * 4th Februrary 2005: Christian Schaller: changed license to LGPL with + * permission of Iain Holmes, Ronald Bultje, Leontine Binchy (SUN), Johan Dalhin + * and Joe Marcus Clarke + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <string.h> +#include <math.h> + +#include <glib/gi18n.h> +#include <gio/gio.h> +#include <gtk/gtk.h> +#include <mateconf/mateconf-client.h> +#include <gst/gst.h> +#include <gst/interfaces/mixer.h> + +#include <profiles/mate-media-profiles.h> + +#include "gsr-window.h" + +GST_DEBUG_CATEGORY_STATIC (gsr_debug); +#define GST_CAT_DEFAULT gsr_debug + +extern GtkWidget * gsr_open_window (const char *filename); +extern void gsr_quit (void); + +extern MateConfClient *mateconf_client; + +extern void gsr_add_recent (gchar *filename); + +#define MATECONF_DIR "/apps/mate-sound-recorder/" +#define KEY_OPEN_DIR MATECONF_DIR "system-state/open-file-directory" +#define KEY_SAVE_DIR MATECONF_DIR "system-state/save-file-directory" +#define KEY_LAST_PROFILE_ID MATECONF_DIR "last-profile-id" +#define KEY_LAST_INPUT MATECONF_DIR "last-input" +#define EBUSY_TRY_AGAIN 3 /* Empirical data */ + +typedef struct _GSRWindowPipeline { + GstElement *pipeline; + GstState state; /* last seen (async) pipeline state */ + GstBus *bus; + + GstElement *src; + GstElement *sink; + + guint tick_id; +} GSRWindowPipeline; + +enum { + PROP_0, + PROP_LOCATION +}; + +static GtkWindowClass *parent_class = NULL; + +#define GSR_WINDOW_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), GSR_TYPE_WINDOW, GSRWindowPrivate)) + +struct _GSRWindowPrivate { + GtkWidget *main_vbox; + GtkWidget *scale; + GtkWidget *profile, *input; + GtkWidget *rate, *time_sec, *format, *channels; + GtkWidget *input_label; + GtkWidget *name_label; + GtkWidget *length_label; + GtkWidget *align; + GtkWidget *volume_label; + GtkWidget *level; + + gulong seek_id; + + GtkUIManager *ui_manager; + GtkActionGroup *action_group; + GtkWidget *recent_view; + GtkRecentFilter *recent_filter; + + /* statusbar */ + GtkWidget *statusbar; + guint status_message_cid; + guint tip_message_cid; + + /* Pipelines */ + GSRWindowPipeline *play; + GSRWindowPipeline *record; + char *record_filename; + char *filename; + char *extension; + char *working_file; /* Working file: Operations only occur on the + working file. The result of that operation then + becomes the new working file. */ + int record_fd; + + /* File info */ + int len_secs; /* In seconds */ + int get_length_attempts; + + /* ATOMIC access */ + struct { + gint n_channels; + gint bitrate; + gint samplerate; + } atomic; + + gboolean has_file; + gboolean saved; + gboolean dirty; + gboolean seek_in_progress; + + gboolean quit_after_save; + + guint32 tick_id; /* tick_callback timeout ID */ + guint32 record_id; /* record idle callback timeout ID */ + + GstElement *ebusy_pipeline; /* which pipeline we're trying to start */ + guint ebusy_timeout_id; + + GstElement *source; + GstMixer *mixer; +}; + +static gboolean make_record_source (GSRWindow *window); +static void fill_record_input (GSRWindow *window, gchar *selected); +static GSRWindowPipeline * make_record_pipeline (GSRWindow *window); +static GSRWindowPipeline * make_play_pipeline (GSRWindow *window); + +static void +show_error_dialog (GtkWindow *window, + const gchar *debug_message, + const gchar *format, ...) G_GNUC_PRINTF (3,4); + +static void +show_error_dialog (GtkWindow *win, const gchar *dbg, const gchar * format, ...) +{ + GtkWidget *dialog; + va_list args; + gchar *s; + + va_start (args, format); + s = g_strdup_vprintf (format, args); + va_end (args); + + dialog = gtk_message_dialog_new (win, + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_CLOSE, + "%s", + s); + + if (dbg != NULL) { + g_printerr ("ERROR: %s\nDEBUG MESSAGE: %s\n", s, dbg); + } + + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); + g_free (s); +} + +static void +show_missing_known_element_error (GtkWindow *win, gchar *description, + gchar *element, gchar *plugin, gchar *module) +{ + show_error_dialog (win, NULL, + _("Could not create the GStreamer %s element.\n" + "Please install the '%s' plugin from the '%s' module.\n" + "Verify that the installation is correct by running\n" + " gst-inspect-0.10 %s\n" + "and then restart mate-sound-recorder."), + description, plugin, module, element); +} + +static void +show_profile_error (GtkWindow *win, gchar *debug, gchar *description, + const char *profile) +{ + gchar *first; + + first = g_strdup_printf (description, profile); + show_error_dialog (win, debug, "%s%s", first, + _("Please verify its settings.\n" + "You may be missing the necessary plugins.")); + g_free (first); +} +/* Why do we need this? when a bin changes from READY => NULL state, its + * bus is set to flushing and we're unlikely to ever see any of its messages + * if the bin's state reaches NULL before we/the watch in the main thread + * collects them. That's why we set the state to READY first, process all + * messages 'manually', and then finally set it to NULL. This makes sure + * our state-changed handler actually gets to see all the state changes */ + +static void +set_pipeline_state_to_null (GstElement *pipeline) +{ + GstMessage *msg; + GstState cur_state, pending; + GstBus *bus; + + gst_element_get_state (pipeline, &cur_state, &pending, 0); + + if (cur_state == GST_STATE_NULL && pending == GST_STATE_VOID_PENDING) + return; + + if (cur_state == GST_STATE_NULL && pending != GST_STATE_VOID_PENDING) { + gst_element_set_state (pipeline, GST_STATE_NULL); + return; + } + + gst_element_set_state (pipeline, GST_STATE_READY); + gst_element_get_state (pipeline, NULL, NULL, -1); + + bus = gst_element_get_bus (pipeline); + while ((msg = gst_bus_pop (bus))) { + gst_bus_async_signal_func (bus, msg, NULL); + } + gst_object_unref (bus); + + gst_element_set_state (pipeline, GST_STATE_NULL); + /* maybe we should be paranoid and do _get_state() and check for + * the return value here, but then errors in shutdown should be + * rather unlikely */ +} + + +static void +shutdown_pipeline (GSRWindowPipeline *pipe) +{ + gst_bus_set_flushing (pipe->bus, TRUE); + gst_bus_remove_signal_watch (pipe->bus); + gst_element_set_state (pipe->pipeline, GST_STATE_NULL); + gst_object_unref (pipe->pipeline); + gst_object_unref (pipe->bus); +} + +static char * +seconds_to_string (guint seconds) +{ + int hour, min, sec; + + min = (seconds / 60); + hour = min / 60; + min -= (hour * 60); + sec = seconds - ((hour * 3600) + (min * 60)); + + if (hour > 0) { + return g_strdup_printf ("%d:%02d:%02d", hour, min, sec); + } else { + return g_strdup_printf ("%d:%02d", min, sec); + } +} + +static char * +seconds_to_full_string (guint seconds) +{ + long days, hours, minutes; + char *time = NULL; + const char *minutefmt; + const char *hourfmt; + const char *secondfmt; + + days = seconds / (60 * 60 * 24); + hours = (seconds / (60 * 60)); + minutes = (seconds / 60) - ((days * 24 * 60) + (hours * 60)); + seconds = seconds % 60; + + minutefmt = ngettext ("%ld minute", "%ld minutes", minutes); + hourfmt = ngettext ("%ld hour", "%ld hours", hours); + secondfmt = ngettext ("%ld second", "%ld seconds", seconds); + + if (hours > 0) { + if (minutes > 0) + if (seconds > 0) { + char *fmt; + /* Translators: the format is "X hours, X minutes and X seconds" */ + fmt = g_strdup_printf (_("%s, %s and %s"), hourfmt, minutefmt, secondfmt); + time = g_strdup_printf (fmt, hours, minutes, seconds); + g_free (fmt); + } else { + char *fmt; + /* Translators: the format is "X hours and X minutes" */ + fmt = g_strdup_printf (_("%s and %s"), hourfmt, minutefmt); + time = g_strdup_printf (fmt, hours, minutes); + g_free (fmt); + } + else + if (seconds > 0) { + char *fmt; + /* Translators: the format is "X minutes and X seconds" */ + fmt = g_strdup_printf (_("%s and %s"), minutefmt, secondfmt); + time = g_strdup_printf (fmt, minutes, seconds); + g_free (fmt); + } else { + time = g_strdup_printf (minutefmt, minutes); + } + } else { + if (minutes > 0) { + if (seconds > 0) { + char *fmt; + /* Translators: the format is "X minutes and X seconds" */ + fmt = g_strdup_printf (_("%s and %s"), minutefmt, secondfmt); + time = g_strdup_printf (fmt, minutes, seconds); + g_free (fmt); + } else { + time = g_strdup_printf (minutefmt, minutes); + } + + } else { + time = g_strdup_printf (secondfmt, seconds); + } + } + + return time; +} + +static void +set_action_sensitive (GSRWindow *window, + const char *name, + gboolean sensitive) +{ + GtkAction *action = gtk_action_group_get_action (window->priv->action_group, + name); + gtk_action_set_sensitive (action, sensitive); +} + +static void +file_new_cb (GtkAction *action, + GSRWindow *window) +{ + gsr_open_window (NULL); +} + +static void +file_open_cb (GtkAction *action, + GSRWindow *window) +{ + GtkWidget *file_chooser; + gchar *directory; + gchar *locale_directory = NULL; + gint response; + + g_return_if_fail (GSR_IS_WINDOW (window)); + + file_chooser = gtk_file_chooser_dialog_new (_("Open a File"), + GTK_WINDOW (window), + GTK_FILE_CHOOSER_ACTION_OPEN, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_OPEN, GTK_RESPONSE_OK, + NULL); + + directory = mateconf_client_get_string (mateconf_client, KEY_OPEN_DIR, NULL); + + if (directory != NULL && *directory != 0) { + locale_directory = g_filename_from_utf8 (directory, -1, NULL, NULL, NULL); + if (!locale_directory || !g_file_test (locale_directory, G_FILE_TEST_EXISTS)) + locale_directory = g_strdup (directory); + gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (file_chooser), + locale_directory); + g_free (locale_directory); + } + + response = gtk_dialog_run (GTK_DIALOG (file_chooser)); + + if (response == GTK_RESPONSE_OK) { + gchar *name; + gchar *utf8_name = NULL; + + name = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (file_chooser)); + if (name) { + gchar *dirname; + + utf8_name = g_filename_to_utf8 (name, -1, NULL, NULL, NULL); + dirname = g_path_get_dirname (utf8_name); + mateconf_client_set_string (mateconf_client, KEY_OPEN_DIR, dirname, NULL); + g_free (dirname); + g_free (utf8_name); + + if (window->priv->has_file == TRUE) { + /* Just open a new window with the file */ + gsr_open_window (name); + } else { + /* Set the file in this window */ + g_object_set (G_OBJECT (window), "location", name, NULL); + window->priv->dirty = FALSE; + } + + g_free (name); + } + } + + gtk_widget_destroy (file_chooser); + g_free (directory); +} + +static void +file_open_recent_cb (GtkRecentChooser *chooser, + GSRWindow *window) +{ + gchar *uri; + gchar *filename; + + uri = gtk_recent_chooser_get_current_uri (chooser); + g_return_if_fail (uri != NULL); + + if (!g_str_has_prefix (uri, "file://")) + return; + + filename = g_filename_from_uri (uri, NULL, NULL); + if (filename == NULL) + goto out; + + if (!g_file_test (filename, G_FILE_TEST_EXISTS)) { + gchar *filename_utf8; + GtkWidget *dlg; + + filename_utf8 = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL); + dlg = gtk_message_dialog_new (GTK_WINDOW (window), + GTK_DIALOG_MODAL, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + _("Unable to load file:\n%s"), filename_utf8); + + gtk_widget_show (dlg); + gtk_dialog_run (GTK_DIALOG (dlg)); + gtk_widget_destroy (dlg); + + gtk_recent_manager_remove_item (gtk_recent_manager_get_default (), uri, NULL); + + g_free (filename_utf8); + goto out; + } + + if (window->priv->has_file == TRUE) { + /* Just open a new window with the file */ + gsr_open_window (filename); + } else { + /* Set the file in this window */ + g_object_set (G_OBJECT (window), "location", filename, NULL); + window->priv->dirty = FALSE; + } + + out: + g_free (filename); + g_free (uri); +} + +#if 0 +static gboolean +cb_iterate (GstBin *bin, + gpointer data) +{ + src = gst_element_get_child (bin, "sink"); + sink = gst_element_get_child (bin, "sink"); + + if (src && sink) { + gint64 pos, tot, enc; + GstFormat fmt = GST_FORMAT_BYTES; + + gst_element_query (src, GST_QUERY_POSITION, &fmt, &pos); + gst_element_query (src, GST_QUERY_TOTAL, &fmt, &tot); + gst_element_query (sink, GST_QUERY_POSITION, &fmt, &enc); + + g_print ("Iterate: %lld/%lld -> %lld\n", pos, tot, enc); + } else + g_print ("Iterate ?\n"); + + /* we don't do anything here */ + return FALSE; +} +#endif + +static gboolean +handle_ebusy_error (GSRWindow *window) +{ + g_return_val_if_fail (window->priv->ebusy_pipeline != NULL, FALSE); + + gst_element_set_state (window->priv->ebusy_pipeline, GST_STATE_NULL); + gst_element_get_state (window->priv->ebusy_pipeline, NULL, NULL, -1); + gst_element_set_state (window->priv->ebusy_pipeline, GST_STATE_PLAYING); + + /* Try only once */ + return FALSE; +} + +static GstElement * +notgst_element_get_toplevel (GstElement * element) +{ + g_return_val_if_fail (element != NULL, NULL); + g_return_val_if_fail (GST_IS_ELEMENT (element), NULL); + + do { + GstElement *parent; + + parent = (GstElement *) gst_element_get_parent (element); + + if (parent == NULL) + break; + + gst_object_unref (parent); + element = parent; + } while (1); + + return element; +} + +static void +pipeline_error_cb (GstBus * bus, GstMessage * msg, GSRWindow * window) +{ + GstElement *pipeline; + GError *error = NULL; + gchar *dbg = NULL; + + g_return_if_fail (GSR_IS_WINDOW (window)); + + gst_message_parse_error (msg, &error, &dbg); + g_return_if_fail (error != NULL); + + pipeline = notgst_element_get_toplevel (GST_ELEMENT (msg->src)); + + if (error->code == GST_RESOURCE_ERROR_BUSY) { + if (window->priv->ebusy_timeout_id == 0) { + set_action_sensitive (window, "FileSave", FALSE); + set_action_sensitive (window, "FileSaveAs", FALSE); + set_action_sensitive (window, "Play", FALSE); + set_action_sensitive (window, "Record", FALSE); + + window->priv->ebusy_pipeline = pipeline; + + window->priv->ebusy_timeout_id = + g_timeout_add_seconds (EBUSY_TRY_AGAIN, + (GSourceFunc) handle_ebusy_error, + window); + + g_error_free (error); + g_free (dbg); + return; + } + } + + if (window->priv->ebusy_timeout_id) { + g_source_remove (window->priv->ebusy_timeout_id); + window->priv->ebusy_timeout_id = 0; + window->priv->ebusy_pipeline = NULL; + } + + + /* set pipeline to NULL before showing error dialog to make sure + * the audio device is freed, in case any accessability software + * wants to make use of it to read out the error message */ + set_pipeline_state_to_null (pipeline); + + show_error_dialog (GTK_WINDOW (window), dbg, "%s", error->message); + + gdk_window_set_cursor (gtk_widget_get_window (window->priv->main_vbox), NULL); + + set_action_sensitive (window, "Stop", FALSE); + set_action_sensitive (window, "Play", TRUE); + set_action_sensitive (window, "Record", TRUE); + set_action_sensitive (window, "FileSave", TRUE); + set_action_sensitive (window, "FileSaveAs", TRUE); + gtk_widget_set_sensitive (window->priv->scale, TRUE); + + gtk_statusbar_pop (GTK_STATUSBAR (window->priv->statusbar), + window->priv->status_message_cid); + gtk_statusbar_push (GTK_STATUSBAR (window->priv->statusbar), + window->priv->status_message_cid, + _("Ready")); + + g_error_free (error); + g_free (dbg); +} + +static GtkWidget * +gsr_dialog_add_button (GtkDialog *dialog, + const gchar *text, + const gchar *stock_id, + gint response_id) +{ + GtkWidget *button; + + g_return_val_if_fail (GTK_IS_DIALOG (dialog), NULL); + g_return_val_if_fail (text != NULL, NULL); + g_return_val_if_fail (stock_id != NULL, NULL); + + button = gtk_button_new_with_mnemonic (text); + gtk_button_set_image (GTK_BUTTON (button), + gtk_image_new_from_stock (stock_id, + GTK_ICON_SIZE_BUTTON)); + + gtk_widget_set_can_default (button, TRUE); + + gtk_widget_show (button); + + gtk_dialog_add_action_widget (dialog, button, response_id); + + return button; +} + +static gboolean +replace_dialog (GtkWindow *parent, + const gchar *message, + const gchar *file_name) +{ + GtkWidget *message_dialog; + gint ret; + + g_return_val_if_fail (file_name != NULL, FALSE); + + message_dialog = gtk_message_dialog_new (parent, + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_QUESTION, + GTK_BUTTONS_NONE, + message, + file_name); + /* Add cancel button */ + gtk_dialog_add_button (GTK_DIALOG (message_dialog), + GTK_STOCK_CANCEL, + GTK_RESPONSE_CANCEL); + /* Add replace button */ + gsr_dialog_add_button (GTK_DIALOG (message_dialog), _("_Replace"), + GTK_STOCK_REFRESH, + GTK_RESPONSE_YES); + + gtk_dialog_set_default_response (GTK_DIALOG (message_dialog), GTK_RESPONSE_CANCEL); + gtk_window_set_resizable (GTK_WINDOW (message_dialog), FALSE); + ret = gtk_dialog_run (GTK_DIALOG (message_dialog)); + gtk_widget_destroy (GTK_WIDGET (message_dialog)); + + return (ret == GTK_RESPONSE_YES); +} + +static gboolean +replace_existing_file (GtkWindow *parent, + const gchar *file_name) +{ + return replace_dialog (parent, + _("A file named \"%s\" already exists. \n" + "Do you want to replace it with the " + "one you are saving?"), + file_name); +} + +static void +do_save_file (GSRWindow *window, + const char *_name) +{ + GSRWindowPrivate *priv; + char *name; + GFile *src, *dst; + GError *error = NULL; + + priv = window->priv; + + if (window->priv->extension == NULL || + g_str_has_suffix (_name, window->priv->extension)) + name = g_strdup (_name); + else + name = g_strdup_printf ("%s.%s", _name, + window->priv->extension); + if (g_file_test (name, G_FILE_TEST_EXISTS)) { + char *utf8_name; + utf8_name = g_filename_to_utf8 (name, -1, NULL, NULL, NULL); + if (!replace_existing_file (GTK_WINDOW (window), utf8_name)) { + g_free (utf8_name); + return; + } + g_free (utf8_name); + } + src = g_file_new_for_path(priv->record_filename); + dst = g_file_new_for_path(name); + + /* TODO: Show progress? Where? */ + if (g_file_copy(src, dst, G_FILE_COPY_OVERWRITE, + NULL, NULL, NULL, &error)) { + g_object_set (G_OBJECT (window), "location", name, NULL); + priv->dirty = FALSE; + window->priv->saved = TRUE; + if (window->priv->quit_after_save == TRUE) { + gsr_window_close (window); + } + } else { + char *utf8_name; + utf8_name = g_filename_to_utf8 (name, -1, NULL, NULL, NULL); + show_error_dialog (GTK_WINDOW (window), NULL, + _("Could not save the file \"%s\""), utf8_name); + g_free (utf8_name); + } + + g_object_unref(src); + g_object_unref(dst); + g_free (name); +} + +static void +file_save_as_cb (GtkAction *action, + GSRWindow *window) +{ + GtkWidget *file_chooser; + gchar *directory; + gchar *locale_directory = NULL; + gint response; + + g_return_if_fail (GSR_IS_WINDOW (window)); + + file_chooser = gtk_file_chooser_dialog_new (_("Save file as"), + GTK_WINDOW (window), + GTK_FILE_CHOOSER_ACTION_SAVE, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_SAVE, GTK_RESPONSE_OK, + NULL); + + directory = mateconf_client_get_string (mateconf_client, KEY_SAVE_DIR, NULL); + if (directory != NULL && *directory != 0) { + locale_directory = g_filename_from_utf8 (directory, -1, NULL, NULL, NULL); + if (!locale_directory || !g_file_test (locale_directory, G_FILE_TEST_EXISTS)) + locale_directory = g_strdup (directory); + gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (file_chooser), locale_directory); + g_free (locale_directory); + } + g_free (directory); + + if (window->priv->filename != NULL) { + char *locale_basename; + char *basename = NULL; + gchar *filename, *filename_ext, *extension; + gint length; + + locale_basename = g_path_get_basename (window->priv->filename); + basename = g_filename_to_utf8 (locale_basename, -1, NULL, NULL, NULL); + length = strlen (basename); + extension = g_strrstr (basename, "."); + + if (extension != NULL) { + length = length - strlen (extension); + } + + filename = g_strndup (basename,length); + if (window->priv->extension) + filename_ext = g_strdup_printf ("%s.%s", filename, + window->priv->extension); + else + filename_ext = g_strdup (filename); + gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (file_chooser), + filename_ext); + g_free (filename); + g_free (filename_ext); + g_free (basename); + g_free (locale_basename); + } + + response = gtk_dialog_run (GTK_DIALOG (file_chooser)); + + if (response == GTK_RESPONSE_OK) { + gchar *name; + gchar *utf8_name = NULL; + + name = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (file_chooser)); + if (name) { + gchar *dirname; + + utf8_name= g_filename_to_utf8 (name, -1, NULL, NULL, NULL); + dirname = g_path_get_dirname (utf8_name); + mateconf_client_set_string (mateconf_client, KEY_SAVE_DIR, dirname, NULL); + g_free (dirname); + g_free (utf8_name); + + do_save_file (window, name); + g_free (name); + } + } + + gtk_widget_destroy (file_chooser); +} + +static void +file_save_cb (GtkAction *action, + GSRWindow *window) +{ + if (!window->priv->has_file) { + file_save_as_cb (NULL, window); + } else { + do_save_file (window, window->priv->filename); + } +} + +static void +run_mixer_cb (GtkAction *action, + GSRWindow *window) +{ + char *mixer_path; + char *argv[4] = {NULL, "--page", "recording", NULL}; + GError *error = NULL; + gboolean ret; + + /* Open the mixer */ + mixer_path = g_find_program_in_path ("mate-volume-control"); + if (mixer_path == NULL) { + show_error_dialog (GTK_WINDOW (window), NULL, + _("%s is not installed in the path."), + "mate-volume-control"); + return; + } + + argv[0] = mixer_path; + ret = g_spawn_async (NULL, argv, NULL, 0, NULL, NULL, NULL, &error); + if (ret == FALSE) { + show_error_dialog (GTK_WINDOW (window), NULL, + _("There was an error starting %s: %s"), + mixer_path, error->message); + g_error_free (error); + } + + g_free (mixer_path); +} + +gboolean +gsr_window_is_saved (GSRWindow *window) +{ + return window->priv->saved; +} + +gboolean +gsr_discard_confirmation_dialog (GSRWindow *window, gboolean closing) +{ + GtkWidget *confirmation_dialog; + AtkObject *atk_obj; + gint response_id; + gboolean ret = TRUE; + + confirmation_dialog = gtk_message_dialog_new_with_markup (GTK_WINDOW (window), + GTK_DIALOG_MODAL, + GTK_MESSAGE_WARNING, + GTK_BUTTONS_NONE, + "<span weight=\"bold\" size=\"larger\">%s</span>", + closing ? + _("Save recording before closing?") : + _("Save recording?")); + + gtk_dialog_add_buttons (GTK_DIALOG (confirmation_dialog), + closing ? + _("Close _without Saving") : + _("Continue _without Saving"), + GTK_RESPONSE_YES, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_SAVE_AS, GTK_RESPONSE_NO, NULL); + gtk_dialog_set_default_response (GTK_DIALOG (confirmation_dialog), + GTK_RESPONSE_NO); + + gtk_window_set_title (GTK_WINDOW (confirmation_dialog), ""); + + atk_obj = gtk_widget_get_accessible (confirmation_dialog); + atk_object_set_name (atk_obj, _("Question")); + + response_id = gtk_dialog_run (GTK_DIALOG (confirmation_dialog)); + + switch (response_id) { + case GTK_RESPONSE_NO: + /* hiding the confirmation dialog allows the user to + see only one dialog at a time if the user click cancel + in the file dialog, they won't expect to return to the + confirmation dialog*/ + gtk_widget_hide (confirmation_dialog); + file_save_as_cb (NULL, window); + ret = window->priv->has_file; + break; + + case GTK_RESPONSE_YES: + ret = TRUE; + break; + + case GTK_RESPONSE_CANCEL: + default: + ret = FALSE; + break; + } + + gtk_widget_destroy (confirmation_dialog); + + return ret; +} + +static GtkWidget * +make_title_label (const char *text) +{ + GtkWidget *label; + char *fulltext; + + fulltext = g_strdup_printf ("<span weight=\"bold\">%s</span>", text); + label = gtk_label_new (fulltext); + g_free (fulltext); + + gtk_label_set_use_markup (GTK_LABEL (label), TRUE); + gtk_misc_set_alignment (GTK_MISC (label), 0, 0.0); + return label; +} + +static GtkWidget * +make_info_label (const char *text) +{ + GtkWidget *label; + + label = gtk_label_new (text); + gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5); + gtk_label_set_selectable (GTK_LABEL (label), TRUE); + gtk_label_set_line_wrap (GTK_LABEL (label), GTK_WRAP_WORD); + + return label; +} + +static void +pack_table_widget (GtkWidget *table, + GtkWidget *widget, + int left, + int top) +{ + gtk_table_attach (GTK_TABLE (table), widget, + left, left + 1, top, top + 1, + GTK_FILL, GTK_FILL, 0, 0); +} + +struct _file_props { + GtkWidget *dialog; + + GtkWidget *dirname; + GtkWidget *filename; + GtkWidget *size; + GtkWidget *length; + GtkWidget *samplerate; + GtkWidget *channels; + GtkWidget *bitrate; +}; + +static void +fill_in_information (GSRWindow *window, + struct _file_props *fp) +{ + struct stat buf; + guint64 file_size = 0; + gchar *text, *name; + gchar *utf8_name = NULL; + gint n_channels, bitrate, samplerate; + + /* dirname */ + if (window->priv->dirty) { + gtk_label_set_text (GTK_LABEL (fp->dirname), ""); + } else { + name = g_path_get_dirname (window->priv->filename); + text = g_filename_to_utf8 (name, -1, NULL, NULL, NULL); + gtk_label_set_text (GTK_LABEL (fp->dirname), text); + g_free (text); + g_free (name); + } + + /* filename */ + name = g_path_get_basename (window->priv->filename); + utf8_name = g_filename_to_utf8 (name, -1, NULL, NULL, NULL); + + if (window->priv->dirty) { + text = g_strdup_printf (_("%s (Has not been saved)"), utf8_name); + } else { + text = g_strdup (utf8_name); + } + gtk_label_set_text (GTK_LABEL (fp->filename), text); + g_free (text); + g_free (utf8_name); + g_free (name); + + /* Size */ + if (stat (window->priv->working_file, &buf) == 0) { + gchar *human; + + file_size = (guint64) buf.st_size; + human = g_format_size_for_display (file_size); + + text = g_strdup_printf (ngettext ("%s (%llu byte)", "%s (%llu bytes)", + file_size), human, file_size); + g_free (human); + } else { + text = g_strdup (_("Unknown size")); + } + gtk_label_set_text (GTK_LABEL (fp->size), text); + g_free (text); + + /* FIXME: Set up and run our own pipeline + * till we can get the info */ + /* Length */ + if (window->priv->len_secs == 0) { + text = g_strdup (_("Unknown")); + } else { + text = seconds_to_full_string (window->priv->len_secs); + } + gtk_label_set_text (GTK_LABEL (fp->length), text); + g_free (text); + + /* sample rate */ + samplerate = g_atomic_int_get (&window->priv->atomic.samplerate); + if (samplerate == 0) { + text = g_strdup (_("Unknown")); + } else { + text = g_strdup_printf (_("%.1f kHz"), (float) samplerate / 1000); + } + gtk_label_set_text (GTK_LABEL (fp->samplerate), text); + g_free (text); + + /* bit rate */ + bitrate = g_atomic_int_get (&window->priv->atomic.bitrate); + if (bitrate > 0) { + text = g_strdup_printf (_("%.0f kb/s"), (float) bitrate / 1000); + } else if (window->priv->len_secs > 0 && file_size > 0) { + bitrate = (file_size * 8.0) / window->priv->len_secs; + text = g_strdup_printf (_("%.0f kb/s (Estimated)"), + (float) bitrate / 1000); + } else { + text = g_strdup (_("Unknown")); + } + gtk_label_set_text (GTK_LABEL (fp->bitrate), text); + g_free (text); + + /* channels */ + n_channels = g_atomic_int_get (&window->priv->atomic.n_channels); + switch (n_channels) { + case 0: + text = g_strdup (_("Unknown")); + break; + case 1: + text = g_strdup (_("1 (mono)")); + break; + case 2: + text = g_strdup (_("2 (stereo)")); + break; + default: + text = g_strdup_printf ("%d", n_channels); + break; + } + gtk_label_set_text (GTK_LABEL (fp->channels), text); + g_free (text); +} + +static void +dialog_closed_cb (GtkDialog *dialog, + guint response_id, + struct _file_props *fp) +{ + gtk_widget_destroy (fp->dialog); + g_free (fp); +} + +static void +file_properties_cb (GtkAction *action, + GSRWindow *window) +{ + GtkWidget *dialog, *vbox, *inner_vbox, *hbox, *table, *label; + char *title, *shortname; + struct _file_props *fp; + shortname = g_path_get_basename (window->priv->filename); + title = g_strdup_printf (_("%s Information"), shortname); + g_free (shortname); + + dialog = gtk_dialog_new_with_buttons (title, GTK_WINDOW (window), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, NULL); + g_free (title); +#if !GTK_CHECK_VERSION (2, 21, 8) + gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE); +#endif + gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE); + gtk_container_set_border_width (GTK_CONTAINER (dialog), 5); + gtk_box_set_spacing (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), 2); + fp = g_new (struct _file_props, 1); + fp->dialog = dialog; + + g_signal_connect (G_OBJECT (dialog), "response", + G_CALLBACK (dialog_closed_cb), fp); + + vbox = gtk_vbox_new (FALSE, 18); + gtk_container_set_border_width (GTK_CONTAINER (vbox), 5); + gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), vbox, TRUE, TRUE, 0); + + inner_vbox = gtk_vbox_new (FALSE, 6); + gtk_box_pack_start (GTK_BOX (vbox), inner_vbox, FALSE, FALSE,0); + + label = make_title_label (_("File Information")); + gtk_box_pack_start (GTK_BOX (inner_vbox), label, FALSE, FALSE, 0); + + hbox = gtk_hbox_new (FALSE, 0); + gtk_box_pack_start (GTK_BOX (inner_vbox), hbox, TRUE, TRUE, 0); + + label = gtk_label_new (" "); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + + /* File properties */ + table = gtk_table_new (3, 2, FALSE); + gtk_table_set_col_spacings (GTK_TABLE (table), 12); + gtk_table_set_row_spacings (GTK_TABLE (table), 6); + gtk_box_pack_start (GTK_BOX (hbox), table, TRUE, TRUE, 0); + + label = make_info_label (_("Folder:")); + pack_table_widget (table, label, 0, 0); + + fp->dirname = make_info_label (""); + pack_table_widget (table, fp->dirname, 1, 0); + + label = make_info_label (_("Filename:")); + pack_table_widget (table, label, 0, 1); + + fp->filename = make_info_label (""); + pack_table_widget (table, fp->filename, 1, 1); + + label = make_info_label (_("File size:")); + pack_table_widget (table, label, 0, 2); + + fp->size = make_info_label (""); + pack_table_widget (table, fp->size, 1, 2); + + inner_vbox = gtk_vbox_new (FALSE, 6); + gtk_box_pack_start (GTK_BOX (vbox), inner_vbox, FALSE, FALSE, 0); + + label = make_title_label (_("Audio Information")); + gtk_box_pack_start (GTK_BOX (inner_vbox), label, FALSE, FALSE, 0); + + hbox = gtk_hbox_new (FALSE, 0); + gtk_box_pack_start (GTK_BOX (inner_vbox), hbox, TRUE, TRUE, 0); + + label = gtk_label_new (" "); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + + /* Audio info */ + table = gtk_table_new (4, 2, FALSE); + gtk_table_set_col_spacings (GTK_TABLE (table), 12); + gtk_table_set_row_spacings (GTK_TABLE (table), 6); + gtk_box_pack_start (GTK_BOX (hbox), table, TRUE, TRUE, 0); + + label = make_info_label (_("File duration:")); + pack_table_widget (table, label, 0, 0); + + fp->length = make_info_label (""); + pack_table_widget (table, fp->length, 1, 0); + + label = make_info_label (_("Number of channels:")); + pack_table_widget (table, label, 0, 1); + + fp->channels = make_info_label (""); + pack_table_widget (table, fp->channels, 1, 1); + + label = make_info_label (_("Sample rate:")); + pack_table_widget (table, label, 0, 2); + + fp->samplerate = make_info_label (""); + pack_table_widget (table, fp->samplerate, 1, 2); + + label = make_info_label (_("Bit rate:")); + pack_table_widget (table, label, 0, 3); + + fp->bitrate = make_info_label (""); + pack_table_widget (table, fp->bitrate, 1, 3); + + fill_in_information (window, fp); + gtk_widget_show_all (dialog); +} + +void +gsr_window_close (GSRWindow *window) +{ + gtk_widget_destroy (GTK_WIDGET (window)); +} + +static void +file_close_cb (GtkAction *action, + GSRWindow *window) +{ + if (gsr_window_is_saved (window) || gsr_discard_confirmation_dialog (window, TRUE)) + gsr_window_close (window); +} + +static void +quit_cb (GtkAction *action, + GSRWindow *window) +{ + gsr_quit (); +} + +static void +help_contents_cb (GtkAction *action, + GSRWindow *window) +{ + GError *error = NULL; + + gtk_show_uri (gtk_window_get_screen (GTK_WINDOW (window)), + "ghelp:mate-sound-recorder", + gtk_get_current_event_time (), &error); + + if (error != NULL) + { + g_warning ("%s", error->message); + + g_error_free (error); + } +} + +static void +about_cb (GtkAction *action, + GSRWindow *window) +{ + const char * const authors[] = {"Iain Holmes <[email protected]>", + "Ronald Bultje <[email protected]>", + "Johan Dahlin <[email protected]>", + "Tim-Philipp M\303\274ller <tim centricular net>", + NULL}; + const char * const documenters[] = {"Sun Microsystems", NULL}; + + gtk_show_about_dialog (GTK_WINDOW (window), + "name", _("Sound Recorder"), + "version", VERSION, + "copyright", "Copyright \xc2\xa9 2002 Iain Holmes", + "comments", _("A sound recorder for MATE\n [email protected]"), + "authors", authors, + "documenters", documenters, + "logo-icon-name", "mate-sound-recorder", + NULL); +} + +static void +play_cb (GtkAction *action, + GSRWindow *window) +{ + GSRWindowPrivate *priv = window->priv; + + if (priv->has_file == FALSE && !priv->working_file) + return; + + if (priv->play) { + shutdown_pipeline (priv->play); + } + + if ((priv->play = make_play_pipeline (window))) { + gchar *uri; + gchar *usefile; + GFile *file; + + if(priv->has_file == FALSE && priv->working_file) usefile = priv->working_file; + else usefile = priv->filename; + + file = g_file_new_for_commandline_arg (usefile); + uri = g_file_get_uri (file); + g_object_unref (file); + g_object_set (window->priv->play->pipeline, "uri", uri, NULL); + g_free (uri); + + if (priv->record && priv->record->state == GST_STATE_PLAYING) { + set_pipeline_state_to_null (priv->record->pipeline); + } + + gst_element_set_state (priv->play->pipeline, GST_STATE_PLAYING); + } +} + +static void +stop_cb (GtkAction *action, + GSRWindow *window) +{ + GSRWindowPrivate *priv = window->priv; + + /* Work out what's playing */ + if (priv->play && priv->play->state >= GST_STATE_PAUSED) { + GST_DEBUG ("Stopping play pipeline"); + set_pipeline_state_to_null (priv->play->pipeline); + } else if (priv->record && priv->record->state == GST_STATE_PLAYING) { + GST_DEBUG ("Stopping recording source"); + /* GstBaseSrc will automatically send an EOS when stopping */ + gst_element_set_state (priv->record->src, GST_STATE_NULL); + gst_element_get_state (priv->record->src, NULL, NULL, -1); + gst_element_set_locked_state (priv->record->src, TRUE); + + GST_DEBUG ("Stopping recording pipeline"); + set_pipeline_state_to_null (priv->record->pipeline); + gtk_widget_set_sensitive (window->priv->level, FALSE); + gtk_widget_set_sensitive (window->priv->volume_label, FALSE); + gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (window->priv->level), 0.0); + } +} + +static void +record_cb (GtkAction *action, + GSRWindow *window) +{ + if (!gsr_window_is_saved(window) && !gsr_discard_confirmation_dialog (window, FALSE)) + return; + + GSRWindowPrivate *priv = window->priv; + + if (priv->record) { + char *current_source; + shutdown_pipeline (priv->record); + if (!make_record_source (window)) + return; + current_source = gtk_combo_box_get_active_text (GTK_COMBO_BOX (window->priv->input)); + fill_record_input (window, current_source); + } + + if ((priv->record = make_record_pipeline (window))) { + window->priv->len_secs = 0; + window->priv->saved = FALSE; + + g_print ("%s", priv->record_filename); + g_object_set (G_OBJECT (priv->record->sink), + "location", priv->record_filename, + NULL); + + gst_element_set_state (priv->record->pipeline, GST_STATE_PLAYING); + gtk_widget_set_sensitive (window->priv->level, TRUE); + gtk_widget_set_sensitive (window->priv->volume_label, TRUE); + + } +} + +static gboolean +seek_started (GtkRange *range, + GdkEventButton *event, + GSRWindow *window) +{ + g_return_val_if_fail (window->priv != NULL, FALSE); + + window->priv->seek_in_progress = TRUE; + return FALSE; +} + +static gboolean +seek_to (GtkRange *range, + GdkEventButton *gdkevent, + GSRWindow *window) +{ + gdouble value; + gint64 time; + + if (window->priv->play->state < GST_STATE_PLAYING) + return FALSE; + + value = gtk_adjustment_get_value (gtk_range_get_adjustment (range)); + time = ((value / 100.0) * window->priv->len_secs) * GST_SECOND; + + gst_element_seek (window->priv->play->pipeline, 1.0, GST_FORMAT_TIME, + GST_SEEK_FLAG_FLUSH, GST_SEEK_TYPE_SET, time, + GST_SEEK_TYPE_NONE, 0); + + window->priv->seek_in_progress = FALSE; + + return FALSE; +} + +static gboolean +play_tick_callback (GSRWindow *window) +{ + GstElement *playbin; + GstFormat format = GST_FORMAT_TIME; + gint64 val = -1; + + g_return_val_if_fail (window->priv->play != NULL, FALSE); + g_return_val_if_fail (window->priv->play->pipeline != NULL, FALSE); + + playbin = window->priv->play->pipeline; + + /* This check stops us from doing an unnecessary query */ + if (window->priv->play->state != GST_STATE_PLAYING) { + GST_DEBUG ("pipeline in wrong state: %s", + gst_element_state_get_name (window->priv->play->state)); + window->priv->play->tick_id = 0; + return FALSE; + } + + if (gst_element_query_duration (playbin, &format, &val) && val != -1) { + gchar *len_str; + + window->priv->len_secs = val / GST_SECOND; + + len_str = seconds_to_full_string (window->priv->len_secs); + gtk_label_set_text (GTK_LABEL (window->priv->length_label), + len_str); + g_free (len_str); + } else { + if (window->priv->get_length_attempts <= 0) { + /* Attempts to get length ran out. */ + gtk_label_set_text (GTK_LABEL (window->priv->length_label), _("Unknown")); + } else { + --window->priv->get_length_attempts; + } + } + + if (window->priv->seek_in_progress) { + GST_DEBUG ("seek in progress, try again later"); + return TRUE; + } + + if (window->priv->len_secs == 0) { + GST_DEBUG ("no duration, try again later"); + return TRUE; + } + + if (gst_element_query_position (playbin, &format, &val) && val != -1) { + gdouble pos, len, percentage; + + pos = (gdouble) (val - (val % GST_SECOND)); + len = (gdouble) window->priv->len_secs * GST_SECOND; + percentage = pos / len * 100.0; + + gtk_adjustment_set_value (gtk_range_get_adjustment (GTK_RANGE (window->priv->scale)), + CLAMP (percentage + 0.5, 0.0, 100.0)); + } else { + GST_DEBUG ("failed to query position"); + } + + return TRUE; +} + +static gboolean +record_tick_callback (GSRWindow *window) +{ + GstElement *pipeline; + GstFormat format = GST_FORMAT_TIME; + gint64 val = -1; + gint secs; + + /* This check stops us from doing an unnecessary query */ + if (window->priv->record->state != GST_STATE_PLAYING) { + GST_DEBUG ("pipeline in wrong state: %s", + gst_element_state_get_name (window->priv->record->state)); + return FALSE; + } + + if (window->priv->seek_in_progress) + return TRUE; + + pipeline = window->priv->record->pipeline; + + if (gst_element_query_position (pipeline, &format, &val) && val != -1) { + gchar* len_str; + + secs = val / GST_SECOND; + + len_str = seconds_to_full_string (secs); + window->priv->len_secs = secs; + gtk_label_set_text (GTK_LABEL (window->priv->length_label), + len_str); + g_free (len_str); + } else { + GST_DEBUG ("failed to query position"); + } + + return TRUE; +} + +static void +play_state_changed_cb (GstBus * bus, GstMessage * msg, GSRWindow * window) +{ + GstState new_state; + + gst_message_parse_state_changed (msg, NULL, &new_state, NULL); + + g_return_if_fail (GSR_IS_WINDOW (window)); + + /* we are only interested in state changes of the top-level pipeline */ + if (msg->src != GST_OBJECT (window->priv->play->pipeline)) + return; + + window->priv->play->state = new_state; + + GST_DEBUG ("playbin state: %s", gst_element_state_get_name (new_state)); + + switch (new_state) { + case GST_STATE_PLAYING: + if (window->priv->play->tick_id == 0) { + window->priv->play->tick_id = + g_timeout_add (200, (GSourceFunc) play_tick_callback, window); + } + + set_action_sensitive (window, "Stop", TRUE); + set_action_sensitive (window, "Play", FALSE); + set_action_sensitive (window, "Record", FALSE); + set_action_sensitive (window, "FileSave", FALSE); + set_action_sensitive (window, "FileSaveAs", FALSE); + gtk_widget_set_sensitive (window->priv->scale, TRUE); + + gtk_statusbar_pop (GTK_STATUSBAR (window->priv->statusbar), + window->priv->status_message_cid); + gtk_statusbar_push (GTK_STATUSBAR (window->priv->statusbar), + window->priv->status_message_cid, + _("Playing…")); + + if (window->priv->ebusy_timeout_id) { + g_source_remove (window->priv->ebusy_timeout_id); + window->priv->ebusy_timeout_id = 0; + window->priv->ebusy_pipeline = NULL; + } + break; + + case GST_STATE_READY: + if (window->priv->play->tick_id > 0) { + g_source_remove (window->priv->play->tick_id); + window->priv->play->tick_id = 0; + } + gtk_adjustment_set_value (gtk_range_get_adjustment (GTK_RANGE (window->priv->scale)), 0.0); + gtk_widget_set_sensitive (window->priv->scale, FALSE); + /* fallthrough */ + case GST_STATE_PAUSED: + set_action_sensitive (window, "Stop", FALSE); + set_action_sensitive (window, "Play", TRUE); + set_action_sensitive (window, "Record", TRUE); + set_action_sensitive (window, "FileSave", TRUE); + set_action_sensitive (window, "FileSaveAs", TRUE); + + gtk_statusbar_pop (GTK_STATUSBAR (window->priv->statusbar), + window->priv->status_message_cid); + gtk_statusbar_push (GTK_STATUSBAR (window->priv->statusbar), + window->priv->status_message_cid, + _("Ready")); + break; + default: + break; + } +} + +static void +pipeline_deep_notify_caps_cb (GstObject *pipeline, + GstObject *object, + GParamSpec *pspec, + GSRWindow *window) +{ + GSRWindowPrivate *priv; + GstPadDirection direction; + + if (!GST_IS_PAD (object)) + return; + + priv = window->priv; + if (priv->play && pipeline == GST_OBJECT_CAST (priv->play->pipeline)) { + direction = GST_PAD_SRC; + } else if (priv->record && pipeline == GST_OBJECT_CAST (priv->record->pipeline)) { + direction = GST_PAD_SINK; + } else { + g_return_if_reached (); + } + + if (GST_PAD_DIRECTION (object) == direction) { + GstObject *pad_parent; + + pad_parent = gst_object_get_parent (object); + if (GST_IS_ELEMENT (pad_parent)) { + GstElementFactory *factory; + GstElement *element; + const gchar *klass; + + element = GST_ELEMENT_CAST (pad_parent); + if ((factory = gst_element_get_factory (element)) && + (klass = gst_element_factory_get_klass (factory)) && + strstr (klass, "Audio") && + (strstr (klass, "Decoder") || strstr (klass, "Encoder"))) { + GstCaps *caps; + + caps = gst_pad_get_negotiated_caps (GST_PAD_CAST (object)); + if (caps) { + GstStructure *s; + gint val; + + s = gst_caps_get_structure (caps, 0); + if (gst_structure_get_int (s, "channels", &val)) { + gst_atomic_int_set (&priv->atomic.n_channels, val); + } + if (gst_structure_get_int (s, "rate", &val)) { + gst_atomic_int_set (&priv->atomic.samplerate, val); + } + gst_caps_unref (caps); + } + } + } + if (pad_parent) + gst_object_unref (pad_parent); + } +} + +/* callback for when the recording profile has been changed */ +static void +profile_changed_cb (GObject *object, GSRWindow *window) +{ + GMAudioProfile *profile; + gchar *id; + + g_return_if_fail (GTK_IS_COMBO_BOX (object)); + + profile = gm_audio_profile_choose_get_active (GTK_WIDGET (object)); + + if (profile != NULL) { + id = g_strdup (gm_audio_profile_get_id (profile)); + GST_DEBUG ("profile changed to %s", GST_STR_NULL (id)); + mateconf_client_set_string (mateconf_client, KEY_LAST_PROFILE_ID, id, NULL); + g_free (id); + } +} + +static void +play_eos_msg_cb (GstBus * bus, GstMessage * msg, GSRWindow * window) +{ + g_return_if_fail (GSR_IS_WINDOW (window)); + + GST_DEBUG ("EOS"); + + stop_cb (NULL, window); +} + +static GSRWindowPipeline * +make_play_pipeline (GSRWindow *window) +{ + GSRWindowPipeline *obj; + GstElement *playbin; + GstElement *audiosink; + + audiosink = gst_element_factory_make ("mateconfaudiosink", "sink"); + if (audiosink == NULL) { + show_missing_known_element_error (NULL, + _("MateConf audio output"), "mateconfaudiosink", "mateconfelements", + "gst-plugins-good"); + return NULL; + } + + playbin = gst_element_factory_make ("playbin", "playbin"); + if (playbin == NULL) { + gst_object_unref (audiosink); + show_missing_known_element_error (NULL, + _("Playback"), "playbin", "playback", + "gst-plugins-base"); + return NULL; + } + + obj = g_new0 (GSRWindowPipeline, 1); + obj->pipeline = playbin; + obj->src = NULL; /* don't need that for playback */ + obj->sink = NULL; /* don't need that for playback */ + + g_object_set (playbin, "audio-sink", audiosink, NULL); + + /* we ultimately want to find out the caps on the decoder's source pad */ + g_signal_connect (playbin, "deep-notify::caps", + G_CALLBACK (pipeline_deep_notify_caps_cb), + window); + + obj->bus = gst_element_get_bus (playbin); + + gst_bus_add_signal_watch_full (obj->bus, G_PRIORITY_HIGH); + + g_signal_connect (obj->bus, "message::state-changed", + G_CALLBACK (play_state_changed_cb), + window); + + g_signal_connect (obj->bus, "message::error", + G_CALLBACK (pipeline_error_cb), + window); + + g_signal_connect (obj->bus, "message::eos", + G_CALLBACK (play_eos_msg_cb), + window); + + return obj; +} + +static void +record_eos_msg_cb (GstBus * bus, GstMessage * msg, GSRWindow * window) +{ + g_return_if_fail (GSR_IS_WINDOW (window)); + + GST_DEBUG ("EOS. Finished recording"); + + /* FIXME: this was READY before (why?) */ + set_pipeline_state_to_null (window->priv->record->pipeline); + + g_free (window->priv->working_file); + window->priv->working_file = g_strdup (window->priv->record_filename); + + g_free (window->priv->filename); + window->priv->filename = g_strdup (window->priv->record_filename); + + window->priv->has_file = TRUE; +} + +extern int gsr_sample_count; + +static gboolean +record_start (gpointer user_data) +{ + GSRWindow *window = GSR_WINDOW (user_data); + gchar *name; + + g_assert (window->priv->tick_id == 0); + + window->priv->get_length_attempts = 16; + window->priv->tick_id = g_timeout_add (200, (GSourceFunc) record_tick_callback, window); + + set_action_sensitive (window, "Stop", TRUE); + set_action_sensitive (window, "Play", FALSE); + set_action_sensitive (window, "Record", FALSE); + set_action_sensitive (window, "FileSave", FALSE); + set_action_sensitive (window, "FileSaveAs", FALSE); + gtk_widget_set_sensitive (window->priv->scale, FALSE); + + gtk_statusbar_pop (GTK_STATUSBAR (window->priv->statusbar), + window->priv->status_message_cid); + gtk_statusbar_push (GTK_STATUSBAR (window->priv->statusbar), + window->priv->status_message_cid, + _("Recording…")); + + window->priv->record_id = 0; + + /* Translator comment: untitled here implies that + * there is no active sound sample. Any newly + * recorded samples will be saved to disk with this + * name as default value. */ + if (gsr_sample_count == 1) { + name = g_strdup (_("Untitled")); + } else { + name = g_strdup_printf (_("Untitled-%d"), gsr_sample_count); + } + ++gsr_sample_count; + gtk_window_set_title (GTK_WINDOW(window), name); + + g_free (name); + + return FALSE; +} + +static void +record_state_changed_cb (GstBus *bus, GstMessage *msg, GSRWindow *window) +{ + GstState new_state; + GMAudioProfile *profile; + + gst_message_parse_state_changed (msg, NULL, &new_state, NULL); + + g_return_if_fail (GSR_IS_WINDOW (window)); + + /* we are only interested in state changes of the top-level pipeline */ + if (msg->src != GST_OBJECT (window->priv->record->pipeline)) + return; + + window->priv->record->state = new_state; + + GST_DEBUG ("record pipeline state: %s", gst_element_state_get_name (new_state)); + + switch (new_state) { + case GST_STATE_PLAYING: + window->priv->record_id = g_idle_add (record_start, window); + g_free (window->priv->extension); + profile = gm_audio_profile_choose_get_active (window->priv->profile); + window->priv->extension = g_strdup (profile ? gm_audio_profile_get_extension (profile) : NULL); + gtk_widget_set_sensitive (window->priv->profile, FALSE); + gtk_widget_set_sensitive (window->priv->input, FALSE); + break; + case GST_STATE_READY: + gtk_adjustment_set_value (gtk_range_get_adjustment (GTK_RANGE (window->priv->scale)), 0.0); + gtk_widget_set_sensitive (window->priv->scale, FALSE); + gtk_widget_set_sensitive (window->priv->profile, TRUE); + gtk_widget_set_sensitive (window->priv->input, GST_IS_MIXER (window->priv->mixer)); + /* fall through */ + case GST_STATE_PAUSED: + set_action_sensitive (window, "Stop", FALSE); + set_action_sensitive (window, "Play", TRUE); + set_action_sensitive (window, "Record", TRUE); + set_action_sensitive (window, "FileSave", TRUE); + set_action_sensitive (window, "FileSaveAs", TRUE); + gtk_widget_set_sensitive (window->priv->scale, FALSE); + gtk_widget_set_sensitive (window->priv->profile, TRUE); + gtk_widget_set_sensitive (window->priv->input, TRUE); + + gtk_statusbar_pop (GTK_STATUSBAR (window->priv->statusbar), + window->priv->status_message_cid); + gtk_statusbar_push (GTK_STATUSBAR (window->priv->statusbar), + window->priv->status_message_cid, + _("Ready")); + if (window->priv->tick_id > 0) { + g_source_remove (window->priv->tick_id); + window->priv->tick_id = 0; + } + break; + default: + break; + } +} + +/* create the mateconf-based source for recording. + * store the source and the mixer in it in our window-private data + */ +static gboolean +make_record_source (GSRWindow *window) +{ + GstElement *source, *e; + + source = gst_element_factory_make ("mateconfaudiosrc", "mateconfaudiosource"); + if (source == NULL) { + show_missing_known_element_error (NULL, + _("MateConf audio recording"), "mateconfaudiosrc", + "mateconfelements", "gst-plugins-good"); + return FALSE; + } + + /* instantiate the underlying element so we can query it */ + /* FIXME: maybe we want to trap errors in this case ? */ + if (!gst_element_set_state (source, GST_STATE_READY)) { + show_error_dialog (NULL, NULL, + _("Your audio capture settings are invalid. " + "Please correct them with the \"Sound Preferences\" " + "under the System Preferences menu.")); + return FALSE; + } + window->priv->source = source; + e = gst_bin_get_by_interface (GST_BIN (source), GST_TYPE_MIXER); + window->priv->mixer = GST_MIXER (e); + + return TRUE; +} + +static void +record_input_changed_cb (GtkComboBox *input, GSRWindow *window) +{ + const gchar *text; + const GList *l; + GstMixerTrack *t = NULL, *new = NULL; + static GstMixerTrack *selected = NULL; + + text = gtk_combo_box_get_active_text (input); + GST_DEBUG ("record input changed to '%s'", GST_STR_NULL (text)); + + if (text == NULL) + return; + + /* The pipeline has been destroyed already, we'll try and remember + * the input for the next record run in fill_record_input() */ + if (GST_IS_MIXER (window->priv->mixer) == FALSE) + return; + + for (l = gst_mixer_list_tracks (window->priv->mixer); + l != NULL; l = l->next) { + t = l->data; + if (t == NULL || t->label == NULL) + continue; + if ((g_str_equal (t->label, text)) && + (t->flags & GST_MIXER_TRACK_INPUT)) { + if (new == NULL) + new = g_object_ref (t); + /* FIXME selected == t is equivalent to NULL == t in this case, + * selected, after its initialization to NULL, was never written to + * before this read access to it + * and NULL == t is equivalent to FALSE, because of the check + * "if (t == NULL || t->label == NULL)" above + */ + } else if (selected == t) + /* re-mute old one */ + gst_mixer_set_record (window->priv->mixer, + selected, FALSE); + } + + /* FIXME selected _is_ NULL always at this point - same as 5 lines above*/ + if (selected != NULL) + g_object_unref (selected); + if (!(selected = new)) + return; + + gst_mixer_set_record (window->priv->mixer, selected, TRUE); + GST_DEBUG ("input changed to: %s\n", selected->label); + mateconf_client_set_string (mateconf_client, KEY_LAST_INPUT, selected->label, NULL); +} + +static void +fill_record_input (GSRWindow *window, gchar *selected) +{ + const GList *l; + int i = 0; + int last_possible_i = 0; + GtkTreeModel *model; + + model = gtk_combo_box_get_model (GTK_COMBO_BOX (window->priv->input)); + + if (model) + gtk_list_store_clear (GTK_LIST_STORE (model)); + + if (GST_IS_MIXER (window->priv->mixer) == FALSE + || gst_mixer_list_tracks (window->priv->mixer) == NULL) { + gtk_widget_hide (window->priv->input); + gtk_widget_hide (window->priv->input_label); + return; + } + + gtk_widget_set_sensitive (window->priv->input, GST_IS_MIXER (window->priv->mixer)); + if (!GST_IS_MIXER (window->priv->mixer)) + return; + + for (l = gst_mixer_list_tracks (window->priv->mixer); l != NULL; l = l->next) { + GstMixerTrack *t = l->data; + if (t->label == NULL) + continue; + if (t->flags & GST_MIXER_TRACK_INPUT) { + gtk_combo_box_append_text (GTK_COMBO_BOX (window->priv->input), t->label); + ++i; + } + if (t->flags & GST_MIXER_TRACK_RECORD) { + if (selected == NULL) { + gtk_combo_box_set_active (GTK_COMBO_BOX (window->priv->input), i - 1); + } else { + last_possible_i = i; + } + } + if ((selected != NULL) && g_str_equal (selected, t->label)) { + gtk_combo_box_set_active (GTK_COMBO_BOX (window->priv->input), i - 1); + } + } + + if (gtk_combo_box_get_active (GTK_COMBO_BOX (window->priv->input)) == -1) { + gtk_combo_box_set_active (GTK_COMBO_BOX (window->priv->input), last_possible_i - 1); + } + + gtk_widget_show (window->priv->input); + gtk_widget_show (window->priv->input_label); +} + +static gboolean +level_message_handler_cb (GstBus * bus, GstMessage * message, GSRWindow *window) +{ + GSRWindowPrivate *priv = window->priv; + + if (message->type == GST_MESSAGE_ELEMENT) { + const GstStructure *s = gst_message_get_structure (message); + const gchar *name = gst_structure_get_name (s); + + if (g_str_equal (name, "level")) { + gint channels; + gdouble peak_dB; + gdouble myind; + const GValue *list; + const GValue *value; + + gint i; + /* we can get the number of channels as the length of any of the value + * lists */ + + list = gst_structure_get_value (s, "rms"); + channels = gst_value_list_get_size (list); + + for (i = 0; i < channels; ++i) { + list = gst_structure_get_value (s, "peak"); + value = gst_value_list_get_value (list, i); + peak_dB = g_value_get_double (value); + myind = exp (peak_dB / 20); + if (myind > 1.0) + myind = 1.0; + gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (priv->level), myind); + } + } + } + /* we handled the message we want, and ignored the ones we didn't want. + * so the core can unref the message for us */ + return TRUE; +} + +static GSRWindowPipeline * +make_record_pipeline (GSRWindow *window) +{ + GSRWindowPipeline *pipeline; + GMAudioProfile *profile; + const gchar *profile_pipeline_desc; + GstElement *encoder, *source, *filesink, *level; + GError *err = NULL; + gchar *pipeline_desc; + const char *name; + + source = window->priv->source; + + /* Any reason we are not using matevfssink here? (tpm) */ + filesink = gst_element_factory_make ("filesink", "sink"); + if (filesink == NULL) + { + show_missing_known_element_error (NULL, + _("file output"), "filesink", "coreelements", + "gstreamer"); + gst_object_unref (source); + return NULL; + } + + pipeline = g_new (GSRWindowPipeline, 1); + + pipeline->pipeline = gst_pipeline_new ("record-pipeline"); + pipeline->src = source; + pipeline->sink = filesink; + + gst_bin_add (GST_BIN (pipeline->pipeline), source); + + level = gst_element_factory_make ("level", "level"); + if (level == NULL) + { + show_missing_known_element_error (NULL, + _("level"), "level", "level", + "gstreamer"); + gst_object_unref (source); + return NULL; + } + gst_element_set_name (level, "level"); + + profile = gm_audio_profile_choose_get_active (window->priv->profile); + if (profile == NULL) + return NULL; + profile_pipeline_desc = gm_audio_profile_get_pipeline (profile); + name = gm_audio_profile_get_name (profile); + + GST_DEBUG ("encoder profile pipeline: '%s'", + GST_STR_NULL (profile_pipeline_desc)); + + pipeline_desc = g_strdup_printf ("audioconvert ! %s", profile_pipeline_desc); + GST_DEBUG ("making encoder bin from description '%s'", pipeline_desc); + encoder = gst_parse_bin_from_description (pipeline_desc, TRUE, &err); + g_free (pipeline_desc); + pipeline_desc = NULL; + + if (err) { + show_profile_error (NULL, err->message, + _("Could not parse the '%s' audio profile. "), name); + g_printerr ("Failed to create GStreamer encoder plugins [%s]: %s\n", + profile_pipeline_desc, err->message); + g_error_free (err); + gst_object_unref (pipeline->pipeline); + gst_object_unref (filesink); + g_free (pipeline); + return NULL; + } + + gst_bin_add (GST_BIN (pipeline->pipeline), level); + gst_bin_add (GST_BIN (pipeline->pipeline), encoder); + gst_bin_add (GST_BIN (pipeline->pipeline), filesink); + + /* now link it all together */ + if (!(gst_element_link_many (source, level, encoder, NULL))) { + show_profile_error (NULL, NULL, + _("Could not capture using the '%s' audio profile. "), + name); + gst_object_unref (pipeline->pipeline); + g_free (pipeline); + return NULL; + } + + if (!gst_element_link (encoder, filesink)) { + show_profile_error (NULL, NULL, + _("Could not write to a file using the '%s' audio profile. "), + name); + gst_object_unref (pipeline->pipeline); + g_free (pipeline); + return NULL; + } + + /* we ultimately want to find out the caps on the encoder's source pad */ + g_signal_connect (pipeline->pipeline, "deep-notify::caps", + G_CALLBACK (pipeline_deep_notify_caps_cb), + window); + + pipeline->bus = gst_element_get_bus (pipeline->pipeline); + + gst_bus_add_signal_watch (pipeline->bus); + + g_signal_connect (pipeline->bus, "message::element", + G_CALLBACK (level_message_handler_cb), + window); + + g_signal_connect (pipeline->bus, "message::state-changed", + G_CALLBACK (record_state_changed_cb), + window); + + g_signal_connect (pipeline->bus, "message::error", + G_CALLBACK (pipeline_error_cb), + window); + + g_signal_connect (pipeline->bus, "message::eos", + G_CALLBACK (record_eos_msg_cb), + window); + + return pipeline; +} + +static char * +calculate_format_value (GtkScale *scale, + double value, + GSRWindow *window) +{ + gint seconds; + + if (window->priv->record && window->priv->record->state == GST_STATE_PLAYING) { + seconds = value; + return seconds_to_string (seconds); + } else { + seconds = window->priv->len_secs * (value / 100); + return seconds_to_string (seconds); + } +} + +static const GtkActionEntry menu_entries[] = +{ + /* File menu. */ + { "File", NULL, N_("_File") }, + { "FileNew", GTK_STOCK_NEW, NULL, NULL, + N_("Create a new sample"), G_CALLBACK (file_new_cb) }, + { "FileOpen", GTK_STOCK_OPEN, NULL, NULL, + N_("Open a file"), G_CALLBACK (file_open_cb) }, + { "FileSave", GTK_STOCK_SAVE, NULL, NULL, + N_("Save the current file"), G_CALLBACK (file_save_cb) }, + { "FileSaveAs", GTK_STOCK_SAVE_AS, NULL, "<shift><control>S", + N_("Save the current file with a different name"), G_CALLBACK (file_save_as_cb) }, + { "RunMixer", GTK_STOCK_EXECUTE, N_("Open Volu_me Control"), NULL, + N_("Open the audio mixer"), G_CALLBACK (run_mixer_cb) }, + { "FileProperties", GTK_STOCK_PROPERTIES, NULL, "<control>I", + N_("Show information about the current file"), G_CALLBACK (file_properties_cb) }, + { "FileClose", GTK_STOCK_CLOSE, NULL, NULL, + N_("Close the current file"), G_CALLBACK (file_close_cb) }, + { "Quit", GTK_STOCK_QUIT, NULL, NULL, + N_("Quit the program"), G_CALLBACK (quit_cb) }, + + /* Control menu */ + { "Control", NULL, N_("_Control") }, + { "Record", GTK_STOCK_MEDIA_RECORD, NULL, "<control>R", + N_("Record sound"), G_CALLBACK (record_cb) }, + { "Play", GTK_STOCK_MEDIA_PLAY, NULL, "<control>P", + N_("Play sound"), G_CALLBACK (play_cb) }, + { "Stop", GTK_STOCK_MEDIA_STOP, NULL, "<control>X", + N_("Stop sound"), G_CALLBACK (stop_cb) }, + + /* Help menu */ + { "Help", NULL, N_("_Help") }, + {"HelpContents", GTK_STOCK_HELP, N_("Contents"), "F1", + N_("Open the manual"), G_CALLBACK (help_contents_cb) }, + { "About", GTK_STOCK_ABOUT, NULL, NULL, + N_("About this application"), G_CALLBACK (about_cb) } +}; + +static void +menu_item_select_cb (GtkMenuItem *proxy, + GSRWindow *window) +{ + GtkAction *action; + char *message; + + action = g_object_get_data (G_OBJECT (proxy), "gtk-action"); + g_return_if_fail (action != NULL); + + g_object_get (G_OBJECT (action), "tooltip", &message, NULL); + if (message) { + gtk_statusbar_push (GTK_STATUSBAR (window->priv->statusbar), + window->priv->tip_message_cid, message); + g_free (message); + } +} + +static void +menu_item_deselect_cb (GtkMenuItem *proxy, + GSRWindow *window) +{ + gtk_statusbar_pop (GTK_STATUSBAR (window->priv->statusbar), + window->priv->tip_message_cid); +} + +static void +connect_proxy_cb (GtkUIManager *manager, + GtkAction *action, + GtkWidget *proxy, + GSRWindow *window) +{ + if (GTK_IS_MENU_ITEM (proxy)) { + g_signal_connect (proxy, "select", + G_CALLBACK (menu_item_select_cb), window); + g_signal_connect (proxy, "deselect", + G_CALLBACK (menu_item_deselect_cb), window); + } +} + +static void +disconnect_proxy_cb (GtkUIManager *manager, + GtkAction *action, + GtkWidget *proxy, + GSRWindow *window) +{ + if (GTK_IS_MENU_ITEM (proxy)) { + g_signal_handlers_disconnect_by_func + (proxy, G_CALLBACK (menu_item_select_cb), window); + g_signal_handlers_disconnect_by_func + (proxy, G_CALLBACK (menu_item_deselect_cb), window); + } +} + +/* find the given filename in the uninstalled or installed ui dir */ +static gchar * +find_ui_file (const gchar * filename) +{ + gchar * path; + + path = g_build_filename (GSR_UIDIR_UNINSTALLED, filename, NULL); + if (g_file_test (path, G_FILE_TEST_EXISTS)) + return path; + + g_free (path); + path = g_build_filename (GSR_UIDIR, filename, NULL); + if (g_file_test (path, G_FILE_TEST_EXISTS)) + return path; + + g_free (path); + return NULL; +} + +static void +gsr_window_init (GSRWindow *window) +{ + GSRWindowPrivate *priv; + GError *error = NULL; + GtkWidget *main_vbox; + GtkWidget *menubar; + GtkWidget *file_menu; + GtkWidget *submenu; + GtkWidget *rec_menu; + GtkWidget *toolbar; + GtkWidget *content_vbox; + GtkWidget *hbox; + GtkWidget *label; + GtkWidget *table; + GtkWidget *align; + GtkWidget *frame; + gchar *id; + gchar *last_input; + gchar *path; + GtkAction *action; + GtkShadowType shadow_type; + window->priv = GSR_WINDOW_GET_PRIVATE (window); + priv = window->priv; + + /* treat mateconf client as a singleton */ + if (mateconf_client == NULL) + mateconf_client = mateconf_client_get_default (); + + main_vbox = gtk_vbox_new (FALSE, 0); + gtk_container_add (GTK_CONTAINER (window), main_vbox); + priv->main_vbox = main_vbox; + gtk_widget_show (main_vbox); + + /* menu & toolbar */ + priv->ui_manager = gtk_ui_manager_new (); + + gtk_window_add_accel_group (GTK_WINDOW (window), + gtk_ui_manager_get_accel_group (priv->ui_manager)); + + path = find_ui_file ("ui.xml"); + gtk_ui_manager_add_ui_from_file (priv->ui_manager, path, &error); + + if (error != NULL) + { + show_error_dialog (GTK_WINDOW (window), error->message, + _("Could not load UI file. The program may not be properly installed.")); + g_error_free (error); + exit (1); + } + g_free (path); + + /* show tooltips in the statusbar */ + g_signal_connect (priv->ui_manager, "connect_proxy", + G_CALLBACK (connect_proxy_cb), window); + g_signal_connect (priv->ui_manager, "disconnect_proxy", + G_CALLBACK (disconnect_proxy_cb), window); + + priv->action_group = gtk_action_group_new ("GSRWindowActions"); + gtk_action_group_set_translation_domain (priv->action_group, NULL); + gtk_action_group_add_actions (priv->action_group, + menu_entries, + G_N_ELEMENTS (menu_entries), + window); + + gtk_ui_manager_insert_action_group (priv->ui_manager, priv->action_group, 0); + + /* set short labels to use in the toolbar */ + action = gtk_action_group_get_action (priv->action_group, "FileOpen"); + g_object_set (action, "short_label", _("Open"), NULL); + action = gtk_action_group_get_action (priv->action_group, "FileSave"); + g_object_set (action, "short_label", _("Save"), NULL); + action = gtk_action_group_get_action (priv->action_group, "FileSaveAs"); + g_object_set (action, "short_label", _("Save As"), NULL); + + set_action_sensitive (window, "FileSave", FALSE); + set_action_sensitive (window, "FileSaveAs", FALSE); + set_action_sensitive (window, "Play", FALSE); + set_action_sensitive (window, "Stop", FALSE); + + menubar = gtk_ui_manager_get_widget (priv->ui_manager, "/MenuBar"); + gtk_box_pack_start (GTK_BOX (main_vbox), menubar, FALSE, FALSE, 0); + gtk_widget_show (menubar); + + toolbar = gtk_ui_manager_get_widget (priv->ui_manager, "/ToolBar"); + gtk_toolbar_set_show_arrow (GTK_TOOLBAR (toolbar), FALSE); + gtk_box_pack_start (GTK_BOX (main_vbox), toolbar, FALSE, FALSE, 0); + gtk_widget_show (toolbar); + + /* recent files */ + file_menu = gtk_ui_manager_get_widget (priv->ui_manager, + "/MenuBar/FileMenu"); + submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (file_menu)); + rec_menu = gtk_ui_manager_get_widget (priv->ui_manager, + "/MenuBar/FileMenu/FileRecentMenu"); + priv->recent_view = gtk_recent_chooser_menu_new (); + gtk_recent_chooser_set_local_only (GTK_RECENT_CHOOSER (priv->recent_view), TRUE); + gtk_recent_chooser_set_limit (GTK_RECENT_CHOOSER (priv->recent_view), 5); + priv->recent_filter = gtk_recent_filter_new (); + gtk_recent_filter_add_application (priv->recent_filter, g_get_application_name ()); + gtk_recent_chooser_set_filter (GTK_RECENT_CHOOSER (priv->recent_view), priv->recent_filter); + g_signal_connect (priv->recent_view, "item-activated", + G_CALLBACK (file_open_recent_cb), window); + + /* window content: hscale, labels, etc */ + content_vbox = gtk_vbox_new (FALSE, 7); + gtk_container_set_border_width (GTK_CONTAINER (content_vbox), 6); + gtk_box_pack_start (GTK_BOX (main_vbox), content_vbox, TRUE, TRUE, 0); + gtk_widget_show (content_vbox); + + priv->scale = gtk_hscale_new (GTK_ADJUSTMENT (gtk_adjustment_new (0, 0, 100, 1, 1, 0))); + priv->seek_in_progress = FALSE; + g_signal_connect (priv->scale, "format-value", + G_CALLBACK (calculate_format_value), window); + g_signal_connect (priv->scale, "button-press-event", + G_CALLBACK (seek_started), window); + g_signal_connect (priv->scale, "button-release-event", + G_CALLBACK (seek_to), window); + + gtk_scale_set_value_pos (GTK_SCALE (window->priv->scale), GTK_POS_BOTTOM); + /* We can't seek until we find out the length */ + gtk_widget_set_sensitive (window->priv->scale, FALSE); + gtk_box_pack_start (GTK_BOX (content_vbox), priv->scale, FALSE, FALSE, 6); + gtk_widget_show (window->priv->scale); + + /* create source and choose mixer input */ + hbox = gtk_hbox_new (FALSE, 12); + gtk_box_pack_start (GTK_BOX (content_vbox), hbox, FALSE, FALSE, 0); + + priv->input_label = gtk_label_new_with_mnemonic (_("Record from _input:")); + gtk_misc_set_alignment (GTK_MISC (priv->input_label), 0, 0.5); + gtk_box_pack_start (GTK_BOX (hbox), priv->input_label, FALSE, FALSE, 0); + + priv->input = gtk_combo_box_new_text (); + gtk_label_set_mnemonic_widget (GTK_LABEL (priv->input_label), priv->input); + gtk_box_pack_start (GTK_BOX (hbox), priv->input, TRUE, TRUE, 0); + + if (!make_record_source (window)) + exit (1); + + g_signal_connect (priv->input, "changed", + G_CALLBACK (record_input_changed_cb), window); + + /* choose profile */ + hbox = gtk_hbox_new (FALSE, 12); + gtk_box_pack_start (GTK_BOX (content_vbox), hbox, FALSE, FALSE, 0); + + label = gtk_label_new_with_mnemonic (_("_Record as:")); + gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + + priv->profile = gm_audio_profile_choose_new (); + gtk_label_set_mnemonic_widget (GTK_LABEL (label), priv->profile); + gtk_box_pack_start (GTK_BOX (hbox), window->priv->profile, TRUE, TRUE, 0); + gtk_widget_show (window->priv->profile); + + atk_object_add_relationship (gtk_widget_get_accessible (GTK_WIDGET (priv->profile)), + ATK_RELATION_LABELLED_BY, + gtk_widget_get_accessible (GTK_WIDGET (label))); + + id = mateconf_client_get_string (mateconf_client, KEY_LAST_PROFILE_ID, NULL); + if (id) { + gm_audio_profile_choose_set_active (window->priv->profile, id); + g_free (id); + } + + g_signal_connect (priv->profile, "changed", + G_CALLBACK (profile_changed_cb), window); + + hbox = gtk_hbox_new (FALSE, 0); + gtk_box_pack_start (GTK_BOX (content_vbox), hbox, FALSE, FALSE, 0); + + label = gtk_label_new (" "); /* FIXME: better padding? */ + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + + table = gtk_table_new (3, 2, FALSE); + gtk_table_set_col_spacings (GTK_TABLE (table), 12); + gtk_table_set_row_spacings (GTK_TABLE (table), 6); + gtk_box_pack_start (GTK_BOX (hbox), table, TRUE, TRUE, 0); + + label = make_title_label (_("File Information")); + + gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5); + gtk_table_attach (GTK_TABLE (table), label, + 0, 2, 0, 1, + GTK_FILL, 0, 0, 0); + + label = gtk_label_new (_("Filename:")); + + gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5); + gtk_table_attach (GTK_TABLE (table), label, + 0, 1, 1, 2, + GTK_FILL, 0, 0, 0); + + priv->name_label = gtk_label_new (_("<none>")); + gtk_label_set_selectable (GTK_LABEL (priv->name_label), TRUE); + gtk_label_set_line_wrap (GTK_LABEL (priv->name_label), GTK_WRAP_WORD); + gtk_misc_set_alignment (GTK_MISC (priv->name_label), 0, 0.5); + gtk_table_attach (GTK_TABLE (table), priv->name_label, + 1, 2, 1, 2, + GTK_FILL | GTK_EXPAND, 0, + 0, 0); + + atk_object_add_relationship (gtk_widget_get_accessible (GTK_WIDGET (priv->name_label)), + ATK_RELATION_LABELLED_BY, + gtk_widget_get_accessible (GTK_WIDGET (label))); + + + label = gtk_label_new (_("Length:")); + + gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5); + gtk_table_attach (GTK_TABLE (table), label, + 0, 1, 2, 3, + GTK_FILL, 0, 0, 0); + + priv->length_label = gtk_label_new (""); + gtk_label_set_selectable (GTK_LABEL (priv->length_label), TRUE); + gtk_misc_set_alignment (GTK_MISC (priv->length_label), 0, 0.5); + gtk_table_attach (GTK_TABLE (table), priv->length_label, + 1, 2, 2, 3, + GTK_FILL | GTK_EXPAND, 0, + 0, 0); + + atk_object_add_relationship (gtk_widget_get_accessible (GTK_WIDGET (priv->length_label)), + ATK_RELATION_LABELLED_BY, + gtk_widget_get_accessible (GTK_WIDGET (label))); + + /* statusbar */ + priv->statusbar = gtk_statusbar_new (); + gtk_widget_set_can_focus (priv->statusbar, TRUE); + gtk_box_pack_end (GTK_BOX (main_vbox), priv->statusbar, FALSE, FALSE, 0); + gtk_widget_show (priv->statusbar); + + /* hack to get the same shadow as the status bar.. */ + gtk_widget_style_get (GTK_WIDGET (priv->statusbar), "shadow-type", &shadow_type, NULL); + + frame = gtk_frame_new (NULL); + gtk_frame_set_shadow_type (GTK_FRAME (frame), shadow_type); + gtk_widget_show (frame); + + gtk_box_pack_end (GTK_BOX (priv->statusbar), frame, FALSE, TRUE, 0); + + hbox = gtk_hbox_new (FALSE, 0); + gtk_container_add (GTK_CONTAINER (frame), hbox); + gtk_box_set_spacing (GTK_BOX (hbox), 6); + + priv->volume_label = gtk_label_new (_("Level:")); + gtk_box_pack_start (GTK_BOX (hbox), priv->volume_label, FALSE, TRUE, 0); + + /* initialize priv->level */ + align = gtk_aspect_frame_new ("", 0.0, 0.0, 20, FALSE); + gtk_frame_set_shadow_type (GTK_FRAME (align), GTK_SHADOW_NONE); + gtk_widget_show (align); + gtk_box_pack_start (GTK_BOX (hbox), align, FALSE, FALSE, 0); + + priv->level = gtk_progress_bar_new (); + gtk_container_add (GTK_CONTAINER (align), priv->level); + + gtk_widget_set_sensitive (window->priv->volume_label, FALSE); + gtk_widget_set_sensitive (window->priv->level, FALSE); + + priv->status_message_cid = gtk_statusbar_get_context_id + (GTK_STATUSBAR (priv->statusbar), "status_message"); + priv->tip_message_cid = gtk_statusbar_get_context_id + (GTK_STATUSBAR (priv->statusbar), "tip_message"); + + gtk_statusbar_push (GTK_STATUSBAR (priv->statusbar), + priv->status_message_cid, + _("Ready")); + + gtk_widget_show_all (main_vbox); + last_input = mateconf_client_get_string (mateconf_client, KEY_LAST_INPUT, NULL); + fill_record_input (window, last_input); + if (last_input) { + g_free (last_input); + } + + /* Make the pipelines */ + priv->play = NULL; + priv->record = NULL; + + priv->len_secs = 0; + priv->get_length_attempts = 16; + priv->dirty = TRUE; +} + +static void +gsr_window_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GSRWindow *window; + GSRWindowPrivate *priv; + struct stat buf; + char *title, *short_name; + char *utf8_name = NULL; + const char *ext; + + window = GSR_WINDOW (object); + priv = window->priv; + + switch (prop_id) { + case PROP_LOCATION: + if (priv->filename != NULL) { + if (g_value_get_string (value) == NULL) + return; + if (g_str_equal (g_value_get_string (value), priv->filename)) { + return; + } + } + + g_free (priv->filename); + g_free (priv->working_file); + + priv->filename = g_value_dup_string (value); + priv->working_file = g_strdup (priv->filename); + priv->len_secs = 0; + + short_name = g_path_get_basename (priv->filename); + if (stat (priv->filename, &buf) == 0) { + window->priv->has_file = TRUE; + } else { + window->priv->has_file = FALSE; + } + + g_free (window->priv->extension); + if ((ext = strrchr (short_name, '.')) && ext[1] != '\0') + window->priv->extension = g_strdup (&ext[1]); + else + window->priv->extension = NULL; + + utf8_name = g_filename_to_utf8 (short_name, -1, NULL, NULL, NULL); + if (priv->name_label != NULL) { + gtk_label_set_text (GTK_LABEL (priv->name_label), + utf8_name); + } + + gsr_add_recent (priv->filename); + + /*Translators: this is the window title, %s is the currently open file's name or Untitled*/ + title = g_strdup_printf (_("%s — Sound Recorder"), utf8_name); + gtk_window_set_title (GTK_WINDOW (window), title); + g_free (title); + g_free (utf8_name); + g_free (short_name); + + set_action_sensitive (window, "Play", window->priv->has_file ? TRUE : FALSE); + set_action_sensitive (window, "Stop", FALSE); + set_action_sensitive (window, "Record", TRUE); + set_action_sensitive (window, "FileSave", window->priv->has_file ? TRUE : FALSE); + set_action_sensitive (window, "FileSaveAs", TRUE); + break; + default: + break; + } +} + +static void +gsr_window_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + switch (prop_id) { + case PROP_LOCATION: + g_value_set_string (value, GSR_WINDOW (object)->priv->filename); + break; + + default: + break; + } +} + +static void +gsr_window_finalize (GObject *object) +{ + GSRWindow *window; + GSRWindowPrivate *priv; + + window = GSR_WINDOW (object); + priv = window->priv; + + GST_DEBUG ("finalizing ..."); + + if (priv == NULL) { + return; + } + + if (priv->ui_manager) { + g_object_unref (priv->ui_manager); + priv->ui_manager = NULL; + } + + if (priv->action_group) { + g_object_unref (priv->action_group); + priv->action_group = NULL; + } + + if (priv->tick_id > 0) { + g_source_remove (priv->tick_id); + window->priv->play->tick_id = 0; + } + + if (priv->record_id > 0) { + g_source_remove (priv->record_id); + } + + if (priv->ebusy_timeout_id > 0) { + g_source_remove (window->priv->ebusy_timeout_id); + } + + g_idle_remove_by_data (window); + + if (priv->play != NULL) { + shutdown_pipeline (priv->play); + g_free (priv->play); + } + + if (priv->record != NULL) { + shutdown_pipeline (priv->record); + g_free (priv->record); + } + + unlink (priv->record_filename); + g_free (priv->record_filename); + + g_free (priv->working_file); + g_free (priv->filename); + + G_OBJECT_CLASS (parent_class)->finalize (object); + + window->priv = NULL; +} + +static void +gsr_window_class_init (GSRWindowClass *klass) +{ + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = gsr_window_finalize; + object_class->set_property = gsr_window_set_property; + object_class->get_property = gsr_window_get_property; + + parent_class = g_type_class_peek_parent (klass); + + g_object_class_install_property (object_class, + PROP_LOCATION, + g_param_spec_string ("location", + "Location", + "", + /* Translator comment: default trackname is 'untitled', which + * has as effect that the user cannot save to this file. The + * 'save' action will open the save-as dialog instead to give + * a proper filename. See mate-record.c:94. */ + _("Untitled"), + G_PARAM_READWRITE)); + + g_type_class_add_private (object_class, sizeof (GSRWindowPrivate)); + + GST_DEBUG_CATEGORY_INIT (gsr_debug, "gsr", 0, "Mate Sound Recorder"); +} + +GType +gsr_window_get_type (void) +{ + static GType type = 0; + + if (G_UNLIKELY (type == 0)) { + GTypeInfo info = { + sizeof (GSRWindowClass), + NULL, NULL, + (GClassInitFunc) gsr_window_class_init, + NULL, NULL, + sizeof (GSRWindow), 0, + (GInstanceInitFunc) gsr_window_init + }; + + type = g_type_register_static (GTK_TYPE_WINDOW, + "GSRWindow", + &info, 0); + } + + return type; +} + +GtkWidget * +gsr_window_new (const char *filename) +{ + GSRWindow *window; + char *template; + + /* filename has been changed to be without extension */ + window = g_object_new (GSR_TYPE_WINDOW, + "location", filename, + NULL); + /* FIXME: check extension too */ + window->priv->filename = g_strdup (filename); + if (g_file_test (filename, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR) != FALSE) { + window->priv->has_file = TRUE; + window->priv->dirty = FALSE; + } else { + window->priv->has_file = FALSE; + } + + template = g_strdup_printf ("gsr-record-%s-%d.XXXXXX", filename, getpid ()); + window->priv->record_fd = g_file_open_tmp (template, &window->priv->record_filename, NULL); + g_free (template); + close (window->priv->record_fd); + + if (window->priv->has_file == FALSE) { + g_free (window->priv->working_file); + window->priv->working_file = g_strdup (window->priv->record_filename); + } else { + g_free (window->priv->working_file); + window->priv->working_file = g_strdup (filename); + } + + window->priv->saved = TRUE; + + gtk_window_set_default_size (GTK_WINDOW (window), 512, 200); + + return GTK_WIDGET (window); +} diff --git a/grecord/src/gsr-window.h b/grecord/src/gsr-window.h new file mode 100644 index 0000000..6b5a67e --- /dev/null +++ b/grecord/src/gsr-window.h @@ -0,0 +1,61 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Authors: Iain Holmes <[email protected]> + * + * Copyright 2002 Iain Holmes + * + * 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. + * + * 4th Februrary 2005: Christian Schaller: changed license to LGPL with + * permission of Iain Holmes, Ronald Bultje, Leontine Binchy (SUN), Johan Dahlin + * and Joe Marcus Clarke + * + */ + +#ifndef __GSR_WINDOW_H__ +#define __GSR_WINDOW_H__ + +#include <gtk/gtk.h> + +#define GSR_TYPE_WINDOW (gsr_window_get_type ()) +#define GSR_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GSR_TYPE_WINDOW, GSRWindow)) +#define GSR_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GSR_TYPE_WINDOW, GSRWindowClass)) +#define GSR_IS_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSR_TYPE_WINDOW)) +#define GSR_IS_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GSR_TYPE_WINDOW)) + +typedef struct _GSRWindow GSRWindow; +typedef struct _GSRWindowClass GSRWindowClass; +typedef struct _GSRWindowPrivate GSRWindowPrivate; + +struct _GSRWindow { + GtkWindow parent; + + GSRWindowPrivate *priv; +}; + +struct _GSRWindowClass { + GtkWindowClass parent_class; +}; + + +GType gsr_window_get_type (void); + +GtkWidget* gsr_window_new (const char *filename); +void gsr_window_close (GSRWindow *window); +gboolean gsr_window_is_saved (GSRWindow *window); +gboolean gsr_discard_confirmation_dialog (GSRWindow *window, gboolean closing); + +#endif diff --git a/grecord/src/mate-recorder.c b/grecord/src/mate-recorder.c new file mode 100644 index 0000000..482d014 --- /dev/null +++ b/grecord/src/mate-recorder.c @@ -0,0 +1,232 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Authors: Iain Holmes <[email protected]> + * + * Copyright 2002, 2003, 2004, 2005 Iain Holmes + * 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. + * + * 4th Februrary 2005: Christian Schaller: changed license to LGPL with + * permission of Iain Holmes, Ronald Bultje, Leontine Binchy (SUN), Johan Dahlin * and Joe Marcus Clarke + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <string.h> + +#include <glib/gi18n.h> +#include <mateconf/mateconf-client.h> +#include <gst/gst.h> + +#include "gsr-window.h" + +void gsr_quit (void); +void gsr_add_recent (gchar *filename); +GtkWidget * gsr_open_window (const char *filename); + +extern void mate_media_profiles_init (MateConfClient *conf); + +static GList *windows = NULL; + +/* Also used in gsr-window.c as extern MateConfClient *mateconf_client */ +MateConfClient *mateconf_client = NULL; + +static gboolean +delete_event_cb (GSRWindow *window, + gpointer data) +{ + if (!gsr_window_is_saved (window) && !gsr_discard_confirmation_dialog (window, TRUE)) + return TRUE; + + return FALSE; +} + +static void +window_destroyed (GtkWidget *window, + gpointer data) +{ + windows = g_list_remove (windows, window); + + if (windows == NULL) { + gtk_main_quit (); + } +} + +void +gsr_quit (void) +{ + GList *p; + + for (p = windows; p;) { + GSRWindow *window = p->data; + + /* p is set here instead of in the for statement, + because by the time we get back to the loop, + p will be invalid */ + p = p->next; + + if (gsr_window_is_saved (window) || gsr_discard_confirmation_dialog (window, TRUE)) + gsr_window_close (window); + } +} + +void +gsr_add_recent (gchar *filename) +{ + GtkRecentData data; + char *groups[] = { NULL, NULL }; + char *uri; + + memset (&data, 0, sizeof (data)); + + uri = g_filename_to_uri (filename, NULL, NULL); + if (uri == NULL) + return; + + data.mime_type = g_content_type_guess (uri, NULL, 0, NULL); + if (data.mime_type == NULL) { + /* No mime-type means warnings, and it breaks when adding + * non-GIO supported URI schemes */ + g_free (uri); + return; + } + + /* It's a local file */ + data.display_name = g_filename_display_basename (data.display_name); + groups[0] = "Totem"; + + data.app_name = g_strdup (g_get_application_name ()); + data.app_exec = g_strjoin (" ", g_get_prgname (), "%u", NULL); + data.groups = groups; + gtk_recent_manager_add_full (gtk_recent_manager_get_default (), + uri, &data); + + g_free (data.display_name); + g_free (data.mime_type); + g_free (data.app_name); + g_free (data.app_exec); + +} + +/* Also referenced from gsr-window.c */ +gint gsr_sample_count = 1; + +GtkWidget * +gsr_open_window (const char *filename) +{ + GtkWidget *window; + char *utf8_name; + char *name; + + if (filename == NULL) { + /* Translator comment: untitled here implies that + * there is no active sound sample. Any newly + * recorded samples will be saved to disk with this + * name as default value. */ + if (gsr_sample_count == 1) { + utf8_name = g_strdup (_("Untitled")); + } else { + utf8_name = g_strdup_printf (_("Untitled-%d"), gsr_sample_count); + } + name = g_filename_from_utf8 (utf8_name, -1, NULL, NULL, NULL); + g_free (utf8_name); + ++gsr_sample_count; + } else { + name = g_strdup (filename); + } + + window = GTK_WIDGET (gsr_window_new (name)); + g_free (name); + + g_signal_connect (G_OBJECT (window), "delete-event", + G_CALLBACK (delete_event_cb), NULL); + + g_signal_connect (G_OBJECT (window), "destroy", + G_CALLBACK (window_destroyed), NULL); + + windows = g_list_prepend (windows, window); + gtk_widget_show (window); + + return window; +} + +int +main (int argc, + char **argv) +{ + gchar **filenames = NULL; + /* this is necessary because someone apparently forgot to add a + * convenient way to get the remaining arguments to the MateProgram + * API when adding the GOption stuff to it ... */ + const GOptionEntry entries[] = { + { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &filenames, + "Special option that collects any remaining arguments for us" }, + { NULL, } + }; + + GOptionContext *ctx; + GError *error = NULL; + + g_thread_init (NULL); + + /* Init gettext */ + bindtextdomain (GETTEXT_PACKAGE, MATELOCALEDIR); + bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); + textdomain (GETTEXT_PACKAGE); + + ctx = g_option_context_new ("mate-sound-recorder"); + /* Initializes gtk during option parsing */ + g_option_context_add_group (ctx, gtk_get_option_group (TRUE)); + g_option_context_add_group (ctx, gst_init_get_option_group ()); + g_option_context_add_main_entries (ctx, entries, GETTEXT_PACKAGE); + + if (!g_option_context_parse (ctx, &argc, &argv, &error)) { + g_printerr ("Option parsing failed: %s\n", error->message); + g_error_free (error); + g_option_context_free (ctx); + return EXIT_FAILURE; + } + + g_option_context_free (ctx); + gtk_window_set_default_icon_name ("mate-sound-recorder"); + + /* use it like a singleton */ + mateconf_client = mateconf_client_get_default (); + + /* init mate-media-profiles */ + mate_media_profiles_init (mateconf_client); + + if (filenames != NULL && filenames[0] != NULL) { + guint i, num; + + num = g_strv_length (filenames); + for (i = 0; i < num; ++i) { + gsr_open_window (filenames[i]); + } + } else { + gsr_open_window (NULL); + } + + if (filenames) { + g_strfreev (filenames); + } + + gtk_main (); + + return 0; +} diff --git a/grecord/src/ui.xml b/grecord/src/ui.xml new file mode 100644 index 0000000..a4e9108 --- /dev/null +++ b/grecord/src/ui.xml @@ -0,0 +1,41 @@ +<ui> + + <menubar name="MenuBar"> + <menu name="FileMenu" action="File"> + <menuitem action="FileNew"/> + <menuitem action="FileOpen"/> + <separator/> + <menuitem action="FileSave"/> + <menuitem action="FileSaveAs"/> + <separator/> + <menuitem action="RunMixer"/> + <menuitem action="FileProperties"/> + <separator/> + <placeholder name="FileRecentMenu"/> + <menuitem action="FileClose"/> + <menuitem action="Quit"/> + </menu> + + <menu name="ControlMenu" action="Control"> + <menuitem action="Record"/> + <menuitem action="Play"/> + <menuitem action="Stop"/> + </menu> + + <menu name="HelpMenu" action="Help"> + <menuitem action="HelpContents"/> + <menuitem action="About"/> + </menu> + </menubar> + + <toolbar name="ToolBar"> + <toolitem action="FileNew"/> + <toolitem action="FileOpen"/> + <toolitem action="FileSave"/> + <separator/> + <toolitem action="Record"/> + <toolitem action="Play"/> + <toolitem action="Stop"/> + </toolbar> + +</ui> |