diff options
Diffstat (limited to 'gst-mixer/src')
-rw-r--r-- | gst-mixer/src/Makefile.am | 42 | ||||
-rw-r--r-- | gst-mixer/src/Makefile.in | 648 | ||||
-rw-r--r-- | gst-mixer/src/button.c | 133 | ||||
-rw-r--r-- | gst-mixer/src/button.h | 71 | ||||
-rw-r--r-- | gst-mixer/src/element.c | 595 | ||||
-rw-r--r-- | gst-mixer/src/element.h | 68 | ||||
-rw-r--r-- | gst-mixer/src/keys.h | 39 | ||||
-rw-r--r-- | gst-mixer/src/main.c | 177 | ||||
-rw-r--r-- | gst-mixer/src/misc.c | 72 | ||||
-rw-r--r-- | gst-mixer/src/misc.h | 32 | ||||
-rw-r--r-- | gst-mixer/src/preferences.c | 441 | ||||
-rw-r--r-- | gst-mixer/src/preferences.h | 77 | ||||
-rw-r--r-- | gst-mixer/src/track.c | 647 | ||||
-rw-r--r-- | gst-mixer/src/track.h | 113 | ||||
-rw-r--r-- | gst-mixer/src/volume.c | 552 | ||||
-rw-r--r-- | gst-mixer/src/volume.h | 82 | ||||
-rw-r--r-- | gst-mixer/src/window.c | 435 | ||||
-rw-r--r-- | gst-mixer/src/window.h | 75 |
18 files changed, 4299 insertions, 0 deletions
diff --git a/gst-mixer/src/Makefile.am b/gst-mixer/src/Makefile.am new file mode 100644 index 0000000..d85a6b9 --- /dev/null +++ b/gst-mixer/src/Makefile.am @@ -0,0 +1,42 @@ + +if HAVE_SOUND_THEME +GVC_ST_LIBS = $(SOUND_THEME_LIBS) \ + $(top_builddir)/sound-theme/libsoundtheme.la +endif + +AM_CPPFLAGS = \ + $(GSTMIXER_CFLAGS) \ + $(DISABLE_DEPRECATED) \ + -I$(top_srcdir)/sound-theme \ + -DMATELOCALEDIR=\""$(datadir)/locale"\" \ + -DDATA_DIR=\""$(pkgdatadir)"\" \ + -DPIX_DIR=\""$(pkgdatadir)/pixmaps"\" + +bin_PROGRAMS = mate-volume-control + +mate_volume_control_SOURCES = \ + button.c \ + element.c \ + main.c \ + preferences.c \ + track.c \ + volume.c \ + window.c \ + misc.c + +noinst_HEADERS = \ + button.h \ + element.h \ + keys.h \ + preferences.h \ + track.h \ + volume.h \ + window.h \ + misc.h + +mate_volume_control_LDFLAGS = \ + $(GVC_ST_LIBS) \ + $(GSTMIXER_LIBS) + + +-include $(top_srcdir)/git.mk diff --git a/gst-mixer/src/Makefile.in b/gst-mixer/src/Makefile.in new file mode 100644 index 0000000..e5334c7 --- /dev/null +++ b/gst-mixer/src/Makefile.in @@ -0,0 +1,648 @@ +# 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-volume-control$(EXEEXT) +subdir = gst-mixer/src +DIST_COMMON = $(noinst_HEADERS) $(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)" +PROGRAMS = $(bin_PROGRAMS) +am_mate_volume_control_OBJECTS = button.$(OBJEXT) element.$(OBJEXT) \ + main.$(OBJEXT) preferences.$(OBJEXT) track.$(OBJEXT) \ + volume.$(OBJEXT) window.$(OBJEXT) misc.$(OBJEXT) +mate_volume_control_OBJECTS = $(am_mate_volume_control_OBJECTS) +mate_volume_control_LDADD = $(LDADD) +AM_V_lt = $(am__v_lt_$(V)) +am__v_lt_ = $(am__v_lt_$(AM_DEFAULT_VERBOSITY)) +am__v_lt_0 = --silent +mate_volume_control_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(mate_volume_control_LDFLAGS) \ + $(LDFLAGS) -o $@ +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_volume_control_SOURCES) +DIST_SOURCES = $(mate_volume_control_SOURCES) +HEADERS = $(noinst_HEADERS) +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@ +@HAVE_SOUND_THEME_TRUE@GVC_ST_LIBS = $(SOUND_THEME_LIBS) \ +@HAVE_SOUND_THEME_TRUE@ $(top_builddir)/sound-theme/libsoundtheme.la + +AM_CPPFLAGS = \ + $(GSTMIXER_CFLAGS) \ + $(DISABLE_DEPRECATED) \ + -I$(top_srcdir)/sound-theme \ + -DMATELOCALEDIR=\""$(datadir)/locale"\" \ + -DDATA_DIR=\""$(pkgdatadir)"\" \ + -DPIX_DIR=\""$(pkgdatadir)/pixmaps"\" + +mate_volume_control_SOURCES = \ + button.c \ + element.c \ + main.c \ + preferences.c \ + track.c \ + volume.c \ + window.c \ + misc.c + +noinst_HEADERS = \ + button.h \ + element.h \ + keys.h \ + preferences.h \ + track.h \ + volume.h \ + window.h \ + misc.h + +mate_volume_control_LDFLAGS = \ + $(GVC_ST_LIBS) \ + $(GSTMIXER_LIBS) + +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 gst-mixer/src/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign gst-mixer/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-volume-control$(EXEEXT): $(mate_volume_control_OBJECTS) $(mate_volume_control_DEPENDENCIES) + @rm -f mate-volume-control$(EXEEXT) + $(AM_V_CCLD)$(mate_volume_control_LINK) $(mate_volume_control_OBJECTS) $(mate_volume_control_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/button.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/element.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/misc.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/preferences.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/track.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/volume.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/window.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 + +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) $(HEADERS) +installdirs: + for dir in "$(DESTDIR)$(bindir)"; 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-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 + +.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 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 + + +-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/gst-mixer/src/button.c b/gst-mixer/src/button.c new file mode 100644 index 0000000..5fde995 --- /dev/null +++ b/gst-mixer/src/button.c @@ -0,0 +1,133 @@ +/* MATE Volume Control + * Copyright (C) 2003-2004 Ronald Bultje <[email protected]> + * + * button.c: flat toggle button with icons + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <string.h> +#include <glib.h> +#include <gtk/gtk.h> + +#include "button.h" + +G_DEFINE_TYPE (MateVolumeControlButton, mate_volume_control_button, GTK_TYPE_BUTTON) + + +static void mate_volume_control_button_class_init (MateVolumeControlButtonClass *klass); +static void mate_volume_control_button_init (MateVolumeControlButton *button); +static void mate_volume_control_button_dispose (GObject *object); + +static void mate_volume_control_button_clicked (GtkButton *button); + +static void +mate_volume_control_button_class_init (MateVolumeControlButtonClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GtkButtonClass *gtkbutton_class = GTK_BUTTON_CLASS (klass); + + gobject_class->dispose = mate_volume_control_button_dispose; + gtkbutton_class->clicked = mate_volume_control_button_clicked; +} + +static void +mate_volume_control_button_init (MateVolumeControlButton *button) +{ + button->active_icon = NULL; + button->inactive_icon = NULL; + + button->active = FALSE; +} + +static void +mate_volume_control_button_dispose (GObject *object) +{ + G_OBJECT_CLASS (mate_volume_control_button_parent_class)->dispose (object); +} + +GtkWidget * +mate_volume_control_button_new (gchar *active_icon, + gchar *inactive_icon, + gchar *msg) +{ + MateVolumeControlButton *button; + GtkWidget *image; + + button = g_object_new (MATE_VOLUME_CONTROL_TYPE_BUTTON, NULL); + gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE); + button->active_icon = active_icon; + button->inactive_icon = inactive_icon; + + image = gtk_image_new (); + gtk_container_add (GTK_CONTAINER (button), image); + gtk_widget_show (image); + button->image = GTK_IMAGE (image); + gtk_button_clicked (GTK_BUTTON (button)); + + gtk_widget_set_tooltip_text (GTK_WIDGET (button), g_strdup (msg)); + + return GTK_WIDGET (button); +} + +gboolean +mate_volume_control_button_get_active (MateVolumeControlButton *button) +{ + return button->active; +} + +void +mate_volume_control_button_set_active (MateVolumeControlButton *button, + gboolean active) +{ + if (button->active != active) + gtk_button_clicked (GTK_BUTTON (button)); +} + +static void +mate_volume_control_button_clicked (GtkButton *_button) +{ + MateVolumeControlButton *button = MATE_VOLUME_CONTROL_BUTTON (_button); + + button->active = !button->active; + + if (strstr (button->active_icon, ".png")) { + gchar *filename; + GdkPixbuf *pixbuf; + + if (button->active) + filename = g_build_filename (PIX_DIR, button->active_icon, NULL); + else + filename = g_build_filename (PIX_DIR, button->inactive_icon, NULL); + + pixbuf = gdk_pixbuf_new_from_file (filename, NULL); + gtk_image_set_from_pixbuf (button->image, pixbuf); + g_object_unref (pixbuf); + g_free (filename); + } else { + if (button->active) { + gtk_image_set_from_icon_name (button->image, button->active_icon, + GTK_ICON_SIZE_MENU); + } else { + gtk_image_set_from_icon_name (button->image, button->inactive_icon, + GTK_ICON_SIZE_MENU); + } + } +} diff --git a/gst-mixer/src/button.h b/gst-mixer/src/button.h new file mode 100644 index 0000000..6f8d0a7 --- /dev/null +++ b/gst-mixer/src/button.h @@ -0,0 +1,71 @@ +/* MATE Button Control + * Copyright (C) 2003-2004 Ronald Bultje <[email protected]> + * + * button.h: flat toggle button with images + * + * 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. + */ + +#ifndef __GVC_BUTTON_H__ +#define __GVC_BUTTON_H__ + +#include <glib.h> +#include <gtk/gtk.h> + +G_BEGIN_DECLS + +#define MATE_VOLUME_CONTROL_TYPE_BUTTON \ + (mate_volume_control_button_get_type ()) +#define MATE_VOLUME_CONTROL_BUTTON(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), MATE_VOLUME_CONTROL_TYPE_BUTTON, \ + MateVolumeControlButton)) +#define MATE_VOLUME_CONTROL_BUTTON_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), MATE_VOLUME_CONTROL_TYPE_BUTTON, \ + MateVolumeControlButtonClass)) +#define MATE_VOLUME_CONTROL_IS_BUTTON(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MATE_VOLUME_CONTROL_TYPE_BUTTON)) +#define MATE_VOLUME_CONTROL_IS_BUTTON_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), MATE_VOLUME_CONTROL_TYPE_BUTTON)) + +typedef struct _MateVolumeControlButton { + GtkButton parent; + + /* stock icons */ + gchar *active_icon, + *inactive_icon; + + /* state */ + gboolean active; + + /* image */ + GtkImage *image; +} MateVolumeControlButton; + +typedef struct _MateVolumeControlButtonClass { + GtkButtonClass klass; +} MateVolumeControlButtonClass; + +GType mate_volume_control_button_get_type (void); +GtkWidget * mate_volume_control_button_new (gchar *active_icon, + gchar *inactive_icon, + gchar *msg); +gboolean mate_volume_control_button_get_active (MateVolumeControlButton *button); +void mate_volume_control_button_set_active (MateVolumeControlButton *button, + gboolean active); + +G_END_DECLS + +#endif /* __GVC_BUTTON_H__ */ diff --git a/gst-mixer/src/element.c b/gst-mixer/src/element.c new file mode 100644 index 0000000..a190582 --- /dev/null +++ b/gst-mixer/src/element.c @@ -0,0 +1,595 @@ +/* MATE Volume Control + * Copyright (C) 2003-2004 Ronald Bultje <[email protected]> + * + * element.c: widget representation of a single mixer element + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <string.h> +#include <glib/gi18n.h> +#include <gtk/gtk.h> + +#include "element.h" +#include "keys.h" +#include "preferences.h" +#include "track.h" +#include "misc.h" +#ifdef HAVE_SOUND_THEME +#include "gvc-sound-theme-chooser.h" +#endif + +G_DEFINE_TYPE (MateVolumeControlElement, mate_volume_control_element, GTK_TYPE_NOTEBOOK) + +static void mate_volume_control_element_class_init (MateVolumeControlElementClass *klass); +static void mate_volume_control_element_init (MateVolumeControlElement *el); +static void mate_volume_control_element_dispose (GObject *object); + +static void cb_mateconf (MateConfClient *client, + guint connection_id, + MateConfEntry *entry, + gpointer data); + + +static void +mate_volume_control_element_class_init (MateVolumeControlElementClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->dispose = mate_volume_control_element_dispose; +} + +static void +mate_volume_control_element_init (MateVolumeControlElement *el) +{ + el->client = NULL; + el->mixer = NULL; +} + +GtkWidget * +mate_volume_control_element_new (MateConfClient *client) +{ + MateVolumeControlElement *el; + + /* element */ + el = g_object_new (MATE_VOLUME_CONTROL_TYPE_ELEMENT, NULL); + el->client = g_object_ref (G_OBJECT (client)); + + mateconf_client_add_dir (el->client, MATE_VOLUME_CONTROL_KEY_DIR, + MATECONF_CLIENT_PRELOAD_RECURSIVE, NULL); + mateconf_client_notify_add (el->client, MATE_VOLUME_CONTROL_KEY_DIR, + cb_mateconf, el, NULL, NULL); + + return GTK_WIDGET (el); +} + +static void +mate_volume_control_element_dispose (GObject *object) +{ + MateVolumeControlElement *el = MATE_VOLUME_CONTROL_ELEMENT (object); + + if (el->client) { + g_object_unref (G_OBJECT (el->client)); + el->client = NULL; + } + + if (el->mixer) { + /* remove g_timeout_add() mainloop handlers */ + mate_volume_control_element_change (el, NULL); + gst_element_set_state (GST_ELEMENT (el->mixer), GST_STATE_NULL); + gst_object_unref (GST_OBJECT (el->mixer)); + el->mixer = NULL; + } + + G_OBJECT_CLASS (mate_volume_control_element_parent_class)->dispose (object); +} + +/* + * Checks if we want to show the track by default ("whitelist"). + */ + +gboolean +mate_volume_control_element_whitelist (GstMixer *mixer, + GstMixerTrack *track) +{ + gint i, pos; + gboolean found = FALSE; + + /* Yes this is a hack. */ + static struct { + gchar *label; + gboolean done; + } list[] = { + +/* Translator comment: the names below are a whitelist for which volume + * controls to show by default. Make sure that those match the translations + * of GStreamer-plugins' ALSA/OSS plugins. */ + { "cd", FALSE }, + { "line", FALSE }, + { "mic", FALSE }, + { "pcm", FALSE }, + { "headphone", FALSE }, + { "speaker", FALSE }, + { "volume", FALSE }, + { "master", FALSE }, + { "digital output", FALSE }, + { "recording", FALSE }, + { "front", FALSE }, + { NULL, FALSE } + }; + + /* + * When the user changes devices, it is necessary to reset the whitelist + * to a good default state. This fixes bugs LP:345645, 576022 + */ + if (track == NULL) + { + for (i = 0; list[i].label != NULL; i++) + list[i].done = FALSE; + return TRUE; + } + + /* honor the mixer supplied hints about whitelisting if available */ + if (gst_mixer_get_mixer_flags (GST_MIXER (mixer)) & GST_MIXER_FLAG_HAS_WHITELIST) { + if (GST_MIXER_TRACK_HAS_FLAG (track, GST_MIXER_TRACK_WHITELIST)) { + return (TRUE); + } else { + return (FALSE); + } + } + + for (i = 0; !found && list[i].label != NULL; i++) { + gchar *label_l = NULL; + + if (list[i].done) + continue; + + /* make case insensitive */ + if (g_object_class_find_property (G_OBJECT_GET_CLASS (track), "untranslated-label")) + g_object_get (track, "untranslated-label", &label_l, NULL); + if (label_l == NULL) + g_object_get (track, "label", &label_l, NULL); + for (pos = 0; label_l[pos] != '\0'; pos++) + label_l[pos] = g_ascii_tolower (label_l[pos]); + + if (g_strrstr (label_l, list[i].label) != NULL) { + found = TRUE; + list[i].done = TRUE; + } + g_free (label_l); + } + + return found; +} + +/* + * Hide/show notebook page. + */ + +static void +update_tab_visibility (MateVolumeControlElement *el, gint page, gint tabnum) +{ + const GList *item; + gboolean visible = FALSE; + GtkWidget *t; + + for (item = gst_mixer_list_tracks (el->mixer); + item != NULL; item = item->next) { + GstMixerTrack *track = item->data; + MateVolumeControlTrack *trkw = + g_object_get_data (G_OBJECT (track), "mate-volume-control-trkw"); + + if (get_page_num (el->mixer, track) == page && trkw->visible) { + visible = TRUE; + break; + } + } + + t = gtk_notebook_get_nth_page (GTK_NOTEBOOK (el), tabnum); + if (visible) + gtk_widget_show (t); + else + gtk_widget_hide (t); +} + +static void +cb_notify_message (GstBus *bus, GstMessage *message, gpointer data) +{ + MateVolumeControlElement *el = data; + GstMixerMessageType type; + MateVolumeControlTrack *trkw; + GstMixerTrack *track = NULL; + GstMixerOptions *options = NULL; + + if (GST_MESSAGE_SRC (message) != GST_OBJECT (el->mixer)) { + /* not from our mixer - can't update anything anyway */ + return; + } + + /* This code only calls refresh if the first_track changes, because the + * refresh code only retrieves the current value from that track anyway */ + type = gst_mixer_message_get_type (message); + if (type == GST_MIXER_MESSAGE_MUTE_TOGGLED) { + gst_mixer_message_parse_mute_toggled (message, &track, NULL); + } else if (type == GST_MIXER_MESSAGE_VOLUME_CHANGED) { + gst_mixer_message_parse_volume_changed (message, &track, NULL, NULL); + } else if (type == GST_MIXER_MESSAGE_OPTION_CHANGED) { + gst_mixer_message_parse_option_changed (message, &options, NULL); + track = GST_MIXER_TRACK (options); + } else { + return; + } + + trkw = g_object_get_data (G_OBJECT (track), + "mate-volume-control-trkw"); + + mate_volume_control_track_update (trkw); +} + +/* + * Change the element. Basically recreates this object internally. + */ + +void +mate_volume_control_element_change (MateVolumeControlElement *el, + GstElement *element) +{ + struct { + GtkWidget *page, *old_sep, *new_sep, *flagbuttonbox; + gboolean use; + gint pos, height, width; + MateVolumeControlTrack * (* get_track_widget) (GtkTable *table, + gint tab_pos, + GstMixer *mixer, + GstMixerTrack *track, + GtkWidget *left_sep, + GtkWidget *right_sep, + GtkWidget *flagbox); + + } content[4] = { + { NULL, NULL, NULL, NULL, FALSE, 0, 5, 1, + mate_volume_control_track_add_playback }, + { NULL, NULL, NULL, NULL, FALSE, 0, 5, 1, + mate_volume_control_track_add_recording }, + { NULL, NULL, NULL, NULL, FALSE, 0, 1, 3, + mate_volume_control_track_add_playback }, + { NULL, NULL, NULL, NULL, FALSE, 0, 1, 3, + mate_volume_control_track_add_option } + }; + static gboolean theme_page = FALSE; + const GList *item; + GstMixer *mixer; + GstBus *bus; + gint i; + + /* remove old pages, but not the "Sound Theme" page */ + i = 0; + if (theme_page) + i = 1; + + while (gtk_notebook_get_n_pages (GTK_NOTEBOOK (el)) > i) { + gtk_notebook_remove_page (GTK_NOTEBOOK (el), 0); + } + + /* take/put reference */ + if (el->mixer) { + for (item = gst_mixer_list_tracks (el->mixer); + item != NULL; item = item->next) { + GstMixerTrack *track = item->data; + MateVolumeControlTrack *trkw; + + trkw = g_object_get_data (G_OBJECT (track), + "mate-volume-control-trkw"); + g_object_set_data (G_OBJECT (track), "mate-volume-control-trkw", NULL); + mate_volume_control_track_free (trkw); + } + } + if (!element) + return; + + g_return_if_fail (GST_IS_MIXER (element)); + mixer = GST_MIXER (element); + gst_object_replace ((GstObject **) &el->mixer, GST_OBJECT (element)); + + /* Bus for notifications */ + if (GST_ELEMENT_BUS (mixer) == NULL) { + bus = gst_bus_new (); + gst_bus_add_signal_watch (bus); + g_signal_connect (G_OBJECT (bus), "message::element", + (GCallback) cb_notify_message, el); + gst_element_set_bus (GST_ELEMENT (mixer), bus); + } + + /* content pages */ + for (i = 0; i < 4; i++) { + content[i].page = gtk_table_new (content[i].width, content[i].height, FALSE); + gtk_container_set_border_width (GTK_CONTAINER (content[i].page), 6); + if (i >= 2) + gtk_table_set_row_spacings (GTK_TABLE (content[i].page), 6); + gtk_table_set_col_spacings (GTK_TABLE (content[i].page), 6); + content[i].flagbuttonbox = NULL; + } + + /* show */ + mate_volume_control_element_whitelist (el->mixer, NULL); + for (item = gst_mixer_list_tracks (el->mixer); + item != NULL; item = item->next) { + GstMixerTrack *track = item->data; + MateVolumeControlTrack *trkw; + gchar *key; + const MateConfValue *value; + gboolean active; + + i = get_page_num (el->mixer, track); + + /* FIXME: + * - do not create separator if there is no more track + * _of this type_. We currently destroy it at the + * end, so it's not critical, but not nice either. + */ + if (i == 3) { + content[i].new_sep = gtk_hseparator_new (); + } else if (i < 2) { + content[i].new_sep = gtk_vseparator_new (); + } else { + content[i].new_sep = NULL; + } + + /* visible? */ + active = mate_volume_control_element_whitelist (mixer, track); + key = get_mateconf_key (el->mixer, track); + if ((value = mateconf_client_get (el->client, key, NULL)) != NULL && + value->type == MATECONF_VALUE_BOOL) { + active = mateconf_value_get_bool (value); + } + g_free (key); + + /* Show left separator if we're not the first track */ + if (active && content[i].use && content[i].old_sep) { + + /* Do not show separator for switches/options on Playback/Recording tab */ + if (i < 2 && track->num_channels != 0) { + gtk_widget_show (content[i].old_sep); + } + } + + /* widget */ + trkw = content[i].get_track_widget (GTK_TABLE (content[i].page), + content[i].pos++, el->mixer, track, + content[i].old_sep, content[i].new_sep, + content[i].flagbuttonbox); + mate_volume_control_track_show (trkw, active); + + /* Only the first trkw on the page will return flagbuttonbox */ + if (trkw->flagbuttonbox != NULL) + content[i].flagbuttonbox = trkw->flagbuttonbox; + g_object_set_data (G_OBJECT (track), + "mate-volume-control-trkw", trkw); + + /* separator */ + if (item->next != NULL && content[i].new_sep) { + if (i >= 2) { + gtk_table_attach (GTK_TABLE (content[i].page), content[i].new_sep, + 0, 3, content[i].pos, content[i].pos + 1, + GTK_SHRINK | GTK_FILL, 0, 0, 0); + } else { + gtk_table_attach (GTK_TABLE (content[i].page), content[i].new_sep, + content[i].pos, content[i].pos + 1, 0, 6, + 0, GTK_SHRINK | GTK_FILL, 0, 0); + } + content[i].pos++; + } + + content[i].old_sep = content[i].new_sep; + + if (active) { + content[i].use = TRUE; + } + } + + /* show - need to build the tabs backwards so that deleting the "Sound Theme" + * page can be avoided. + */ + for (i = 3; i >= 0; i--) { + GtkWidget *label, *view, *viewport; + GtkAdjustment *hadjustment, *vadjustment; + + /* don't show last separator */ + if (content[i].new_sep) + gtk_widget_destroy (content[i].new_sep); + + /* viewport for lots of tracks */ + view = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (view), + i >= 2 ? GTK_POLICY_NEVER : + GTK_POLICY_AUTOMATIC, + i >= 2 ? GTK_POLICY_AUTOMATIC : + GTK_POLICY_NEVER); + + hadjustment = gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (view)); + vadjustment = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (view)); + viewport = gtk_viewport_new (hadjustment, vadjustment); + gtk_viewport_set_shadow_type (GTK_VIEWPORT (viewport), GTK_SHADOW_NONE); + + if (content[i].flagbuttonbox != NULL) { + GtkWidget *vbox = NULL; + GtkWidget *hbox = NULL; + GtkWidget *hbox2 = NULL; + GtkWidget *separator = NULL; + + if (i < 2) { + vbox = gtk_vbox_new (FALSE, 0); + hbox = gtk_hbox_new (FALSE, 6); + hbox2 = gtk_hbox_new (FALSE, 6); + separator = gtk_hseparator_new (); + gtk_box_pack_start (GTK_BOX (vbox), content[i].page, TRUE, TRUE, 6); + gtk_box_pack_start (GTK_BOX (vbox), hbox2, FALSE, FALSE, 6); + gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 6); + gtk_box_pack_start (GTK_BOX (hbox2), separator, TRUE, TRUE, 6); + gtk_box_pack_start (GTK_BOX (hbox), content[i].flagbuttonbox, TRUE, + FALSE, 6); + } else { + /* orientation is rotated for these ... */ + vbox = gtk_hbox_new (FALSE, 0); + hbox = gtk_vbox_new (FALSE, 0); + hbox2 = gtk_vbox_new (FALSE, 0); + gtk_box_pack_start (GTK_BOX (vbox), content[i].page, FALSE, FALSE, 6); + gtk_box_pack_start (GTK_BOX (vbox), hbox2, FALSE, FALSE, 6); + gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, FALSE, 6); + gtk_box_pack_start (GTK_BOX (hbox), content[i].flagbuttonbox, TRUE, + FALSE, 6); + } + gtk_widget_show_all (hbox2); + gtk_widget_show (content[i].flagbuttonbox); + gtk_widget_show (hbox); + gtk_widget_show (content[i].page); + gtk_widget_show (vbox); + + gtk_container_add (GTK_CONTAINER (viewport), vbox); + gtk_container_add (GTK_CONTAINER (view), viewport); + } else { + gtk_container_add (GTK_CONTAINER (viewport), content[i].page); + gtk_container_add (GTK_CONTAINER (view), viewport); + } + + label = gtk_label_new (get_page_description (i)); + gtk_notebook_prepend_page (GTK_NOTEBOOK (el), view, label); + gtk_widget_show (content[i].page); + gtk_widget_show (viewport); + gtk_widget_show (view); + gtk_widget_show (label); + + update_tab_visibility (el, i, 0); + } + + /* refresh fix */ + for (i = gtk_notebook_get_n_pages (GTK_NOTEBOOK (el)) - 1; + i >= 0; i--) { + gtk_notebook_set_current_page (GTK_NOTEBOOK (el), i); + } + +#ifdef HAVE_SOUND_THEME + /* Add tab for managing themes */ + if (!theme_page) { + theme_page = TRUE; + GtkWidget *label, *view, *viewport, *sound_theme_chooser, *vbox; + GtkAdjustment *hadjustment, *vadjustment; + + label = gtk_label_new (_("Sound Theme")); + + view = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (view), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC); + + hadjustment = gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (view)); + vadjustment = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (view)); + viewport = gtk_viewport_new (hadjustment, vadjustment); + gtk_viewport_set_shadow_type (GTK_VIEWPORT (viewport), GTK_SHADOW_NONE); + gtk_viewport_set_shadow_type (GTK_VIEWPORT (viewport), GTK_SHADOW_NONE); + + sound_theme_chooser = gvc_sound_theme_chooser_new (); + vbox = gtk_vbox_new (FALSE, 0); + gtk_box_pack_start (GTK_BOX (vbox), sound_theme_chooser, TRUE, TRUE, 6); + gtk_container_add (GTK_CONTAINER (viewport), vbox); + gtk_container_add (GTK_CONTAINER (view), viewport); + + gtk_widget_show_all (vbox); + gtk_widget_show (sound_theme_chooser); + gtk_widget_show (viewport); + gtk_widget_show (view); + gtk_widget_show (label); + + gtk_notebook_append_page (GTK_NOTEBOOK (el), view, label); + } +#endif +} + +/* + * MateConf callback. + */ + +static void +cb_mateconf (MateConfClient *client, + guint connection_id, + MateConfEntry *entry, + gpointer data) +{ + MateVolumeControlElement *el = MATE_VOLUME_CONTROL_ELEMENT (data); + gchar *keybase = get_mateconf_key (el->mixer, NULL); + + if (!strncmp (mateconf_entry_get_key (entry), + keybase, strlen (keybase))) { + const GList *item; + + for (item = gst_mixer_list_tracks (el->mixer); + item != NULL; item = item->next) { + GstMixerTrack *track = item->data; + MateVolumeControlTrack *trkw = + g_object_get_data (G_OBJECT (track), "mate-volume-control-trkw"); + gchar *key = get_mateconf_key (el->mixer, track); + + g_return_if_fail (mateconf_entry_get_key (entry) != NULL); + g_return_if_fail (key != NULL); + + if (g_str_equal (mateconf_entry_get_key (entry), key)) { + MateConfValue *value = mateconf_entry_get_value (entry); + + if (value->type == MATECONF_VALUE_BOOL) { + gboolean active = mateconf_value_get_bool (value), + first[4] = { TRUE, TRUE, TRUE, TRUE }; + gint n, page = get_page_num (el->mixer, track); + + mate_volume_control_track_show (trkw, active); + + /* separators */ + for (item = gst_mixer_list_tracks (el->mixer); + item != NULL; item = item->next) { + GstMixerTrack *track = item->data; + MateVolumeControlTrack *trkw = + g_object_get_data (G_OBJECT (track), "mate-volume-control-trkw"); + + n = get_page_num (el->mixer, track); + if (trkw->visible && !first[n]) { + if (trkw->left_separator) { + if (n < 2 && track->num_channels == 0) { + gtk_widget_hide (trkw->left_separator); + } else { + gtk_widget_show (trkw->left_separator); + } + } + } else { + if (trkw->left_separator) + gtk_widget_hide (trkw->left_separator); + } + + if (trkw->visible && first[n]) + first[n] = FALSE; + } + update_tab_visibility (el, page, page); + break; + } + } + + g_free (key); + } + } + g_free (keybase); +} diff --git a/gst-mixer/src/element.h b/gst-mixer/src/element.h new file mode 100644 index 0000000..bb224a0 --- /dev/null +++ b/gst-mixer/src/element.h @@ -0,0 +1,68 @@ +/* MATE Volume Control + * Copyright (C) 2003-2004 Ronald Bultje <[email protected]> + * + * element.h: widget representation of a single mixer element + * + * 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. + */ + +#ifndef __GVC_ELEMENT_H__ +#define __GVC_ELEMENT_H__ + +#include <glib.h> +#include <gtk/gtk.h> +#include <mateconf/mateconf-client.h> +#include <gst/interfaces/mixer.h> + +G_BEGIN_DECLS + +#define MATE_VOLUME_CONTROL_TYPE_ELEMENT \ + (mate_volume_control_element_get_type ()) +#define MATE_VOLUME_CONTROL_ELEMENT(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), MATE_VOLUME_CONTROL_TYPE_ELEMENT, \ + MateVolumeControlElement)) +#define MATE_VOLUME_CONTROL_ELEMENT_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), MATE_VOLUME_CONTROL_TYPE_ELEMENT, \ + MateVolumeControlElementClass)) +#define MATE_VOLUME_CONTROL_IS_ELEMENT(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MATE_VOLUME_CONTROL_TYPE_ELEMENT)) +#define MATE_VOLUME_CONTROL_IS_ELEMENT_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), MATE_VOLUME_CONTROL_TYPE_ELEMENT)) + +typedef struct _MateVolumeControlElement { + GtkNotebook parent; + + /* current element that we're working on */ + GstMixer *mixer; + + /* mateconf client inherited from our parent */ + MateConfClient *client; +} MateVolumeControlElement; + +typedef struct _MateVolumeControlElementClass { + GtkNotebookClass klass; +} MateVolumeControlElementClass; + +GType mate_volume_control_element_get_type (void); +GtkWidget * mate_volume_control_element_new (MateConfClient *client); +void mate_volume_control_element_change (MateVolumeControlElement *el, + GstElement *element); +gboolean mate_volume_control_element_whitelist (GstMixer *mixer, + GstMixerTrack *track); + +G_END_DECLS + +#endif /* __GVC_ELEMENT_H__ */ diff --git a/gst-mixer/src/keys.h b/gst-mixer/src/keys.h new file mode 100644 index 0000000..809d507 --- /dev/null +++ b/gst-mixer/src/keys.h @@ -0,0 +1,39 @@ +/* MATE Volume Control + * Copyright (C) 2003-2004 Ronald Bultje <[email protected]> + * + * keys.h: MateConf key macros + * + * 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. + */ + +#ifndef __GVC_KEYS_H__ +#define __GVC_KEYS_H__ + +G_BEGIN_DECLS + +#define MATE_VOLUME_CONTROL_KEY_DIR \ + "/apps/mate-volume-control" +#define MATE_VOLUME_CONTROL_KEY(key) \ + MATE_VOLUME_CONTROL_KEY_DIR "/" key + +#define MATE_VOLUME_CONTROL_KEY_ACTIVE_ELEMENT \ + MATE_VOLUME_CONTROL_KEY ("active-element") +#define PREF_UI_WINDOW_WIDTH MATE_VOLUME_CONTROL_KEY ("ui/window_width") +#define PREF_UI_WINDOW_HEIGHT MATE_VOLUME_CONTROL_KEY ("ui/window_height") + +G_END_DECLS + +#endif /* __GVC_KEYS_H__ */ diff --git a/gst-mixer/src/main.c b/gst-mixer/src/main.c new file mode 100644 index 0000000..f039af8 --- /dev/null +++ b/gst-mixer/src/main.c @@ -0,0 +1,177 @@ +/* MATE Volume Control + * Copyright (C) 2003-2004 Ronald Bultje <[email protected]> + * + * main.c: intialization, window setup + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <getopt.h> +#include <glib.h> +#include <glib/gi18n.h> +#include <gtk/gtk.h> +#include <gst/gst.h> +#include <gst/audio/mixerutils.h> + +#include "keys.h" +#include "window.h" + +static gchar* page = NULL; +static GOptionEntry entries[] = +{ + { "page", 'p', 0, G_OPTION_ARG_STRING, &page, N_("Startup page"), "playback|recording|switches|options" } +}; + +/* + * Probe for mixer elements. Set up GList * with elements, + * where each element has a GObject data node set of the + * name "mate-volume-control-name" with the value being + * the human-readable name of the element. + * + * All elements in the returned GList * are in state + * GST_STATE_NULL. + */ + +static gboolean +mixer_filter_func (GstMixer * mixer, gpointer user_data) +{ + GstElementFactory *factory; + const gchar *long_name; + gchar *devname = NULL; + gchar *name; + gint *p_count = (gint *) user_data; + + /* fetch name */ + if (g_object_class_find_property (G_OBJECT_GET_CLASS (G_OBJECT (mixer)), + "device-name")) { + g_object_get (mixer, "device-name", &devname, NULL); + GST_DEBUG ("device name: %s", GST_STR_NULL (devname)); + } else { + devname = NULL; + GST_DEBUG ("device name unknown, no 'device-name' property"); + } + + factory = gst_element_get_factory (GST_ELEMENT (mixer)); + long_name = gst_element_factory_get_longname (factory); + + if (devname) { + name = g_strdup_printf ("%s (%s)", devname, long_name); + g_free (devname); + } else { + gchar *title; + + *p_count += 1; + + title = g_strdup_printf (_("Unknown Volume Control %d"), *p_count); + name = g_strdup_printf ("%s (%s)", title, long_name); + g_free (title); + } + + g_object_set_data_full (G_OBJECT (mixer), + "mate-volume-control-name", + name, + (GDestroyNotify) g_free); + + GST_DEBUG ("Adding '%s' to list of available mixers", name); + + gst_element_set_state (GST_ELEMENT (mixer), GST_STATE_NULL); + + return TRUE; /* add mixer to list */ +} + +static GList * +create_mixer_collection (void) +{ + GList *mixer_list; + gint counter = 0; + + mixer_list = gst_audio_default_registry_mixer_filter (mixer_filter_func, + FALSE, + &counter); + + return mixer_list; +} + +static void +cb_destroy (GtkWidget *widget, + gpointer data) +{ + gtk_main_quit (); +} + +static void +cb_check_resize (GtkContainer *container, + gpointer user_data) +{ + MateConfClient *client; + gint width, height; + + client = mateconf_client_get_default(); + gtk_window_get_size (GTK_WINDOW (container), &width, &height); + mateconf_client_set_int (client, PREF_UI_WINDOW_WIDTH, width, NULL); + mateconf_client_set_int (client, PREF_UI_WINDOW_HEIGHT, height, NULL); +} + +gint +main (gint argc, + gchar *argv[]) +{ + GOptionContext *ctx; + GtkWidget *win; + GList *elements; + + /* i18n */ + bindtextdomain (GETTEXT_PACKAGE, MATELOCALEDIR); + bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); + textdomain (GETTEXT_PACKAGE); + + g_thread_init (NULL); + ctx = g_option_context_new ("mate-volume-control"); + g_option_context_add_main_entries(ctx, entries, GETTEXT_PACKAGE); + g_option_context_add_group (ctx, gst_init_get_option_group ()); + g_option_context_parse(ctx, &argc, &argv, NULL); + g_option_context_free(ctx); + + gtk_init (&argc, &argv); + + gtk_window_set_default_icon_name ("multimedia-volume-control"); + + if (!(elements = create_mixer_collection ())) { + win = gtk_message_dialog_new (NULL, 0, GTK_MESSAGE_ERROR, + GTK_BUTTONS_CLOSE, + _("No volume control GStreamer plugins and/or devices found.")); + gtk_widget_show (win); + gtk_dialog_run (GTK_DIALOG (win)); + gtk_widget_destroy (win); + return -1; + } + + /* window contains everything automagically */ + win = mate_volume_control_window_new (elements); + if (page != NULL) + mate_volume_control_window_set_page(win, page); + g_signal_connect (win, "destroy", G_CALLBACK (cb_destroy), NULL); + g_signal_connect (win, "check_resize", G_CALLBACK (cb_check_resize), NULL); + + gtk_widget_show (win); + gtk_main (); + + return 0; +} diff --git a/gst-mixer/src/misc.c b/gst-mixer/src/misc.c new file mode 100644 index 0000000..4ede2c6 --- /dev/null +++ b/gst-mixer/src/misc.c @@ -0,0 +1,72 @@ +/* MATE Volume Control + * Copyright (C) 2003-2004 Ronald Bultje <[email protected]> + * + * misc.c: miscelaneous functions + * + * 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. + */ + +#include <gst/interfaces/mixer.h> +#include <gst/interfaces/mixertrack.h> +#include <gst/interfaces/mixeroptions.h> + +#include "misc.h" + +#include <glib.h> +#include <glib/gi18n.h> + +gint get_page_num (GstMixer *mixer, GstMixerTrack *track) +{ + /* is it possible to have a track that does input and output? */ + g_assert (! (GST_MIXER_TRACK_HAS_FLAG (track, GST_MIXER_TRACK_INPUT) + && GST_MIXER_TRACK_HAS_FLAG (track, GST_MIXER_TRACK_OUTPUT))); + + if ((gst_mixer_get_mixer_flags (GST_MIXER (mixer)) & + GST_MIXER_FLAG_GROUPING) == 0) { + /* old style grouping, only volume sliders on the first two pages */ + if (GST_IS_MIXER_OPTIONS (track)) + return 3; + else if (track->num_channels == 0) + return 2; + } + if (GST_MIXER_TRACK_HAS_FLAG (track, GST_MIXER_TRACK_INPUT)) + return 1; + else if (GST_MIXER_TRACK_HAS_FLAG (track, GST_MIXER_TRACK_OUTPUT)) + return 0; + else if (GST_IS_MIXER_OPTIONS (track)) + return 3; + else + return 2; + + g_assert_not_reached (); +} + +gchar *get_page_description (gint n) +{ + /* needs i18n work */ + switch (n) { + case 0: + return _("Playback"); + case 1: + return _("Recording"); + case 2: + return _("Switches"); + case 3: + return _("Options"); + } + + g_assert_not_reached (); +} diff --git a/gst-mixer/src/misc.h b/gst-mixer/src/misc.h new file mode 100644 index 0000000..722ba56 --- /dev/null +++ b/gst-mixer/src/misc.h @@ -0,0 +1,32 @@ +/* MATE Volume Control + * Copyright (C) 2003-2004 Ronald Bultje <[email protected]> + * + * element.h: widget representation of a single mixer element + * + * 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. + */ + +#ifndef __GST_MIXER_MISC_H__ +#define __GST_MIXER_MISC_H__ + +#include <glib.h> +#include <gst/interfaces/mixertrack.h> + +gint get_page_num (GstMixer *mixer, GstMixerTrack *track); + +gchar *get_page_description (gint n); + +#endif /* __GST_MIXER_MISC_H__ */ diff --git a/gst-mixer/src/preferences.c b/gst-mixer/src/preferences.c new file mode 100644 index 0000000..7b34085 --- /dev/null +++ b/gst-mixer/src/preferences.c @@ -0,0 +1,441 @@ +/* MATE Volume Control + * Copyright (C) 2003-2004 Ronald Bultje <[email protected]> + * + * preferences.c: preferences screen + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <string.h> +#include <glib/gi18n.h> +#include <gtk/gtk.h> +#include <mateconf/mateconf-client.h> + +#include "element.h" +#include "preferences.h" +#include "keys.h" +#include "track.h" +#include "misc.h" + +enum { + COL_ACTIVE, + COL_LABEL, + COL_TRACK, + COL_TYPE, + COL_PAGE, + NUM_COLS +}; + +G_DEFINE_TYPE (MateVolumeControlPreferences, mate_volume_control_preferences, GTK_TYPE_DIALOG) + +static void mate_volume_control_preferences_class_init (MateVolumeControlPreferencesClass *klass); +static void mate_volume_control_preferences_init (MateVolumeControlPreferences *prefs); +static void mate_volume_control_preferences_dispose (GObject *object); +static void mate_volume_control_preferences_response (GtkDialog *dialog, + gint response_id); + +static void set_mateconf_track_active (MateConfClient *client, GstMixer *mixer, + GstMixerTrack *track, gboolean active); + + +static void cb_toggle (GtkCellRendererToggle *cell, + gchar *path_str, + gpointer data); +static void cb_activated (GtkTreeView *view, GtkTreePath *path, + GtkTreeViewColumn *col, gpointer userdata); +static void cb_mateconf (MateConfClient *client, + guint connection_id, + MateConfEntry *entry, + gpointer userdata); + + +static void +mate_volume_control_preferences_class_init (MateVolumeControlPreferencesClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GtkDialogClass *gtkdialog_class = (GtkDialogClass *) klass; + + gobject_class->dispose = mate_volume_control_preferences_dispose; + gtkdialog_class->response = mate_volume_control_preferences_response; +} + +/* + * Mixer tracks are sorted by their types. + */ +static gint +sort_by_page_num (GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer user_data) +{ + gint a_type, b_type; + + gtk_tree_model_get (model, a, COL_PAGE, &a_type, -1); + gtk_tree_model_get (model, b, COL_PAGE, &b_type, -1); + + return (a_type - b_type); +} + +static void +mate_volume_control_preferences_init (MateVolumeControlPreferences *prefs) +{ + GtkWidget *box, *label, *view; + GtkListStore *store; + GtkTreeSelection *sel; + GtkTreeViewColumn *col; + GtkCellRenderer *render; + + prefs->client = NULL; + prefs->client_cnxn = 0; + prefs->mixer = NULL; + + /* make window look cute */ + gtk_window_set_title (GTK_WINDOW (prefs), _("Volume Control Preferences")); + gtk_dialog_set_has_separator (GTK_DIALOG (prefs), FALSE); + gtk_container_set_border_width (GTK_CONTAINER (prefs), 5); + gtk_box_set_spacing (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (prefs))), 2); + gtk_dialog_add_buttons (GTK_DIALOG (prefs), + GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, + /* help goes here (future) */ + NULL); + + /* add a treeview for all the properties */ + box = gtk_vbox_new (FALSE, 6); + gtk_container_set_border_width (GTK_CONTAINER (box), 5); + + label = gtk_label_new_with_mnemonic (_("_Select mixers to be visible:")); + gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); + gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 0); + gtk_widget_show (label); + + store = gtk_list_store_new (NUM_COLS, G_TYPE_BOOLEAN, + G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_STRING, + G_TYPE_INT); + gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (store), sort_by_page_num, NULL, NULL); + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store), GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID, GTK_SORT_ASCENDING); + prefs->treeview = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store)); + gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (prefs->treeview), FALSE); + gtk_label_set_mnemonic_widget (GTK_LABEL(label), GTK_WIDGET (prefs->treeview)); + + /* viewport for lots of tracks */ + view = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (view), + GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (view), + GTK_SHADOW_IN); + gtk_widget_set_size_request (view, -1, 250); + + gtk_container_add (GTK_CONTAINER (view), prefs->treeview); + gtk_box_pack_start (GTK_BOX (box), view, TRUE, TRUE, 0); + + gtk_widget_show (prefs->treeview); + gtk_widget_show (view); + + /* treeview internals */ + sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (prefs->treeview)); + gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE); + + render = gtk_cell_renderer_toggle_new (); + g_signal_connect (render, "toggled", + G_CALLBACK (cb_toggle), prefs); + g_signal_connect (prefs->treeview, "row-activated", + G_CALLBACK (cb_activated), prefs); + col = gtk_tree_view_column_new_with_attributes ("Active", render, + "active", COL_ACTIVE, + NULL); + gtk_tree_view_column_set_clickable (col, TRUE); + gtk_tree_view_append_column (GTK_TREE_VIEW (prefs->treeview), col); + + render = gtk_cell_renderer_text_new (); + col = gtk_tree_view_column_new_with_attributes ("Track name", render, + "text", COL_LABEL, + NULL); + gtk_tree_view_append_column (GTK_TREE_VIEW (prefs->treeview), col); + + render = gtk_cell_renderer_text_new (); + col = gtk_tree_view_column_new_with_attributes ("Type", render, + "text", COL_TYPE, + NULL); + gtk_tree_view_append_column (GTK_TREE_VIEW (prefs->treeview), col); + + gtk_tree_view_set_search_column (GTK_TREE_VIEW (prefs->treeview), COL_LABEL); + + /* and show */ + gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (prefs))), box, + TRUE, TRUE, 0); + gtk_widget_show (box); +} + +GtkWidget * +mate_volume_control_preferences_new (GstElement *element, + MateConfClient *client) +{ + MateVolumeControlPreferences *prefs; + + g_return_val_if_fail (GST_IS_MIXER (element), NULL); + + /* element */ + prefs = g_object_new (MATE_VOLUME_CONTROL_TYPE_PREFERENCES, NULL); + prefs->client = g_object_ref (G_OBJECT (client)); + + mate_volume_control_preferences_change (prefs, element); + + /* mateconf */ + prefs->client_cnxn = mateconf_client_notify_add (prefs->client, + MATE_VOLUME_CONTROL_KEY_DIR, + cb_mateconf, prefs, NULL, NULL); + + return GTK_WIDGET (prefs); +} + +static void +mate_volume_control_preferences_dispose (GObject *object) +{ + MateVolumeControlPreferences *prefs; + + prefs = MATE_VOLUME_CONTROL_PREFERENCES (object); + + if (prefs->client) { + mateconf_client_notify_remove (prefs->client, prefs->client_cnxn); + g_object_unref (G_OBJECT (prefs->client)); + prefs->client = NULL; + } + + if (prefs->mixer) { + gst_object_unref (GST_OBJECT (prefs->mixer)); + prefs->mixer = NULL; + } + + G_OBJECT_CLASS (mate_volume_control_preferences_parent_class)->dispose (object); +} + +static void +mate_volume_control_preferences_response (GtkDialog *dialog, + gint response_id) +{ + switch (response_id) { + case GTK_RESPONSE_CLOSE: + gtk_widget_destroy (GTK_WIDGET (dialog)); + break; + + default: + break; + } + + if (((GtkDialogClass *) mate_volume_control_preferences_parent_class)->response) + ((GtkDialogClass *) mate_volume_control_preferences_parent_class)->response (dialog, response_id); +} + +/* + * Hide non-alphanumeric characters, for saving in mateconf. + */ + +gchar * +get_mateconf_key (GstMixer *mixer, GstMixerTrack *track) +{ + const gchar *dev; + gchar *res; + gint i, pos; + gchar *label = NULL; + + g_return_val_if_fail(mixer != NULL, NULL); + + dev = g_object_get_data (G_OBJECT (mixer), + "mate-volume-control-name"); + if (track != NULL) { + label = g_strdup (track->label); + } else { + label = g_strdup (""); + } + + pos = strlen (MATE_VOLUME_CONTROL_KEY_DIR) + 1; + res = g_new (gchar, pos + strlen (dev) + 1 + strlen (label) + 1); + strcpy (res, MATE_VOLUME_CONTROL_KEY_DIR "/"); + + for (i = 0; dev[i] != '\0'; i++) { + if (g_ascii_isalnum (dev[i])) + res[pos++] = dev[i]; + } + res[pos] = '/'; + for (i = 0; label[i] != '\0'; i++) { + if (g_ascii_isalnum (label[i])) + res[pos++] = label[i]; + } + res[pos] = '\0'; + + g_free (label); + return res; +} + +/* + * Change the element. Basically recreates this object internally. + */ + +void +mate_volume_control_preferences_change (MateVolumeControlPreferences *prefs, + GstElement *element) +{ + GstMixer *mixer; + GtkTreeIter iter; + GtkListStore *store; + const GList *item; + gint pgnum; + + g_return_if_fail (GST_IS_MIXER (element)); + mixer = GST_MIXER (element); + + store = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (prefs->treeview))); + + /* remove old */ + while (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter)) { + gtk_list_store_remove (store, &iter); + } + + /* take/put reference */ + gst_object_replace ((GstObject **) &prefs->mixer, GST_OBJECT (element)); + + /* add all tracks */ + mate_volume_control_element_whitelist (mixer, NULL); + for (item = gst_mixer_list_tracks (mixer); + item != NULL; item = item->next) { + GstMixerTrack *track = item->data; + gchar *key = get_mateconf_key (mixer, track); + MateConfValue *value; + gboolean active = mate_volume_control_element_whitelist (mixer, track); + + if ((value = mateconf_client_get (prefs->client, key, NULL)) != NULL && + value->type == MATECONF_VALUE_BOOL) { + active = mateconf_value_get_bool (value); + } + g_free (key); + + pgnum = get_page_num (mixer, track); + gtk_list_store_append (store, &iter); + gtk_list_store_set (store, &iter, + COL_ACTIVE, active, + COL_LABEL, track->label, + COL_TRACK, track, + COL_TYPE, get_page_description (pgnum), + COL_PAGE, pgnum, + -1); + } +} + +/* + * Callback if something is toggled. + */ + +static void +set_mateconf_track_active(MateConfClient *client, GstMixer *mixer, + GstMixerTrack *track, gboolean active) +{ + gchar *key; + + key = get_mateconf_key (mixer, track); + mateconf_client_set_bool (client, key, active, NULL); + g_free (key); +} + +static void +cb_mateconf(MateConfClient *client, guint connection_id, + MateConfEntry *entry, gpointer userdata) +{ + MateVolumeControlPreferences *prefs; + MateConfValue *value; + GtkTreeIter iter; + GtkTreeModel *model; + gchar *keybase; + gboolean active, valid; + GstMixerTrack *track; + + prefs = MATE_VOLUME_CONTROL_PREFERENCES (userdata); + model = gtk_tree_view_get_model (GTK_TREE_VIEW(prefs->treeview)); + keybase = get_mateconf_key (prefs->mixer, NULL); + + if (g_str_equal (mateconf_entry_get_key (entry), keybase) && + (value = mateconf_entry_get_value (entry)) != NULL && + (value->type == MATECONF_VALUE_BOOL)) { + active = mateconf_value_get_bool (value); + valid = gtk_tree_model_get_iter_first(model, &iter); + + while (valid == TRUE) { + gtk_tree_model_get (model, &iter, + COL_TRACK, &track, + -1); + if (g_str_equal (track->label, mateconf_entry_get_key (entry) + strlen (keybase))) { + gtk_list_store_set( GTK_LIST_STORE(model), &iter, COL_ACTIVE, active, -1); + break ; + } + valid = gtk_tree_model_iter_next(model, &iter); + } + } +} + +static void +cb_activated(GtkTreeView *view, GtkTreePath *path, + GtkTreeViewColumn *col, gpointer userdata) + +{ + GtkTreeModel *model; + GtkTreeIter iter; + gboolean active; + GstMixerTrack *track; + MateVolumeControlPreferences *prefs; + + prefs = MATE_VOLUME_CONTROL_PREFERENCES (userdata); + model = gtk_tree_view_get_model(view); + + if (gtk_tree_model_get_iter(model, &iter, path)) { + gtk_tree_model_get(model, &iter, + COL_ACTIVE, &active, + COL_TRACK, &track, + -1); + + active = !active; + + gtk_list_store_set( GTK_LIST_STORE(model), &iter, COL_ACTIVE, active, -1); + set_mateconf_track_active(prefs->client, prefs->mixer, track, active); + } +} + +static void +cb_toggle (GtkCellRendererToggle *cell, + gchar *path_str, + gpointer data) +{ + MateVolumeControlPreferences *prefs = data; + GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (prefs->treeview)); + GtkTreePath *path = gtk_tree_path_new_from_string (path_str); + GtkTreeIter iter; + gboolean active; + GstMixerTrack *track; + + gtk_tree_model_get_iter (model, &iter, path); + gtk_tree_model_get (model, &iter, + COL_ACTIVE, &active, + COL_TRACK, &track, + -1); + + active = !active; + + gtk_list_store_set (GTK_LIST_STORE (model), &iter, + COL_ACTIVE, active, + -1); + gtk_tree_path_free (path); + + set_mateconf_track_active(prefs->client, prefs->mixer, track, active); +} diff --git a/gst-mixer/src/preferences.h b/gst-mixer/src/preferences.h new file mode 100644 index 0000000..d9850f4 --- /dev/null +++ b/gst-mixer/src/preferences.h @@ -0,0 +1,77 @@ +/* MATE Volume Control + * Copyright (C) 2003-2004 Ronald Bultje <[email protected]> + * + * preferences.h: preferences screen + * + * 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. + */ + +#ifndef __GVC_PREFERENCES_H__ +#define __GVC_PREFERENCES_H__ + +#include <glib.h> +#include <gtk/gtk.h> +#include <mateconf/mateconf-client.h> +#include <gst/interfaces/mixer.h> + +G_BEGIN_DECLS + +#define MATE_VOLUME_CONTROL_TYPE_PREFERENCES \ + (mate_volume_control_preferences_get_type ()) +#define MATE_VOLUME_CONTROL_PREFERENCES(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), MATE_VOLUME_CONTROL_TYPE_PREFERENCES, \ + MateVolumeControlPreferences)) +#define MATE_VOLUME_CONTROL_PREFERENCES_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), MATE_VOLUME_CONTROL_TYPE_PREFERENCES, \ + MateVolumeControlPreferencesClass)) +#define MATE_VOLUME_CONTROL_IS_PREFERENCES(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MATE_VOLUME_CONTROL_TYPE_PREFERENCES)) +#define MATE_VOLUME_CONTROL_IS_PREFERENCES_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), MATE_VOLUME_CONTROL_TYPE_PREFERENCES)) + +typedef struct _MateVolumeControlPreferences { + GtkDialog parent; + + /* current element that we're working on */ + GstMixer *mixer; + + /* mateconf client inherited from our parent */ + MateConfClient *client; + guint client_cnxn; + + /* treeview inside us */ + GtkWidget *treeview; +} MateVolumeControlPreferences; + +typedef struct _MateVolumeControlPreferencesClass { + GtkDialogClass klass; +} MateVolumeControlPreferencesClass; + +GType mate_volume_control_preferences_get_type (void); +GtkWidget *mate_volume_control_preferences_new (GstElement *element, + MateConfClient *client); +void mate_volume_control_preferences_change (MateVolumeControlPreferences *prefs, + GstElement *element); + +/* + * MateConf thingy. Escapes spaces and such. + */ +gchar * get_mateconf_key (GstMixer *mixer, GstMixerTrack *track); + + +G_END_DECLS + +#endif /* __GVC_PREFERENCES_H__ */ diff --git a/gst-mixer/src/track.c b/gst-mixer/src/track.c new file mode 100644 index 0000000..9b742be --- /dev/null +++ b/gst-mixer/src/track.c @@ -0,0 +1,647 @@ +/* MATE Volume Control + * Copyright (C) 2003-2004 Ronald Bultje <[email protected]> + * + * track.c: layout of a single mixer track + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <unistd.h> +#include <glib/gi18n.h> +#include <string.h> + +#include "button.h" +#include "track.h" +#include "volume.h" + +static const struct { + gchar *label, + *pixmap; +} pix[] = { + { "cd", "media-optical" }, + { "line", "gvc-line-in" }, + { "aux", "gvc-line-in" }, + { "mic", "audio-input-microphone" }, + { "cap", "gvc-line-in" }, + { "mix", "multimedia-volume-control" }, + { "pcm", "gvc-tone" }, + { "headphone", "gvc-headphones" }, + { "phone", "phone" }, + { "speaker", "audio-volume-high" }, + { "front", "audio-volume-high" }, + { "surround", "audio-volume-high" }, + { "side", "audio-volume-high" }, + { "center", "audio-volume-high" }, + { "lfe", "audio-volume-high" }, + { "video", "video-display" }, + { "volume", "gvc-tone" }, + { "master", "gvc-tone" }, + { "3d", "gvc-3d-sound" }, + { "beep", "keyboard" }, + { "record", "audio-input-microphone" }, + { NULL, NULL } +}; + +/* + * UI responses. + */ + +static void +cb_mute_toggled (MateVolumeControlButton *button, + gpointer data) +{ + MateVolumeControlTrack *ctrl = data; + + gst_mixer_set_mute (ctrl->mixer, ctrl->track, + !mate_volume_control_button_get_active (button)); +} + +static void +cb_record_toggled (MateVolumeControlButton *button, + gpointer data) +{ + MateVolumeControlTrack *ctrl = data; + + gst_mixer_set_record (ctrl->mixer, ctrl->track, + mate_volume_control_button_get_active (button)); +} + +/* Tells us whether toggling a switch should change the corresponding + * GstMixerTrack's MUTE or RECORD flag. + */ +static gboolean +should_toggle_record_switch (const GstMixerTrack *track) +{ + return (GST_MIXER_TRACK_HAS_FLAG (track, GST_MIXER_TRACK_INPUT) && + !GST_MIXER_TRACK_HAS_FLAG (track, GST_MIXER_TRACK_NO_RECORD)); +} + + +static void +cb_toggle_changed (GtkToggleButton *button, + gpointer data) +{ + MateVolumeControlTrack *ctrl = data; + + if (should_toggle_record_switch (ctrl->track)) { + gst_mixer_set_record (ctrl->mixer, ctrl->track, + gtk_toggle_button_get_active (button)); + } else { + gst_mixer_set_mute (ctrl->mixer, ctrl->track, + !gtk_toggle_button_get_active (button)); + } +} + +static void +cb_option_changed (GtkComboBox *box, + gpointer data) +{ + MateVolumeControlTrack *ctrl = data; + gchar *opt; + + opt = gtk_combo_box_get_active_text (box); + if (opt) + gst_mixer_set_option (ctrl->mixer, GST_MIXER_OPTIONS (ctrl->track), opt); + g_free (opt); +} + +/* + * Timeout to check for changes in mixer outside ourselves. + */ + +void +mate_volume_control_track_update (MateVolumeControlTrack *trkw) +{ + gboolean mute, record; + gboolean vol_is_zero = FALSE, slider_is_zero = FALSE; + GstMixer *mixer; + GstMixerTrack *track; + gint i; + gint *dummy; + + g_return_if_fail (trkw != NULL); + + track = trkw->track; + mixer = trkw->mixer; + + /* trigger an update of the mixer state */ + if (GST_IS_MIXER_OPTIONS (track)) { + const GList *opt; + GstMixerOptions *options = GST_MIXER_OPTIONS (track); + const char *active_opt; + active_opt = gst_mixer_get_option (mixer, options); + + for (i = 0, opt = gst_mixer_options_get_values (options); + opt != NULL; + opt = opt->next, i++) { + if (g_str_equal (active_opt, opt->data)) { + gtk_combo_box_set_active (GTK_COMBO_BOX (trkw->options), i); + } + } + + return; + } + + dummy = g_new (gint, MAX (track->num_channels, 1)); + gst_mixer_get_volume (mixer, track, dummy); + g_free (dummy); + + mute = GST_MIXER_TRACK_HAS_FLAG (track, GST_MIXER_TRACK_MUTE) ? + TRUE : FALSE; + record = GST_MIXER_TRACK_HAS_FLAG (track, GST_MIXER_TRACK_RECORD) ? + TRUE : FALSE; + + if (trkw->sliderbox) { + mate_volume_control_volume_update (MATE_VOLUME_CONTROL_VOLUME (trkw->sliderbox)); + mate_volume_control_volume_ask ( + MATE_VOLUME_CONTROL_VOLUME (trkw->sliderbox), + &vol_is_zero, &slider_is_zero); + + if (trkw->mute && !slider_is_zero && vol_is_zero) + mute = TRUE; + } + + if (trkw->mute) { + if (mate_volume_control_button_get_active (trkw->mute) == mute) { + mate_volume_control_button_set_active (trkw->mute, !mute); + } + } + + if (trkw->record) { + if (mate_volume_control_button_get_active (trkw->record) != record) { + mate_volume_control_button_set_active (trkw->record, record); + } + } + + if (trkw->toggle) { + if (should_toggle_record_switch (trkw->track)) { + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (trkw->toggle), + GST_MIXER_TRACK_HAS_FLAG (trkw->track, GST_MIXER_TRACK_RECORD)); + } else { + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (trkw->toggle), + !GST_MIXER_TRACK_HAS_FLAG (trkw->track, GST_MIXER_TRACK_MUTE)); + } + } + + /* FIXME: + * - options. + */ +} + + +static gboolean +cb_check (gpointer data) +{ + mate_volume_control_track_update (data); + + return TRUE; +} + +/* + * Actual UI code. + */ + +static MateVolumeControlTrack * +mate_volume_control_track_add_title (GtkTable *table, + gint tab_pos, + GtkOrientation or, + GstMixer *mixer, + GstMixerTrack *track, + GtkWidget *l_sep, + GtkWidget *r_sep) +{ + MateVolumeControlTrack *ctrl; + gchar *ulabel = NULL; + gchar *str = NULL; + gint i; + gboolean need_timeout = TRUE; + + need_timeout = ((gst_mixer_get_mixer_flags (GST_MIXER (mixer)) & + GST_MIXER_FLAG_AUTO_NOTIFICATIONS) == 0); + + /* start */ + ctrl = g_new0 (MateVolumeControlTrack, 1); + ctrl->mixer = mixer; + g_object_ref (G_OBJECT (track)); + ctrl->track = track; + ctrl->left_separator = l_sep; + ctrl->right_separator = r_sep; + ctrl->visible = TRUE; + ctrl->table = table; + ctrl->pos = tab_pos; + if (need_timeout) + ctrl->id = g_timeout_add (200, cb_check, ctrl); + ctrl->flagbuttonbox = NULL; + + /* find image from label string (optional) */ + if (g_object_class_find_property (G_OBJECT_GET_CLASS (track), "untranslated-label")) + g_object_get (track, "untranslated-label", &ulabel, NULL); + + if (ulabel == NULL) + g_object_get (track, "label", &ulabel, NULL); + + if (ulabel) { + gint pos; + + /* make case insensitive */ + for (pos = 0; ulabel[pos] != '\0'; pos++) + ulabel[pos] = g_ascii_tolower (ulabel[pos]); + + for (i = 0; pix[i].label != NULL; i++) { + if (g_strrstr (ulabel, pix[i].label) != NULL) { + str = pix[i].pixmap; + break; + } + } + + g_free (ulabel); + } + + if ((str != NULL) && (track->num_channels != 0)) { + if ((ctrl->image = gtk_image_new_from_icon_name (str, GTK_ICON_SIZE_MENU)) != NULL) { + gtk_misc_set_alignment (GTK_MISC (ctrl->image), 0.5, 0.5); + if (or == GTK_ORIENTATION_VERTICAL) { + gtk_table_attach (GTK_TABLE (table), ctrl->image, + tab_pos, tab_pos + 1, 0, 1, + GTK_EXPAND, 0, 0, 0); + } else { + gtk_table_attach (GTK_TABLE (table), ctrl->image, + 0, 1, tab_pos, tab_pos + 1, + 0, GTK_EXPAND, 0, 0); + } + gtk_widget_show (ctrl->image); + } + } + + /* text label */ + if (or == GTK_ORIENTATION_HORIZONTAL) + str = g_strdup_printf (_("%s:"), track->label); + else + str = g_strdup (track->label); + ctrl->label = gtk_label_new (str); + if (or == GTK_ORIENTATION_HORIZONTAL) { + g_free (str); + gtk_misc_set_alignment (GTK_MISC (ctrl->label), 0.0, 0.5); + } + if (or == GTK_ORIENTATION_VERTICAL) { + gtk_table_attach (table, ctrl->label, + tab_pos, tab_pos + 1, 1, 2, + GTK_EXPAND, 0, 0, 0); + } else { + gtk_table_attach (table, ctrl->label, + 1, 2, tab_pos, tab_pos + 1, + GTK_FILL, GTK_EXPAND, 0, 0); + } + gtk_widget_show (ctrl->label); + + return ctrl; +} + +static void +mate_volume_control_track_put_switch (GtkTable *table, + gint tab_pos, + MateVolumeControlTrack *ctrl) +{ + GtkWidget *button; + AtkObject *accessible; + gchar *accessible_name, *msg; + + /* container box */ + ctrl->buttonbox = gtk_hbox_new (FALSE, 0); + gtk_table_attach (GTK_TABLE (table), ctrl->buttonbox, + tab_pos, tab_pos + 1, + 3, 4, GTK_EXPAND, 0, 0, 0); + gtk_widget_show (ctrl->buttonbox); + + /* if we weren't supposed to show the mute button, then don't create it */ + if (GST_MIXER_TRACK_HAS_FLAG (ctrl->track, GST_MIXER_TRACK_NO_MUTE)) { + return; + } + + /* mute button */ + msg = g_strdup_printf (_("Mute/Unmute %s"), ctrl->track->label); + button = mate_volume_control_button_new ("audio-volume-high", + "audio-volume-muted", + msg); + ctrl->mute = MATE_VOLUME_CONTROL_BUTTON (button); + g_free (msg); + + mate_volume_control_button_set_active ( + MATE_VOLUME_CONTROL_BUTTON (button), + !GST_MIXER_TRACK_HAS_FLAG (ctrl->track, GST_MIXER_TRACK_MUTE)); + + g_signal_connect (G_OBJECT (button), "clicked", + G_CALLBACK (cb_mute_toggled), ctrl); + + /* a11y */ + accessible = gtk_widget_get_accessible (button); + if (GTK_IS_ACCESSIBLE (accessible)) { + accessible_name = g_strdup_printf (_("Track %s: mute"), + ctrl->track->label); + atk_object_set_name (accessible, accessible_name); + g_free (accessible_name); + } + + /* show */ + gtk_box_pack_start (GTK_BOX (ctrl->buttonbox), button, + FALSE, FALSE, 0); + gtk_widget_show (button); +} + +MateVolumeControlTrack * +mate_volume_control_track_add_playback (GtkTable *table, + gint tab_pos, + GstMixer *mixer, + GstMixerTrack *track, + GtkWidget *l_sep, + GtkWidget *r_sep, + GtkWidget *fbox) +{ + MateVolumeControlTrack *ctrl; + + /* switch and options exception (no sliders) */ + if (track->num_channels == 0) { + if (GST_IS_MIXER_OPTIONS (track)) { + return (mate_volume_control_track_add_option (table, tab_pos, mixer, track, + l_sep, r_sep, fbox)); + } + return (mate_volume_control_track_add_switch (table, tab_pos, mixer, track, + l_sep, r_sep, fbox)); + } + + /* image, title */ + ctrl = mate_volume_control_track_add_title (table, tab_pos, + GTK_ORIENTATION_VERTICAL, + mixer, track, l_sep, r_sep); + + ctrl->sliderbox = mate_volume_control_volume_new (ctrl->mixer, + ctrl->track, 6); + gtk_table_attach (GTK_TABLE (table), ctrl->sliderbox, + tab_pos, tab_pos + 1, 2, 3, + GTK_EXPAND, GTK_EXPAND | GTK_FILL, 0, 0); + gtk_widget_show (ctrl->sliderbox); + + /* mute button */ + mate_volume_control_track_put_switch (table, tab_pos, ctrl); + + return ctrl; +} + +MateVolumeControlTrack * +mate_volume_control_track_add_recording (GtkTable *table, + gint tab_pos, + GstMixer *mixer, + GstMixerTrack *track, + GtkWidget *l_sep, + GtkWidget *r_sep, + GtkWidget *fbox) +{ + MateVolumeControlTrack *ctrl; + GtkWidget *button; + AtkObject *accessible; + gchar *accessible_name, *msg; + + ctrl = mate_volume_control_track_add_playback (table, tab_pos, mixer, + track, l_sep, r_sep, fbox); + if (track->num_channels == 0) { + return ctrl; + } + + /* FIXME: + * - there's something fishy about this button, it + * is always FALSE. + */ + if (!GST_MIXER_TRACK_HAS_FLAG (track, GST_MIXER_TRACK_NO_RECORD)) { + /* only the record button here */ + msg = g_strdup_printf (_("Toggle audio recording from %s"), + ctrl->track->label); + button = mate_volume_control_button_new ("audio-input-microphone", + "audio-input-microphone-muted", + msg); + ctrl->record = MATE_VOLUME_CONTROL_BUTTON (button); + g_free (msg); + mate_volume_control_button_set_active (MATE_VOLUME_CONTROL_BUTTON (button), + GST_MIXER_TRACK_HAS_FLAG (track, + GST_MIXER_TRACK_RECORD)); + g_signal_connect (G_OBJECT (button), "clicked", + G_CALLBACK (cb_record_toggled), ctrl); + + /* a11y */ + accessible = gtk_widget_get_accessible (button); + if (GTK_IS_ACCESSIBLE (accessible)) { + accessible_name = g_strdup_printf (_("Track %s: audio recording"), + track->label); + atk_object_set_name (accessible, accessible_name); + g_free (accessible_name); + } + + /* attach, show */ + gtk_box_pack_start (GTK_BOX (ctrl->buttonbox), button, + FALSE, FALSE, 0); + gtk_widget_show (button); + } + + return ctrl; +} + +MateVolumeControlTrack * +mate_volume_control_track_add_switch (GtkTable *table, + gint tab_pos, + GstMixer *mixer, + GstMixerTrack *track, + GtkWidget *l_sep, + GtkWidget *r_sep, + GtkWidget *fbox) +{ + MateVolumeControlTrack *ctrl; + GtkWidget *toggle; + gint volume; + + /* image, title */ + toggle = gtk_check_button_new (); + + /* this is a hack - we query volume to initialize switch state */ + gst_mixer_get_volume (mixer, track, &volume); + + if (should_toggle_record_switch (track)) { + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), + GST_MIXER_TRACK_HAS_FLAG (track, GST_MIXER_TRACK_RECORD)); + } else { + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), + !GST_MIXER_TRACK_HAS_FLAG (track, GST_MIXER_TRACK_MUTE)); + } + + if (fbox == NULL) { + fbox = gtk_table_new(0, 3, FALSE); + gtk_table_set_col_spacings (GTK_TABLE (fbox), 6); + } + table = GTK_TABLE (fbox); + + ctrl = mate_volume_control_track_add_title (table, tab_pos, + GTK_ORIENTATION_HORIZONTAL, + mixer, track, l_sep, r_sep); + ctrl->toggle = toggle; + ctrl->flagbuttonbox = fbox; + + /* attach'n'show */ + gtk_table_attach (table, toggle, + 2, 3, tab_pos, tab_pos + 1, + GTK_EXPAND | GTK_FILL, GTK_EXPAND, 0, 0); + + g_signal_connect (toggle, "toggled", + G_CALLBACK (cb_toggle_changed), ctrl); + gtk_widget_show (toggle); + + return ctrl; +} + +MateVolumeControlTrack * +mate_volume_control_track_add_option (GtkTable *table, + gint tab_pos, + GstMixer *mixer, + GstMixerTrack *track, + GtkWidget *l_sep, + GtkWidget *r_sep, + GtkWidget *fbox) +{ + MateVolumeControlTrack *ctrl; + GstMixerOptions *options = GST_MIXER_OPTIONS (track); + const GList *opt, *opts; + AtkObject *accessible; + gchar *accessible_name; + gint i = 0; + const gchar *active_opt; + + if (fbox == NULL) { + fbox = gtk_table_new(0, 3, FALSE); + gtk_table_set_col_spacings (GTK_TABLE (fbox), 6); + } + table = GTK_TABLE (fbox); + + ctrl = mate_volume_control_track_add_title (table, tab_pos, + GTK_ORIENTATION_HORIZONTAL, + mixer, track, l_sep, r_sep); + + /* optionmenu */ + active_opt = gst_mixer_get_option (mixer, options); + if (active_opt != NULL) { + ctrl->options = gtk_combo_box_new_text (); + opts = gst_mixer_options_get_values (options); + for (opt = opts; opt != NULL; opt = opt->next, i++) { + if (opt->data == NULL) + continue; + + gtk_combo_box_append_text (GTK_COMBO_BOX (ctrl->options), opt->data); + + if (g_str_equal (active_opt, opt->data)) { + gtk_combo_box_set_active (GTK_COMBO_BOX (ctrl->options), i); + } + } + } + + /* a11y */ + accessible = gtk_widget_get_accessible (ctrl->options); + if (GTK_IS_ACCESSIBLE (accessible)) { + accessible_name = g_strdup_printf (_("%s Option Selection"), + ctrl->track->label); + atk_object_set_name (accessible, accessible_name); + g_free (accessible_name); + } + gtk_widget_show (ctrl->options); + g_signal_connect (ctrl->options, "changed", + G_CALLBACK (cb_option_changed), ctrl); + + ctrl->flagbuttonbox = fbox; + + /* attach'n'show */ + gtk_table_attach (table, ctrl->options, + 2, 3, tab_pos, tab_pos + 1, + GTK_EXPAND | GTK_FILL, GTK_EXPAND, 0, 0); + gtk_widget_show (ctrl->options); + + return ctrl; +} + +void +mate_volume_control_track_free (MateVolumeControlTrack *track) +{ + if (track->id != 0) { + g_source_remove (track->id); + track->id = 0; + } + + g_object_unref (G_OBJECT (track->track)); + + g_free (track); +} + +void +mate_volume_control_track_show (MateVolumeControlTrack *track, + gboolean visible) +{ +#define func(w) \ + if (w != NULL) { \ + if (visible) { \ + gtk_widget_show (w); \ + } else { \ + gtk_widget_hide (w); \ + } \ + } + + func (track->label); + func (track->image); + func (track->sliderbox); + func (track->buttonbox); + func (track->toggle); + func (track->options); + + track->visible = visible; + + /* get rid of spacing between hidden tracks */ + if (visible) { + if (track->options) { + gtk_table_set_row_spacing (track->table, + track->pos, 6); + if (track->pos > 0) + gtk_table_set_row_spacing (track->table, + track->pos - 1, 6); + } else if (!track->toggle) { + gtk_table_set_col_spacing (track->table, + track->pos, 6); + if (track->pos > 0) + gtk_table_set_col_spacing (track->table, + track->pos - 1, 6); + } + } else { + if (track->options) { + gtk_table_set_row_spacing (track->table, + track->pos, 0); + if (track->pos > 0) + gtk_table_set_row_spacing (track->table, + track->pos - 1, 0); + } else if (!track->toggle) { + gtk_table_set_col_spacing (track->table, + track->pos, 0); + if (track->pos > 0) + gtk_table_set_col_spacing (track->table, + track->pos - 1, 0); + } + } +} diff --git a/gst-mixer/src/track.h b/gst-mixer/src/track.h new file mode 100644 index 0000000..1ace5f8 --- /dev/null +++ b/gst-mixer/src/track.h @@ -0,0 +1,113 @@ +/* MATE Volume Control + * Copyright (C) 2003-2004 Ronald Bultje <[email protected]> + * + * track.h: layout of a single mixer track + * + * 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. + */ + +#ifndef __GVC_TRACK_H__ +#define __GVC_TRACK_H__ + +#include <glib.h> +#include <gtk/gtk.h> +#include <mateconf/mateconf-client.h> +#include <gst/gst.h> +#include <gst/interfaces/mixer.h> + +#include "button.h" + +G_BEGIN_DECLS + +typedef struct _MateVolumeControlTrack { + /* pointer to table in which we write */ + GtkTable *table; + gint pos; + + /* gstreamer object pointers */ + GstMixer *mixer; + GstMixerTrack *track; + + /* widgets associated with this track */ + GtkWidget *label, + *image, + *sliderbox, + *buttonbox, + *toggle, + *options, + *flagbuttonbox; + + MateVolumeControlButton *mute, *record; + + /* list of slider adjustments */ + GList *sliders; + + /* separator left/right (or top/bottom) of the actual widget */ + GtkWidget *left_separator, + *right_separator; + + /* whether we're currently "visible" */ + gboolean visible; + + /* signal IDs */ + guint id; +} MateVolumeControlTrack; + +MateVolumeControlTrack * + mate_volume_control_track_add_playback (GtkTable *table, + gint tab_pos, + GstMixer *mixer, + GstMixerTrack *track, + GtkWidget *l_sep, + GtkWidget *r_sep, + GtkWidget *fbox); +MateVolumeControlTrack * + mate_volume_control_track_add_recording(GtkTable *table, + gint tab_pos, + GstMixer *mixer, + GstMixerTrack *track, + GtkWidget *l_sep, + GtkWidget *r_sep, + GtkWidget *fbox); + +MateVolumeControlTrack * + mate_volume_control_track_add_switch (GtkTable *table, + gint tab_pos, + GstMixer *mixer, + GstMixerTrack *track, + GtkWidget *l_sep, + GtkWidget *r_sep, + GtkWidget *fbox); + +MateVolumeControlTrack * + mate_volume_control_track_add_option (GtkTable *table, + gint tab_pos, + GstMixer *mixer, + GstMixerTrack *track, + GtkWidget *l_sep, + GtkWidget *r_sep, + GtkWidget *fbox); + +void mate_volume_control_track_free (MateVolumeControlTrack *track); + +void mate_volume_control_track_show (MateVolumeControlTrack *track, + gboolean visible); + +void mate_volume_control_track_update (MateVolumeControlTrack *trkw); + +G_END_DECLS + +#endif /* __GVC_TRACK_H__ */ diff --git a/gst-mixer/src/volume.c b/gst-mixer/src/volume.c new file mode 100644 index 0000000..a3386f1 --- /dev/null +++ b/gst-mixer/src/volume.c @@ -0,0 +1,552 @@ +/* MATE Volume Control + * Copyright (C) 2003-2004 Ronald Bultje <[email protected]> + * + * volume.c: representation of a track's volume channels + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#define _ISOC99_SOURCE + +#include <math.h> +#include <glib/gi18n.h> +#include <gdk/gdkx.h> +#include <gtk/gtk.h> + +#include "volume.h" +#include "button.h" + +G_DEFINE_TYPE (MateVolumeControlVolume, mate_volume_control_volume, GTK_TYPE_FIXED) + + +static void mate_volume_control_volume_class_init (MateVolumeControlVolumeClass *klass); +static void mate_volume_control_volume_init (MateVolumeControlVolume *el); +static void mate_volume_control_volume_dispose (GObject *object); + +static void mate_volume_control_volume_size_req (GtkWidget *widget, + GtkRequisition *req); +static void mate_volume_control_volume_size_alloc (GtkWidget *widget, + GtkAllocation *alloc); +static gboolean mate_volume_control_volume_expose (GtkWidget *widget, + GdkEventExpose *expose); + +static void cb_volume_changed (GtkAdjustment *adj, + gpointer data); +static void cb_lock_toggled (GtkToggleButton *button, + gpointer data); + +static gboolean cb_check (gpointer data); + + +static void +mate_volume_control_volume_class_init (MateVolumeControlVolumeClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *gtkwidget_class = GTK_WIDGET_CLASS (klass); + + gobject_class->dispose = mate_volume_control_volume_dispose; + gtkwidget_class->size_allocate = mate_volume_control_volume_size_alloc; + gtkwidget_class->size_request = mate_volume_control_volume_size_req; + gtkwidget_class->expose_event = mate_volume_control_volume_expose; +} + +static void +mate_volume_control_volume_init (MateVolumeControlVolume *vol) +{ + #if GTK_CHECK_VERSION(2,18,0) + gtk_widget_set_has_window (GTK_WIDGET (vol), TRUE); + #else + gtk_fixed_set_has_window (GTK_FIXED (vol), TRUE); + #endif + + vol->mixer = NULL; + vol->track = NULL; + vol->padding = 6; + vol->scales = NULL; + vol->button = NULL; + vol->locked = FALSE; + vol->id = 0; +} + +static GtkWidget * +get_scale (MateVolumeControlVolume *vol, + gint num_chan, + gint volume) +{ + GtkWidget *slider; + GtkObject *adj; + AtkObject *accessible; + gchar *accessible_name; + + adj = gtk_adjustment_new (volume, + vol->track->min_volume, vol->track->max_volume, + (vol->track->max_volume - vol->track->min_volume) / 100.0, + (vol->track->max_volume - vol->track->min_volume) / 10.0, 0.0); + g_signal_connect (adj, "value_changed", + G_CALLBACK (cb_volume_changed), vol); + slider = gtk_vscale_new (GTK_ADJUSTMENT (adj)); + gtk_scale_set_draw_value (GTK_SCALE (slider), FALSE); + gtk_range_set_inverted (GTK_RANGE (slider), TRUE); + + /* a11y */ + accessible = gtk_widget_get_accessible (slider); + if (GTK_IS_ACCESSIBLE (accessible)) { + if (vol->track->num_channels == 1) { + accessible_name = g_strdup_printf (_("Track %s"), + vol->track->label); + } else { + gchar *accessible_desc = g_strdup_printf (_("Channel %d of track %s"), + num_chan + 1, + vol->track->label); + accessible_name = g_strdup_printf (_("Track %s, channel %d"), + vol->track->label, num_chan + 1); + atk_object_set_description (accessible, accessible_desc); + g_free (accessible_desc); + } + atk_object_set_name (accessible, accessible_name); + g_free (accessible_name); + } + + return slider; +} + +static void +get_button (MateVolumeControlVolume *vol, + gint *volumes) +{ + AtkObject *accessible; + gchar *accessible_name, *msg; + gint n; + + msg = g_strdup_printf (_("Lock channels for %s together"), vol->track->label); + vol->button = mate_volume_control_button_new ("chain.png", + "chain-broken.png", + msg); + g_free (msg); + g_signal_connect (vol->button, "clicked", + G_CALLBACK (cb_lock_toggled), vol); + for (n = 1; n < vol->track->num_channels; n++) { + /* default, unlocked */ + if (volumes[n] != volumes[0]) + break; + } + mate_volume_control_button_set_active (MATE_VOLUME_CONTROL_BUTTON (vol->button), + n == vol->track->num_channels); + + /* a11y */ + accessible = gtk_widget_get_accessible (vol->button); + if (GTK_IS_ACCESSIBLE (accessible)) { + accessible_name = g_strdup_printf (_("Track %s: lock channels together"), + vol->track->label); + atk_object_set_name (accessible, accessible_name); + g_free (accessible_name); + } +} + +GtkWidget * +mate_volume_control_volume_new (GstMixer *mixer, + GstMixerTrack *track, + gint padding) +{ + MateVolumeControlVolume *vol; + gint *volumes, n; + gchar *msg, *chan; + gboolean need_timeout = TRUE; + + need_timeout = ((gst_mixer_get_mixer_flags (GST_MIXER (mixer)) & + GST_MIXER_FLAG_AUTO_NOTIFICATIONS) == 0); + + /* volume */ + vol = g_object_new (MATE_VOLUME_CONTROL_TYPE_VOLUME, NULL); + gst_object_ref (GST_OBJECT (mixer)); + vol->mixer = mixer; + vol->track = g_object_ref (G_OBJECT (track)); + if (padding >= 0) + vol->padding = padding; + + /* sliders */ + volumes = g_new0 (gint, track->num_channels); + gst_mixer_get_volume (mixer, track, volumes); + for (n = 0; n < track->num_channels; n++) { + GtkWidget *slider; + + /* we will reposition the widget once we're drawing up */ + slider = get_scale (vol, n, volumes[n]); + gtk_fixed_put (GTK_FIXED (vol), slider, 0, 0); + gtk_widget_show (slider); + vol->scales = g_list_append (vol->scales, slider); + + /* somewhat dirty hack that will suffice for now. 1 chan + * means mono, two means stereo (left/right) and > 2 means + * alsa, where channel order is front, rear, center, lfe, + * side. */ + if (vol->track->num_channels == 1) { + chan = _("mono"); + } else if (vol->track->num_channels == 2) { + chan = (n == 0) ? _("left") : _("right"); + } else { + switch (n) { + case 0: chan = _("front left"); break; + case 1: chan = _("front right"); break; + case 2: chan = _("rear left"); break; + case 3: chan = _("rear right"); break; + case 4: chan = _("front center"); break; + /* Translators: This is the name of a surround sound channel. It + * stands for "Low-Frequency Effects". If you're not sure that + * this has an established and different translation in your + * language, leave it unchanged. */ + case 5: chan = _("LFE"); break; + case 6: chan = _("side left"); break; + case 7: chan = _("side right"); break; + default: chan = _("unknown"); break; + } + } + + /* Here, we can actually tell people that this + * is a slider that will change channel X. */ + msg = g_strdup_printf (_("Volume of %s channel on %s"), + chan, vol->track->label); + gtk_widget_set_tooltip_text (slider, msg); + g_free (msg); + } + + /* chainbutton */ + get_button (vol, volumes); + if (track->num_channels > 1) { + gtk_fixed_put (GTK_FIXED (vol), vol->button, 0, 0); + gtk_widget_show (vol->button); + } + + g_free (volumes); + + /* GStreamer signals */ + if (need_timeout) + vol->id = g_timeout_add (100, cb_check, vol); + + return GTK_WIDGET (vol); +} + +static void +mate_volume_control_volume_dispose (GObject *object) +{ + MateVolumeControlVolume *vol = MATE_VOLUME_CONTROL_VOLUME (object); + + if (vol->id != 0) { + g_source_remove (vol->id); + vol->id = 0; + } + + if (vol->track) { + g_object_unref (G_OBJECT (vol->track)); + vol->track = NULL; + } + + if (vol->mixer) { + gst_object_unref (GST_OBJECT (vol->mixer)); + vol->mixer = NULL; + } + + if (vol->scales) { + g_list_free (vol->scales); + vol->scales = NULL; + } + + G_OBJECT_CLASS (mate_volume_control_volume_parent_class)->dispose (object); +} + +/* + * Gtk/GDK virtual functions for size negotiation. + */ + +static void +mate_volume_control_volume_size_req (GtkWidget *widget, + GtkRequisition *req) +{ + MateVolumeControlVolume *vol = MATE_VOLUME_CONTROL_VOLUME (widget); + GtkRequisition but_req, scale_req; + + /* request size of kids */ + GTK_WIDGET_GET_CLASS (vol->button)->size_request (vol->button, &but_req); + GTK_WIDGET_GET_CLASS (vol->scales->data)->size_request (vol->scales->data, + &scale_req); + if (scale_req.height < 100) + scale_req.height = 100; + + /* calculate our own size from that */ + req->width = scale_req.width * vol->track->num_channels + + vol->padding * (vol->track->num_channels - 1); + req->height = scale_req.height + but_req.height /*+ vol->padding*/; +} + +static void +mate_volume_control_volume_size_alloc (GtkWidget *widget, + GtkAllocation *alloc) +{ + MateVolumeControlVolume *vol = MATE_VOLUME_CONTROL_VOLUME (widget); + GtkRequisition but_req, scale_req; + GtkAllocation but_all, scale_all; + gint x_offset, but_deco_width, n = 0; + GList *scales; + GtkAllocation allocation; + + /* loop? */ + gtk_widget_get_allocation (widget, &allocation); + if (alloc->x == allocation.x && + alloc->y == allocation.y && + alloc->width == allocation.width && + alloc->height == allocation.height) + return; + + /* request size of kids */ + GTK_WIDGET_GET_CLASS (vol->button)->size_request (vol->button, &but_req); + GTK_WIDGET_GET_CLASS (vol->scales->data)->size_request (vol->scales->data, + &scale_req); + + /* calculate */ + x_offset = (alloc->width - ((vol->track->num_channels * scale_req.width) + + (vol->track->num_channels - 1) * vol->padding)) / 2; + scale_all.width = scale_req.width; + scale_all.height = alloc->height - but_req.height; + scale_all.y = 0; + but_deco_width = alloc->width - (2 * x_offset); + but_all.width = but_req.width; + but_all.height = but_req.height; + but_all.x = x_offset + (but_deco_width - but_req.width) / 2; + but_all.y = alloc->height - but_req.height; + + /* tell sliders */ + for (scales = vol->scales; scales != NULL; scales = scales->next, n++) { + scale_all.x = x_offset + n * (scale_req.width + vol->padding); + gtk_fixed_move (GTK_FIXED (vol), scales->data, scale_all.x, scale_all.y); + gtk_widget_set_size_request (scales->data, scale_all.width, scale_all.height); + } + + /* tell button */ + if (vol->track->num_channels > 1) { + gtk_fixed_move (GTK_FIXED (vol), vol->button, but_all.x, but_all.y); + gtk_widget_set_size_request (vol->button, but_all.width, but_all.height); + } + + /* parent will resize window */ + GTK_WIDGET_CLASS (mate_volume_control_volume_parent_class)->size_allocate (widget, alloc); +} + +static gboolean +mate_volume_control_volume_expose (GtkWidget *widget, + GdkEventExpose *expose) +{ + GtkAllocation allocation; + MateVolumeControlVolume *vol = MATE_VOLUME_CONTROL_VOLUME (widget); + + /* clear background */ + gtk_widget_get_allocation (widget, &allocation); + gdk_window_clear_area (gtk_widget_get_window (widget), 0, 0, + allocation.width, + allocation.height); + + if (vol->track->num_channels > 1) { + gint x_offset, y_offset, height, width; + GtkRequisition scale_req, but_req; + GdkPoint points[3]; + GtkStyle *style; + GtkStateType state; + + /* request size of kids */ + GTK_WIDGET_GET_CLASS (vol->button)->size_request (vol->button, &but_req); + GTK_WIDGET_GET_CLASS (vol->scales->data)->size_request (vol->scales->data, + &scale_req); + + /* calculate */ + gtk_widget_get_allocation (widget, &allocation); + x_offset = (allocation.width - + ((vol->track->num_channels * scale_req.width) + + (vol->track->num_channels - 1) * vol->padding)) / 2; + y_offset = allocation.height - but_req.height; + width = allocation.width - (2 * x_offset + but_req.width); + height = but_req.height / 2; + points[0].y = y_offset + 3; + points[1].y = points[2].y = points[0].y + height - 3; + + /* draw chainbutton decorations */ + style = gtk_widget_get_style (widget); + state = gtk_widget_get_state (widget); + + points[0].x = points[1].x = x_offset + 3; + points[2].x = points[0].x + width - 6; + gtk_paint_polygon (style, gtk_widget_get_window (widget), + state, + GTK_SHADOW_ETCHED_IN, + &expose->area, widget, "hseparator", + points, 3, FALSE); + + points[0].x = points[1].x = allocation.width - x_offset - 3; + points[2].x = points[0].x - width + 6; + gtk_paint_polygon (style, gtk_widget_get_window (widget), + state, + GTK_SHADOW_ETCHED_IN, + &expose->area, widget, "hseparator", + points, 3, FALSE); + } + + /* take care of redrawing the kids */ + return GTK_WIDGET_CLASS (mate_volume_control_volume_parent_class)->expose_event (widget, expose); +} + +/* + * Signals handlers. + */ + +static void +cb_volume_changed (GtkAdjustment *_adj, + gpointer data) +{ + MateVolumeControlVolume *vol = data; + gint *volumes, i = 0; + GList *scales; + + if (vol->locked) + return; + vol->locked = TRUE; + volumes = g_new (gint, vol->track->num_channels); + + for (scales = vol->scales; scales != NULL; scales = scales->next) { + GtkAdjustment *adj = gtk_range_get_adjustment (scales->data); + + if (mate_volume_control_button_get_active ( + MATE_VOLUME_CONTROL_BUTTON (vol->button))) { + gtk_adjustment_set_value (adj, gtk_adjustment_get_value (_adj)); + volumes[i++] = rint (gtk_adjustment_get_value (_adj)); + } else { + volumes[i++] = rint (gtk_adjustment_get_value (adj)); + } + } + + gst_mixer_set_volume (vol->mixer, vol->track, volumes); + + g_free (volumes); + vol->locked = FALSE; +} + +static void +cb_lock_toggled (GtkToggleButton *button, + gpointer data) +{ + MateVolumeControlVolume *vol = data; + + if (mate_volume_control_button_get_active ( + MATE_VOLUME_CONTROL_BUTTON (vol->button))) { + /* get the mean value, and set it on the first adjustment. + * the cb_volume_changed () callback will take care of the + * rest. */ + gint volume = 0, num = 0; + GList *scales; + + for (scales = vol->scales ; scales != NULL; scales = scales->next) { + GtkAdjustment *adj = gtk_range_get_adjustment (scales->data); + + num++; + volume += gtk_adjustment_get_value (adj); + } + + /* safety check */ + if (vol->scales != NULL) { + gtk_adjustment_set_value (gtk_range_get_adjustment (vol->scales->data), + volume / num); + } + } +} + +/* + * See if our volume is zero. + */ + +void +mate_volume_control_volume_ask (MateVolumeControlVolume * vol, + gboolean *real_zero, gboolean *slider_zero) +{ + GList *scales; + gint *volumes, n, tot = 0; + + volumes = g_new (gint, vol->track->num_channels); + gst_mixer_get_volume (vol->mixer, vol->track, volumes); + for (n = 0; n < vol->track->num_channels; n++) + tot += volumes[n]; + g_free (volumes); + *real_zero = (tot == 0); + + *slider_zero = TRUE; + for (n = 0, scales = vol->scales; + scales != NULL; scales = scales->next, n++) { + GtkAdjustment *adj = gtk_range_get_adjustment (scales->data); + + if (rint (gtk_adjustment_get_value (adj)) != 0) { + *slider_zero = FALSE; + break; + } + } +} + + +void +mate_volume_control_volume_update (MateVolumeControlVolume *vol) +{ + gint *volumes, n; + GList *scales; + + /* don't do callbacks */ + if (vol->locked) + return; + + vol->locked = TRUE; + + volumes = g_new (gint, vol->track->num_channels); + gst_mixer_get_volume (vol->mixer, vol->track, volumes); + + /* did we change? */ + for (n = 0, scales = vol->scales; + scales != NULL; scales = scales->next, n++) { + GtkAdjustment *adj = gtk_range_get_adjustment (scales->data); + + if ((gint) gtk_adjustment_get_value (adj) != volumes[n]) { + gtk_range_set_value (scales->data, volumes[n]); + } + + /* should we release lock? */ + if (volumes[n] != volumes[0]) { + mate_volume_control_button_set_active ( + MATE_VOLUME_CONTROL_BUTTON (vol->button), FALSE); + } + } + + g_free (volumes); + vol->locked = FALSE; +} + +/* + * Timeout to check for volume changes. + */ + +static gboolean +cb_check (gpointer data) +{ + mate_volume_control_volume_update (data); + + return TRUE; +} diff --git a/gst-mixer/src/volume.h b/gst-mixer/src/volume.h new file mode 100644 index 0000000..678aab4 --- /dev/null +++ b/gst-mixer/src/volume.h @@ -0,0 +1,82 @@ +/* MATE Volume Control + * Copyright (C) 2003-2004 Ronald Bultje <[email protected]> + * + * volume.h: representation of a track's volume channels + * + * 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. + */ + +#ifndef __GVC_VOLUME_H__ +#define __GVC_VOLUME_H__ + +#include <glib.h> +#include <gtk/gtk.h> +#include <gst/interfaces/mixer.h> + +G_BEGIN_DECLS + +#define MATE_VOLUME_CONTROL_TYPE_VOLUME \ + (mate_volume_control_volume_get_type ()) +#define MATE_VOLUME_CONTROL_VOLUME(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), MATE_VOLUME_CONTROL_TYPE_VOLUME, \ + MateVolumeControlVolume)) +#define MATE_VOLUME_CONTROL_VOLUME_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), MATE_VOLUME_CONTROL_TYPE_VOLUME, \ + MateVolumeControlVolumeClass)) +#define MATE_VOLUME_CONTROL_IS_VOLUME(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MATE_VOLUME_CONTROL_TYPE_VOLUME)) +#define MATE_VOLUME_CONTROL_IS_VOLUME_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), MATE_VOLUME_CONTROL_TYPE_VOLUME)) + +typedef struct _MateVolumeControlVolume { + GtkFixed parent; + + /* track + mixer */ + GstMixer *mixer; + GstMixerTrack *track; + + /* padding */ + gint padding; + + /* childs */ + GList *scales; + GtkWidget *button, *image; + + /* this will be set to true if the user changes volumes + * in the mixer as a response to a user query. It prevents + * infinite loops. */ + gboolean locked; + + /* signal ID */ + guint id; +} MateVolumeControlVolume; + +typedef struct _MateVolumeControlVolumeClass { + GtkFixedClass klass; +} MateVolumeControlVolumeClass; + +GType mate_volume_control_volume_get_type (void); +GtkWidget * mate_volume_control_volume_new (GstMixer *mixer, + GstMixerTrack *track, + gint padding); +void mate_volume_control_volume_ask (MateVolumeControlVolume *volume, + gboolean * real_zero, + gboolean * slider_zero); +void mate_volume_control_volume_update (MateVolumeControlVolume *volume); + +G_END_DECLS + +#endif /* __GVC_VOLUME_H__ */ diff --git a/gst-mixer/src/window.c b/gst-mixer/src/window.c new file mode 100644 index 0000000..19b9eed --- /dev/null +++ b/gst-mixer/src/window.c @@ -0,0 +1,435 @@ +/* MATE Volume Control + * Copyright (C) 2003-2004 Ronald Bultje <[email protected]> + * + * window.c: main window + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <glib/gi18n.h> +#include <gtk/gtk.h> +#include <mateconf/mateconf-client.h> +#include <gdk/gdkkeysyms.h> + +#include "keys.h" +#include "preferences.h" +#include "window.h" + +G_DEFINE_TYPE (MateVolumeControlWindow, mate_volume_control_window, GTK_TYPE_WINDOW) + +void mate_volume_control_window_set_page(GtkWidget *widget, const gchar *page) +{ + MateVolumeControlWindow *win = MATE_VOLUME_CONTROL_WINDOW (widget); + + if (g_ascii_strncasecmp(page, "playback",8) == 0) + gtk_notebook_set_current_page (GTK_NOTEBOOK (win->el), 0); + else if (g_ascii_strncasecmp(page, "recording",9) == 0) + gtk_notebook_set_current_page (GTK_NOTEBOOK (win->el), 1); + else if (g_ascii_strncasecmp(page, "switches",9) == 0) + gtk_notebook_set_current_page (GTK_NOTEBOOK (win->el), 2); + else if (g_ascii_strncasecmp(page, "options",9) == 0) + gtk_notebook_set_current_page (GTK_NOTEBOOK (win->el), 3); + else /* default is "playback" */ + gtk_notebook_set_current_page (GTK_NOTEBOOK (win->el), 0); +} + + +/* + * Menu actions. + */ + +static void +cb_change (GtkComboBox *widget, + MateVolumeControlWindow *win) +{ + gchar *device_name; + + device_name = gtk_combo_box_get_active_text (widget); + g_return_if_fail (device_name != NULL); + + mateconf_client_set_string (win->client, MATE_VOLUME_CONTROL_KEY_ACTIVE_ELEMENT, device_name, NULL); + + g_free (device_name); +} + +static void +cb_exit (GtkAction *action, + MateVolumeControlWindow *win) +{ + gtk_widget_destroy (GTK_WIDGET (win)); +} + +static void +cb_preferences_destroy (GtkWidget *widget, + MateVolumeControlWindow *win) +{ + win->prefs = NULL; +} + +static void +cb_preferences (GtkAction *action, + MateVolumeControlWindow *win) +{ + + if (!win->prefs) { + win->prefs = mate_volume_control_preferences_new (GST_ELEMENT (win->el->mixer), + win->client); + g_signal_connect (win->prefs, "destroy", G_CALLBACK (cb_preferences_destroy), win); + gtk_widget_show (win->prefs); + } else { + gtk_window_present (GTK_WINDOW (win->prefs)); + } +} + +static void +cb_help (GtkAction *action, + MateVolumeControlWindow *win) +{ + GdkScreen *screen; + GtkWidget *dialog; + GError *error = NULL; + + screen = gtk_window_get_screen (GTK_WINDOW (win)); + + if (gtk_show_uri (screen, "ghelp:mate-volume-control", GDK_CURRENT_TIME, + &error) == FALSE) { + dialog = gtk_message_dialog_new (GTK_WINDOW (win), GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, "%s", error->message); + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); + g_error_free (error); + } +} + +static void +cb_show_about (MateVolumeControlWindow *win) +{ + const gchar *authors[] = { "Ronald Bultje <[email protected]>", + "Leif Johnson <[email protected]>", + NULL }; + const gchar *documenters[] = { "Sun Microsystems", + NULL}; + + gtk_show_about_dialog (GTK_WINDOW (win), + "version", VERSION, + "copyright", "Copyright \xc2\xa9 2003-2004 Ronald Bultje", + "comments", _("A MATE/GStreamer-based volume control application"), + "authors", authors, + "documenters", documenters, + "translator-credits", _("translator-credits"), + "logo-icon-name", "multimedia-volume-control", + NULL); +} + +static void +window_change_mixer_element (MateVolumeControlWindow *win, + const gchar *el) +{ + const char *cur_el_str; + GList *item; + + g_return_if_fail (win != NULL); + g_return_if_fail (el != NULL); + + for (item = win->elements; item != NULL; item = item->next) { + cur_el_str = g_object_get_data (item->data, "mate-volume-control-name"); + + if (cur_el_str == NULL) + continue; + + if (g_str_equal (cur_el_str, el)) { + GstElement *old_element = GST_ELEMENT (win->el->mixer); + gchar *title; + + /* change element */ + gst_element_set_state (item->data, GST_STATE_READY); + mate_volume_control_element_change (win->el, item->data); + + if (win->prefs != NULL) + mate_volume_control_preferences_change (MATE_VOLUME_CONTROL_PREFERENCES (win->prefs), + item->data); + + if (old_element != NULL) + gst_element_set_state (old_element, GST_STATE_NULL); + + /* change window title */ + title = g_strdup_printf (_("Volume Control: %s"), cur_el_str); + gtk_window_set_title (GTK_WINDOW (win), title); + g_free (title); + + break; + } + } +} + +static void +cb_mateconf (MateConfClient *client, + guint connection_id, + MateConfEntry *entry, + gpointer data) +{ + g_return_if_fail (mateconf_entry_get_key (entry) != NULL); + + if (g_str_equal (mateconf_entry_get_key (entry), + MATE_VOLUME_CONTROL_KEY_ACTIVE_ELEMENT)) { + window_change_mixer_element (MATE_VOLUME_CONTROL_WINDOW (data), + mateconf_value_get_string (mateconf_entry_get_value (entry))); + } +} + +/* + * Signal handlers. + */ + +#if 0 +static void +cb_error (GstElement *element, + GstElement *source, + GError *error, + gchar *debug, + gpointer data) +{ + MateVolumeControlWindow *win = MATE_VOLUME_CONTROL_WINDOW (data); + GtkWidget *dialog; + + dialog = gtk_message_dialog_new (GTK_WINDOW (win), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, + error->message); + gtk_widget_show (dialog); + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); +} +#endif + +static void +mate_volume_control_window_dispose (GObject *object) +{ + MateVolumeControlWindow *win = MATE_VOLUME_CONTROL_WINDOW (object); + + if (win->prefs) { + gtk_widget_destroy (win->prefs); + win->prefs = NULL; + } + + /* clean up */ + if (win->elements) { + + g_list_foreach (win->elements, (GFunc) g_object_unref, NULL); + g_list_free (win->elements); + win->elements = NULL; + } + + if (win->client) { + g_object_unref (win->client); + win->client = NULL; + } + + G_OBJECT_CLASS (mate_volume_control_window_parent_class)->dispose (object); +} + + +static void +mate_volume_control_window_class_init (MateVolumeControlWindowClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->dispose = mate_volume_control_window_dispose; +} + + +static void +mate_volume_control_window_init (MateVolumeControlWindow *win) +{ + int width, height; + + win->elements = NULL; + win->el = NULL; + win->client = mateconf_client_get_default (); + win->prefs = NULL; + win->use_default_mixer = FALSE; + + g_set_application_name (_("Volume Control")); + gtk_window_set_title (GTK_WINDOW (win), _("Volume Control")); + + /* To set the window according to previous geometry */ + width = mateconf_client_get_int (win->client, PREF_UI_WINDOW_WIDTH, NULL); + if (width < 250) + width = 250; + height = mateconf_client_get_int (win->client, PREF_UI_WINDOW_HEIGHT, NULL); + if (height < 100) + height = -1; + gtk_window_set_default_size (GTK_WINDOW (win), width, height); +} + +GtkWidget * +mate_volume_control_window_new (GList *elements) +{ + gchar *active_el_str, *cur_el_str; + GstElement *active_element; + GList *item; + MateVolumeControlWindow *win; + GtkAccelGroup *accel_group; + GtkWidget *combo_box; + GtkWidget *label; + GtkWidget *hbox; + GtkWidget *buttons; + GtkWidget *el; + GtkWidget *prefsbtn; + GtkWidget *closebtn; + GtkWidget *helpbtn; + gint count = 0; + GtkWidget *vbox; + GtkCellRenderer *renderer; + gint active_element_num; + + g_return_val_if_fail (elements != NULL, NULL); + active_element = NULL; + + /* window */ + win = g_object_new (MATE_VOLUME_CONTROL_TYPE_WINDOW, NULL); + win->elements = elements; + + accel_group = gtk_accel_group_new (); + + gtk_window_add_accel_group (GTK_WINDOW (win), accel_group); + gtk_accel_group_connect (accel_group, GDK_A, GDK_CONTROL_MASK, 0, + g_cclosure_new_swap (G_CALLBACK (cb_show_about), win, NULL)); + + /* get active element, if any (otherwise we use the default) */ + active_el_str = mateconf_client_get_string (win->client, + MATE_VOLUME_CONTROL_KEY_ACTIVE_ELEMENT, + NULL); + if (active_el_str != NULL && *active_el_str != '\0') { + for (count = 0, item = elements; item != NULL; item = item->next, count++) { + cur_el_str = g_object_get_data (item->data, "mate-volume-control-name"); + if (cur_el_str == NULL) + continue; + + if (g_str_equal (active_el_str, cur_el_str)) { + active_element = item->data; + break; + } + } + g_free (active_el_str); + if (!item) { + count = 0; + active_element = elements->data; + /* If there's a default but it doesn't match what we have available, + * reset the default */ + mateconf_client_set_string (win->client, + MATE_VOLUME_CONTROL_KEY_ACTIVE_ELEMENT, + g_object_get_data (G_OBJECT (active_element), + "mate-volume-control-name"), + NULL); + } + /* default element to first */ + if (!active_element) + active_element = elements->data; + } else { + count = 0; + active_element = elements->data; + } + active_element_num = count; + + combo_box = gtk_combo_box_new_text (); + renderer = gtk_cell_renderer_text_new (); + g_object_set (renderer, "ellipsize", PANGO_ELLIPSIZE_END, NULL); + gtk_cell_layout_clear (GTK_CELL_LAYOUT (combo_box)); + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box), renderer, TRUE); + gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combo_box), renderer, "text", 0); + for (count = 0, item = elements; item != NULL; item = item->next, count++) { + const gchar *name; + + name = g_object_get_data (item->data, "mate-volume-control-name"); + gtk_combo_box_append_text(GTK_COMBO_BOX (combo_box), name); + } + gtk_combo_box_set_active (GTK_COMBO_BOX (combo_box), active_element_num); + g_signal_connect (combo_box, "changed", G_CALLBACK (cb_change), win); + + + /* mateconf */ + mateconf_client_add_dir (win->client, MATE_VOLUME_CONTROL_KEY_DIR, + MATECONF_CLIENT_PRELOAD_RECURSIVE, NULL); + mateconf_client_notify_add (win->client, MATE_VOLUME_CONTROL_KEY_DIR, + cb_mateconf, win, NULL, NULL); + + win->use_default_mixer = (active_el_str == NULL); + + /* add the combo box to choose the device */ + label = gtk_label_new (NULL); + gtk_label_set_text_with_mnemonic (GTK_LABEL (label), _("_Device: ")); + gtk_label_set_mnemonic_widget (GTK_LABEL (label), combo_box); + + hbox = gtk_hbox_new (FALSE, 6); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (hbox), combo_box, TRUE, TRUE, 0); + + /* add content for this element */ + el = mate_volume_control_element_new (win->client); + win->el = MATE_VOLUME_CONTROL_ELEMENT (el); + + /* create the buttons box */ + helpbtn = gtk_button_new_from_stock (GTK_STOCK_HELP); + prefsbtn = gtk_button_new_from_stock (GTK_STOCK_PREFERENCES); + closebtn = gtk_button_new_from_stock (GTK_STOCK_CLOSE); + g_signal_connect (helpbtn, "clicked", G_CALLBACK (cb_help), win); + g_signal_connect (prefsbtn, "clicked", G_CALLBACK (cb_preferences), win); + g_signal_connect (closebtn, "clicked", G_CALLBACK (cb_exit), win); + gtk_widget_add_accelerator (closebtn, "clicked", accel_group, + GDK_Escape, 0, 0); + gtk_widget_add_accelerator (helpbtn, "clicked", accel_group, + GDK_F1, 0, 0); + buttons = gtk_hbutton_box_new (); + gtk_box_pack_start (GTK_BOX (buttons), helpbtn, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (buttons), prefsbtn, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (buttons), closebtn, FALSE, FALSE, 0); + gtk_box_set_spacing (GTK_BOX (buttons), 6); + gtk_button_box_set_layout (GTK_BUTTON_BOX (buttons), GTK_BUTTONBOX_END); + gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (buttons), helpbtn, TRUE); + + /* Put the the elements in a vbox */ + vbox = gtk_vbox_new (FALSE, 0); + gtk_container_add (GTK_CONTAINER(win), vbox); + gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 6); + gtk_box_pack_start (GTK_BOX (vbox), el, TRUE, TRUE, 6); + gtk_box_pack_start (GTK_BOX (vbox), buttons, FALSE, FALSE, 0); + gtk_container_set_border_width (GTK_CONTAINER (vbox), 6); + + /* set tooltips */ + gtk_widget_set_tooltip_text (combo_box, _("Control volume on a different device")); + + gtk_widget_show_all (GTK_WIDGET (win)); + + /* refresh the control and window title with the default mixer */ + window_change_mixer_element (win, g_object_get_data (G_OBJECT (active_element), + "mate-volume-control-name")); + + /* FIXME: + * - set error handler (cb_error) after device activation: + * g_signal_connect (element, "error", G_CALLBACK (cb_error), win);. + * - on device change: reset error handler, change menu (in case this + * was done outside the UI). + */ + + return GTK_WIDGET (win); +} + + diff --git a/gst-mixer/src/window.h b/gst-mixer/src/window.h new file mode 100644 index 0000000..e108d75 --- /dev/null +++ b/gst-mixer/src/window.h @@ -0,0 +1,75 @@ +/* MATE Volume Control + * Copyright (C) 2003-2004 Ronald Bultje <[email protected]> + * + * window.h: main window + * + * 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. + */ + +#ifndef __GVC_WINDOW_H__ +#define __GVC_WINDOW_H__ + +#include <glib.h> +#include <mateconf/mateconf-client.h> +#include <gst/gst.h> + +#include "element.h" + +G_BEGIN_DECLS + +#define MATE_VOLUME_CONTROL_TYPE_WINDOW \ + (mate_volume_control_window_get_type ()) +#define MATE_VOLUME_CONTROL_WINDOW(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), MATE_VOLUME_CONTROL_TYPE_WINDOW, \ + MateVolumeControlWindow)) +#define MATE_VOLUME_CONTROL_WINDOW_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), MATE_VOLUME_CONTROL_TYPE_WINDOW, \ + MateVolumeControlWindowClass)) +#define MATE_VOLUME_CONTROL_IS_WINDOW(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MATE_VOLUME_CONTROL_TYPE_WINDOW)) +#define MATE_VOLUME_CONTROL_IS_WINDOW_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), MATE_VOLUME_CONTROL_TYPE_WINDOW)) + +typedef struct { + GtkWindow parent; + + /* element list */ + GList *elements; + + /* mateconf client */ + MateConfClient *client; + + /* contents */ + MateVolumeControlElement *el; + + /* preferences window, if opened */ + GtkWidget *prefs; + + /* use default mixer */ + gboolean use_default_mixer; +} MateVolumeControlWindow; + +typedef struct { + GtkWindowClass klass; +} MateVolumeControlWindowClass; + +GType mate_volume_control_window_get_type (void); +GtkWidget * mate_volume_control_window_new (GList *elements); +void mate_volume_control_window_set_page (GtkWidget *win, const gchar *page); + +G_END_DECLS + +#endif /* __GVC_WINDOW_H__ */ |